Mesh Oriented datABase  (version 5.6.0)
An array-based unstructured mesh library
GeomQueryTool.cpp
Go to the documentation of this file.
1 #include "moab/GeomQueryTool.hpp"
2 
3 #ifdef WIN32 /* windows */
4 #define _USE_MATH_DEFINES // For M_PI
5 #endif
6 #include <string>
7 #include <iostream>
8 #include <fstream>
9 #include <sstream>
10 #include <limits>
11 #include <algorithm>
12 #include <set>
13 
14 #include <cctype>
15 #include <cstring>
16 #include <cstdlib>
17 #include <cstdio>
18 
20 
21 #ifdef __DEBUG
22 #define MB_GQT_DEBUG
23 #endif
24 
25 namespace moab
26 {
27 
28 /** \class FindVolume_IntRegCtxt
29  *
30  *
31  *\brief An intersection context used for finding a volume
32  *
33  * This context is used to find the nearest intersection location
34  * and is intended for use with a global surface tree from
35  * the GeomTopoTool.
36  *
37  * The behavior of this context is relatively simple in that it
38  * returns only one intersection distance, surface, and facet.
39  * Intersections of any orientation are accepted. The positive
40  * value of the search window is limited to the current nearest
41  * intersection distance.
42  *
43  */
44 
46 {
47 
48  public:
49  // Constructor
51  {
52  // initialize return vectors
53  // only one hit is returned in this context
54  intersections.push_back( std::numeric_limits< double >::max() );
55  sets.push_back( 0 );
56  facets.push_back( 0 );
57  }
58 
60  EntityHandle tri,
61  double dist,
64  {
65  // update dist, set, and triangle hit if
66  // we found a new minimum distance
67  double abs_dist = fabs( dist );
68  if( abs_dist < fabs( intersections[0] ) )
69  {
70  intersections[0] = dist;
71  sets[0] = set;
72  facets[0] = tri;
73 
74  // narrow search window based on the hit distance
75  pos = abs_dist;
76  neg = -abs_dist;
77  search_win.first = &pos;
78  search_win.second = &neg;
79  }
80 
81  return MB_SUCCESS;
82  }
83 
84  // storage for updated window values during search
85  double pos;
86  double neg;
87 };
88 
89 /** \class GQT_IntRegCtxt
90  *
91  *\brief An implementation of an Intersection Registration Context for use GQT ray-firing
92  *
93  * This context uses a variety of tests and conditions to confirm whether or
94  * not to accumulate an intersection, to ensure robustness for ray firing.
95  *
96  * This context only accumulates intersections that are oriented parallel to
97  * the 'desiredOrient', if provided, with respect to 'geomVol', using
98  * information in the in 'senseTag'.
99  *
100  * This context only accumulates a single intersection out of a set of
101  * multiple intersections that fall in the same 'neighborhood', where a
102  * 'neighborhood' is defined as facets that share edges or vertices.
103  *
104  * This context only accumulates piercing intersections. This is relevant
105  * for intersections that are found to be on an edge or vertex by the
106  * Plucker test. Such intersections are piercing if the ray has the same
107  * orientation w.r.t. to all fecets that share that edge or vertex.
108  *
109  * This context tests intersections against a list of 'prevFacets' to
110  * prevent a ray from crossing the same facet more than once. The user is
111  * responsible for ensuring that this list is reset when appropriate.
112  *
113  * This context accumulates all intersections within 'tol' of the
114  * start of the ray and if the number of intersections within the
115  * 'tol' of the ray start point is less than 'minTolInt', the next
116  * closest intersection. If the desired result is only the closest
117  * intersection, 'minTolInt' should be 0. This function will return all
118  * intersections, regardless of distance from the start of the ray, if
119  * 'minTolInt' is negative.
120  *
121  */
122 
124 {
125 
126  private:
127  // Input
131  const double tol; /* used for box.intersect_ray, radius of
132  neighborhood for adjacent triangles,
133  and old mode of add_intersection */
134  const int minTolInt;
135 
136  // Optional Input - to screen RTIs by orientation and edge/node intersection
137  const EntityHandle* rootSet; /* used for sphere_intersect */
138  const EntityHandle* geomVol; /* used for determining surface sense */
139  const Tag* senseTag; /* allows screening by triangle orientation.
140  both geomVol and senseTag must be used together. */
141  const int* desiredOrient; /* points to desired orientation of ray with
142  respect to surf normal, if this feature is used.
143  Must point to -1 (reverse) or 1 (forward).
144  geomVol and senseTag are needed for this feature */
145 
146  // Optional Input - to avoid returning these as RTIs
147  const std::vector< EntityHandle >* prevFacets; /* intersections on these triangles
148  will not be returned */
149 
150  // Other Variables
151  std::vector< std::vector< EntityHandle > > neighborhoods;
152  std::vector< EntityHandle > neighborhood;
153 
154  void add_intersection( EntityHandle set,
155  EntityHandle tri,
156  double dist,
158  void append_intersection( EntityHandle set, EntityHandle facet, double dist );
159  void set_intersection( int len_idx, EntityHandle set, EntityHandle facet, double dist );
161  EntityHandle facet,
162  double dist,
165  const CartVect& ray_direction,
166  const GeomUtil::intersection_type int_type,
167  const std::vector< EntityHandle >& close_tris,
168  const std::vector< int >& close_senses,
169  const Interface* MBI,
170  std::vector< EntityHandle >* neighborhood_tris = 0 );
171 
172  bool in_prevFacets( const EntityHandle tri );
173  bool in_neighborhoods( const EntityHandle tri );
174 
175  public:
177  const double ray_point[3],
178  const double ray_dir[3],
179  double tolerance,
180  int min_tolerance_intersections,
181  const EntityHandle* root_set,
182  const EntityHandle* geom_volume,
183  const Tag* sense_tag,
184  const int* desired_orient,
185  const std::vector< EntityHandle >* prev_facets )
186  : tool( obbtool ), ray_origin( ray_point ), ray_direction( ray_dir ), tol( tolerance ),
187  minTolInt( min_tolerance_intersections ), rootSet( root_set ), geomVol( geom_volume ), senseTag( sense_tag ),
188  desiredOrient( desired_orient ), prevFacets( prev_facets ) {
189 
190  };
191 
193  EntityHandle triangle,
194  double distance,
196  GeomUtil::intersection_type int_type );
197 
198  virtual ErrorCode update_orient( EntityHandle set, int* surfTriOrient );
199  virtual const int* getDesiredOrient()
200  {
201  return desiredOrient;
202  };
203 };
204 
206 {
207  // Get desired orientation of surface wrt volume. Use this to return only
208  // exit or entrance intersections.
209  if( geomVol && senseTag && desiredOrient && surfTriOrient )
210  {
211  if( 1 != *desiredOrient && -1 != *desiredOrient )
212  {
213  std::cerr << "error: desired orientation must be 1 (forward) or -1 (reverse)" << std::endl;
214  }
215  EntityHandle vols[2];
217  "failed to get sense tag data" );
218  if( vols[0] == vols[1] )
219  {
220  std::cerr << "error: surface has positive and negative sense wrt same volume" << std::endl;
221  return MB_FAILURE;
222  }
223  // surfTriOrient will be used by plucker_ray_tri_intersect to avoid
224  // intersections with wrong orientation.
225  if( *geomVol == vols[0] )
226  {
227  *surfTriOrient = *desiredOrient * 1;
228  }
229  else if( *geomVol == vols[1] )
230  {
231  *surfTriOrient = *desiredOrient * ( -1 );
232  }
233  else
234  {
235  assert( false );
236  return MB_FAILURE;
237  }
238  }
239 
240  return MB_SUCCESS;
241 }
242 
244 {
245  return ( prevFacets && ( ( *prevFacets ).end() != find( ( *prevFacets ).begin(), ( *prevFacets ).end(), tri ) ) );
246 }
247 
249 {
250  bool same_neighborhood = false;
251  for( unsigned i = 0; i < neighborhoods.size(); ++i )
252  {
253  if( neighborhoods[i].end() != find( neighborhoods[i].begin(), neighborhoods[i].end(), tri ) )
254  {
255  same_neighborhood = true;
256  continue;
257  }
258  }
259  return same_neighborhood;
260 }
261 
262 /**\brief Determine if a ray-edge/node intersection is glancing or piercing.
263  * This function avoids asking for upward adjacencies to prevent their
264  * creation.
265  *\param tri The intersected triangle
266  *\param ray_dir The direction of the ray
267  *\param int_type The type of intersection (EDGE0, EDGE1, NODE2, ...)
268  *\param close_tris Vector of triangles in the proximity of the intersection
269  *\param close_senses Vector of surface senses for tris in the proximity of
270  * the intersection
271  *\param neighborhood Vector of triangles in the topological neighborhood of the intersection
272  *\return True if piercing, false otherwise.
273  */
275  const CartVect& ray_dir,
276  const GeomUtil::intersection_type int_type,
277  const std::vector< EntityHandle >& close_tris,
278  const std::vector< int >& close_senses,
279  const Interface* MBI,
280  std::vector< EntityHandle >* neighborhood_tris )
281 {
282 
283  // get the node of the triangle
284  const EntityHandle* conn = NULL;
285  int len = 0;
286  MB_CHK_ERR_RET_VAL( MBI->get_connectivity( tri, conn, len ), false );
287  // NOTE: original code is next line, but return type was wrong; returning true to get same net effect
288  if( 3 != len ) return false;
289 
290  // get adjacent tris (and keep their corresponding senses)
291  std::vector< EntityHandle > adj_tris;
292  std::vector< int > adj_senses;
293 
294  // node intersection
295  if( GeomUtil::NODE0 == int_type || GeomUtil::NODE1 == int_type || GeomUtil::NODE2 == int_type )
296  {
297 
298  // get the intersected node
299  EntityHandle node;
300  if( GeomUtil::NODE0 == int_type )
301  node = conn[0];
302  else if( GeomUtil::NODE1 == int_type )
303  node = conn[1];
304  else
305  node = conn[2];
306 
307  // get tris adjacent to node
308  for( unsigned i = 0; i < close_tris.size(); ++i )
309  {
310  const EntityHandle* con = NULL;
311  MB_CHK_ERR_RET_VAL( MBI->get_connectivity( close_tris[i], con, len ), false );
312  if( 3 != len ) return false;
313 
314  if( node == con[0] || node == con[1] || node == con[2] )
315  {
316  adj_tris.push_back( close_tris[i] );
317  adj_senses.push_back( close_senses[i] );
318  }
319  }
320  if( adj_tris.empty() )
321  {
322  std::cerr << "error: no tris are adjacent to the node" << std::endl;
323  return false;
324  }
325  // edge intersection
326  }
327  else if( GeomUtil::EDGE0 == int_type || GeomUtil::EDGE1 == int_type || GeomUtil::EDGE2 == int_type )
328  {
329 
330  // get the endpoints of the edge
331  EntityHandle endpts[2];
332  if( GeomUtil::EDGE0 == int_type )
333  {
334  endpts[0] = conn[0];
335  endpts[1] = conn[1];
336  }
337  else if( GeomUtil::EDGE1 == int_type )
338  {
339  endpts[0] = conn[1];
340  endpts[1] = conn[2];
341  }
342  else
343  {
344  endpts[0] = conn[2];
345  endpts[1] = conn[0];
346  }
347 
348  // get tris adjacent to edge
349  for( unsigned i = 0; i < close_tris.size(); ++i )
350  {
351  const EntityHandle* con = NULL;
352  MB_CHK_ERR_RET_VAL( MBI->get_connectivity( close_tris[i], con, len ), false );
353  if( 3 != len ) return false;
354 
355  // check both orientations in case close_tris are not on the same surface
356  if( ( endpts[0] == con[0] && endpts[1] == con[1] ) || ( endpts[0] == con[1] && endpts[1] == con[0] ) ||
357  ( endpts[0] == con[1] && endpts[1] == con[2] ) || ( endpts[0] == con[2] && endpts[1] == con[1] ) ||
358  ( endpts[0] == con[2] && endpts[1] == con[0] ) || ( endpts[0] == con[0] && endpts[1] == con[2] ) )
359  {
360  adj_tris.push_back( close_tris[i] );
361  adj_senses.push_back( close_senses[i] );
362  }
363  }
364  // In a 2-manifold each edge is adjacent to exactly 2 tris
365  if( 2 != adj_tris.size() )
366  {
367  std::cerr << "error: edge of a manifold must be topologically adjacent to exactly 2 tris" << std::endl;
368  MBI->list_entities( endpts, 2 );
369  return true;
370  }
371  }
372  else
373  {
374  std::cerr << "error: special case not an node/edge intersection" << std::endl;
375  return false;
376  }
377 
378  // The close tris were in proximity to the intersection. The adj_tris are
379  // topologically adjacent to the intersection (the neighborhood).
380  if( neighborhood_tris ) ( *neighborhood_tris ).assign( adj_tris.begin(), adj_tris.end() );
381 
382  // determine glancing/piercing
383  // If a desired_orientation was used in this call to ray_intersect_sets,
384  // the plucker_ray_tri_intersect will have already used it. For a piercing
385  // intersection, the normal of all tris must have the same orientation.
386  int sign = 0;
387  for( unsigned i = 0; i < adj_tris.size(); ++i )
388  {
389  const EntityHandle* con = NULL;
390  MB_CHK_ERR_RET_VAL( MBI->get_connectivity( adj_tris[i], con, len ), false );
391  if( 3 != len ) return false;
392 
393  CartVect coords[3];
394  MB_CHK_ERR_RET_VAL( MBI->get_coords( con, len, coords[0].array() ), false );
395 
396  // get normal of triangle
397  CartVect v0 = coords[1] - coords[0];
398  CartVect v1 = coords[2] - coords[0];
399  CartVect norm = adj_senses[i] * ( v0 * v1 );
400  double dot_prod = norm % ray_dir;
401 
402  // if the sign has not yet been decided, choose it
403  if( 0 == sign && 0 != dot_prod )
404  {
405  if( 0 < dot_prod )
406  sign = 1;
407  else
408  sign = -1;
409  }
410 
411  // intersection is glancing if tri and ray do not point in same direction
412  // for every triangle
413  if( 0 != sign && 0 > sign * dot_prod ) return false;
414  }
415  return true;
416 }
417 
419  EntityHandle t,
420  double int_dist,
422  GeomUtil::intersection_type int_type )
423 {
424  // Do not accept intersections if they are in the vector of previously intersected
425  // facets.
426  if( in_prevFacets( t ) ) return MB_SUCCESS;
427 
428  // Do not accept intersections if they are in the neighborhood of previous
429  // intersections.
430  if( in_neighborhoods( t ) ) return MB_SUCCESS;
431 
432  neighborhood.clear();
433 
434  // Handle special case of edge/node intersection. Accept piercing
435  // intersections and reject glancing intersections.
436  // The edge_node_intersection function needs to know surface sense wrt volume.
437  // A less-robust implementation could work without sense information.
438  // Would it ever be useful to accept a glancing intersection?
439  if( GeomUtil::INTERIOR != int_type && rootSet && geomVol && senseTag )
440  {
441  // get triangles in the proximity of the intersection
442  std::vector< EntityHandle > close_tris;
443  std::vector< int > close_senses;
445  close_tris, close_senses ),
446  "failed to get close triangles" );
447 
448  if( !edge_node_piercing_intersect( t, ray_direction, int_type, close_tris, close_senses,
450  return MB_SUCCESS;
451  }
452  else
453  {
454  neighborhood.push_back( t );
455  }
456 
457  // NOTE: add_intersection may modify the 'neg_ray_len' and 'nonneg_ray_len'
458  // members, which will affect subsequent calls to ray_tri_intersect
459  // in this loop.
460  add_intersection( set, t, int_dist, search_win );
461 
462  return MB_SUCCESS;
463 }
464 
466 {
467  intersections.push_back( dist );
468  sets.push_back( set );
469  facets.push_back( facet );
470  neighborhoods.push_back( neighborhood );
471  return;
472 }
473 
474 void GQT_IntRegCtxt::set_intersection( int len_idx, EntityHandle set, EntityHandle facet, double dist )
475 {
476  intersections[len_idx] = dist;
477  sets[len_idx] = set;
478  facets[len_idx] = facet;
479  return;
480 }
481 
482 /* Mode 1: Used if neg_ray_len and nonneg_ray_len are specified
483  variables used: nonneg_ray_len, neg_ray_len
484  variables not used: min_tol_int, tol
485  1) keep the closest nonneg intersection and one negative intersection, if closer
486 */
488  EntityHandle facet,
489  double dist,
491 {
492  if( 2 != intersections.size() )
493  {
494  intersections.resize( 2, 0 );
495  sets.resize( 2, 0 );
496  facets.resize( 2, 0 );
497  // must initialize this for comparison below
498  intersections[0] = -std::numeric_limits< double >::max();
499  }
500 
501  // negative case
502  if( 0.0 > dist )
503  {
504  set_intersection( 0, set, facet, dist );
505  search_win.second = &intersections[0];
506  // nonnegative case
507  }
508  else
509  {
510  set_intersection( 1, set, facet, dist );
511  search_win.first = &intersections[1];
512  // if the intersection is closer than the negative one, remove the negative one
513  if( dist < -*( search_win.second ) )
514  {
515  set_intersection( 0, 0, 0, -intersections[1] );
516  search_win.second = &intersections[0];
517  }
518  }
519  // std::cout << "add_intersection: dist = " << dist << " search_win.second=" <<
520  // *search_win.second
521  // << " search_win.first=" << *search_win.first << std::endl;
522  return;
523 }
524 
526  EntityHandle facet,
527  double dist,
529 {
530 
531  // Mode 1, detected by non-null neg_ray_len pointer
532  // keep the closest nonneg intersection and one negative intersection, if closer
533  if( search_win.second && search_win.first )
534  {
535  return add_mode1_intersection( set, facet, dist, search_win );
536  }
537 
538  // ---------------------------------------------------------------------------
539  /* Mode 2: Used if neg_ray_len is not specified
540  variables used: min_tol_int, tol, search_win.first
541  variables not used: neg_ray_len
542  1) if(min_tol_int<0) return all intersections
543  2) otherwise return all inside tolerance and unless there are >min_tol_int
544  inside of tolerance, return the closest outside of tolerance */
545  // Mode 2
546  // If minTolInt is less than zero, return all intersections
547  if( minTolInt < 0 && dist > -tol )
548  {
549  append_intersection( set, facet, dist );
550  neighborhoods.push_back( neighborhood );
551  return;
552  }
553 
554  // Check if the 'len' pointer is pointing into the intersection
555  // list. If this is the case, then the list contains, at that
556  // location, an intersection greater than the tolerance away from
557  // the base point of the ray.
558  int len_idx = -1;
559  if( search_win.first && search_win.first >= &intersections[0] &&
560  search_win.first < &intersections[0] + intersections.size() )
561  len_idx = search_win.first - &intersections[0];
562 
563  // If the intersection is within tol of the ray base point, we
564  // always add it to the list.
565  if( dist <= tol )
566  {
567  // If the list contains an intersection outside the tolerance...
568  if( len_idx >= 0 )
569  {
570  // If we no longer want an intersection outside the tolerance,
571  // remove it.
572  if( (int)intersections.size() >= minTolInt )
573  {
574  set_intersection( len_idx, set, facet, dist );
575  // From now on, we want only intersections within the tolerance,
576  // so update length accordingly
577  search_win.first = &tol;
578  }
579  // Otherwise appended to the list and update pointer
580  else
581  {
582  append_intersection( set, facet, dist );
583  search_win.first = &intersections[len_idx];
584  }
585  }
586  // Otherwise just append it
587  else
588  {
589  append_intersection( set, facet, dist );
590  // If we have all the intersections we want, set
591  // length such that we will only find further intersections
592  // within the tolerance
593  if( (int)intersections.size() >= minTolInt ) search_win.first = &tol;
594  }
595  }
596  // Otherwise the intersection is outside the tolerance
597  // If we already have an intersection outside the tolerance and
598  // this one is closer, replace the existing one with this one.
599  else if( len_idx >= 0 )
600  {
601  if( dist <= *search_win.first )
602  {
603  set_intersection( len_idx, set, facet, dist );
604  }
605  }
606  // Otherwise if we want an intersection outside the tolerance
607  // and don'thave one yet, add it.
608  else if( (int)intersections.size() < minTolInt )
609  {
610  append_intersection( set, facet, dist );
611  // update length. this is currently the closest intersection, so
612  // only want further intersections that are closer than this one.
613  search_win.first = &intersections.back();
614  }
615 }
616 
618  bool find_geomsets,
619  EntityHandle modelRootSet,
620  bool p_rootSets_vector,
621  bool restore_rootSets,
622  bool trace_counting,
623  double overlap_thickness,
624  double numerical_precision )
625  : verbose( false ), owns_gtt( true )
626 {
627  geomTopoTool = new GeomTopoTool( impl, find_geomsets, modelRootSet, p_rootSets_vector, restore_rootSets );
628 
630 
633 
634  counting = trace_counting;
635  overlapThickness = overlap_thickness;
636  numericalPrecision = numerical_precision;
637 
638  // reset query counters
639  n_pt_in_vol_calls = 0;
640  n_ray_fire_calls = 0;
641 }
642 
644  bool trace_counting,
645  double overlap_thickness,
646  double numerical_precision )
647  : verbose( false ), owns_gtt( false )
648 {
649 
650  geomTopoTool = geomtopotool;
651 
653 
656 
657  counting = trace_counting;
658  overlapThickness = overlap_thickness;
659  numericalPrecision = numerical_precision;
660 
661  // reset query counters
662  n_pt_in_vol_calls = 0;
663  n_ray_fire_calls = 0;
664 }
665 
667 {
668  if( owns_gtt )
669  {
670  delete geomTopoTool;
671  }
672 }
673 
675 {
676  MB_CHK_SET_ERR( geomTopoTool->find_geomsets(), "Failed to find geometry sets" );
677 
678  MB_CHK_SET_ERR( geomTopoTool->setup_implicit_complement(), "Couldn't setup the implicit complement" );
679 
680  MB_CHK_SET_ERR( geomTopoTool->construct_obb_trees(), "Failed to construct OBB trees" );
681 
682  return MB_SUCCESS;
683 }
684 
686 {
687  prev_facets.clear();
688 }
689 
691 {
692 
693  if( prev_facets.size() > 1 )
694  {
695  prev_facets[0] = prev_facets.back();
696  prev_facets.resize( 1 );
697  }
698 }
699 
701 {
702  if( prev_facets.size() ) prev_facets.pop_back();
703 }
704 
706 {
707  if( prev_facets.size() > 0 )
708  {
709  last_facet_hit = prev_facets.back();
710  return MB_SUCCESS;
711  }
712  else
713  {
714  return MB_ENTITY_NOT_FOUND;
715  }
716 }
717 
719 {
720  return std::find( prev_facets.begin(), prev_facets.end(), ent ) != prev_facets.end();
721 }
722 
724 {
725  prev_facets.push_back( ent );
726 }
727 
729  const double point[3],
730  const double dir[3],
731  EntityHandle& next_surf,
732  double& next_surf_dist,
733  RayHistory* history,
734  double user_dist_limit,
735  int ray_orientation,
737 {
738 
739  // take some stats that are independent of nps
740  if( counting )
741  {
743  if( 0 == n_ray_fire_calls % 10000000 )
744  {
745  std::cout << "n_ray_fires=" << n_ray_fire_calls << " n_pt_in_vols=" << n_pt_in_vol_calls << std::endl;
746  }
747  }
748 
749 #ifdef MB_GQT_DEBUG
750  std::cout << "ray_fire:"
751  << " xyz=" << point[0] << " " << point[1] << " " << point[2] << " uvw=" << dir[0] << " " << dir[1] << " "
752  << dir[2] << " entity_handle=" << volume << std::endl;
753 #endif
754 
755  const double huge_val = std::numeric_limits< double >::max();
756  double dist_limit = huge_val;
757  if( user_dist_limit > 0 ) dist_limit = user_dist_limit;
758 
759  // don't recreate these every call
760  std::vector< double > dists;
761  std::vector< EntityHandle > surfs;
762  std::vector< EntityHandle > facets;
763 
764  EntityHandle root;
765  MB_CHK_SET_ERR( geomTopoTool->get_root( volume, root ), "Failed to get the obb tree root of the volume" );
766 
767  // check behind the ray origin for intersections
768  double neg_ray_len;
769  if( 0 == overlapThickness )
770  {
771  neg_ray_len = -numericalPrecision;
772  }
773  else
774  {
775  neg_ray_len = -overlapThickness;
776  }
777 
778  // optionally, limit the nonneg_ray_len with the distance to next collision.
779  double nonneg_ray_len = dist_limit;
780 
781  // the nonneg_ray_len should not be less than -neg_ray_len, or an overlap
782  // may be missed due to optimization within ray_intersect_sets
783  if( nonneg_ray_len < -neg_ray_len ) nonneg_ray_len = -neg_ray_len;
784  if( 0 > nonneg_ray_len || 0 <= neg_ray_len )
785  {
786  MB_SET_ERR( MB_FAILURE, "Incorrect ray length provided" );
787  }
788 
789  // min_tolerance_intersections is passed but not used in this call
790  const int min_tolerance_intersections = 0;
791 
792  // numericalPrecision is used for box.intersect_ray and find triangles in the
793  // neighborhood of edge/node intersections.
794  GQT_IntRegCtxt int_reg_ctxt( geomTopoTool->obb_tree(), point, dir, numericalPrecision, min_tolerance_intersections,
795  &root, &volume, &senseTag, &ray_orientation,
796  history ? &( history->prev_facets ) : NULL );
797 
798  OrientedBoxTreeTool::IntersectSearchWindow search_win( &nonneg_ray_len, &neg_ray_len );
799  MB_CHK_SET_ERR( geomTopoTool->obb_tree()->ray_intersect_sets( dists, surfs, facets, root, numericalPrecision, point,
800  dir, search_win, int_reg_ctxt, stats ),
801  "Ray query failed" );
802 
803  // If no distances are returned, the particle is lost unless the physics limit
804  // is being used. If the physics limit is being used, there is no way to tell
805  // if the particle is lost. To avoid ambiguity, DO NOT use the distance limit
806  // unless you know lost particles do not occur.
807  if( dists.empty() )
808  {
809  next_surf = 0;
810 #ifdef MB_GQT_DEBUG
811  std::cout << " next_surf=0 dist=(undef)" << std::endl;
812 #endif
813  return MB_SUCCESS;
814  }
815 
816  // Assume that a (neg, nonneg) pair of RTIs could be returned,
817  // however, only one or the other may exist. dists[] may be populated, but
818  // intersections are ONLY indicated by nonzero surfs[] and facets[].
819  if( 2 != dists.size() || 2 != facets.size() )
820  {
821  MB_SET_ERR( MB_FAILURE, "Incorrect number of facets/distances" );
822  }
823  if( 0.0 < dists[0] || 0.0 > dists[1] )
824  {
825  MB_SET_ERR( MB_FAILURE, "Invalid intersection distance signs" );
826  }
827 
828  // If both negative and nonnegative RTIs are returned, the negative RTI must
829  // closer to the origin.
830  if( ( 0 != facets[0] && 0 != facets[1] ) && ( -dists[0] > dists[1] ) )
831  {
832  MB_SET_ERR( MB_FAILURE, "Invalid intersection distance values" );
833  }
834 
835  // If an RTI is found at negative distance, perform a PMT to see if the
836  // particle is inside an overlap.
837  int exit_idx = -1;
838  if( 0 != facets[0] )
839  {
840  // get the next volume
841  std::vector< EntityHandle > vols;
842  EntityHandle nx_vol;
843  MB_CHK_SET_ERR( MBI->get_parent_meshsets( surfs[0], vols ), "Failed to get the parent meshsets" );
844  if( 2 != vols.size() )
845  {
846  MB_SET_ERR( MB_FAILURE, "Invaid number of parent volumes found" );
847  }
848  if( vols.front() == volume )
849  {
850  nx_vol = vols.back();
851  }
852  else
853  {
854  nx_vol = vols.front();
855  }
856  // Check to see if the point is actually in the next volume.
857  // The list of previous facets is used to topologically identify the
858  // "on_boundary" result of the PMT. This avoids a test that uses proximity
859  // (a tolerance).
860  int result;
861  MB_CHK_SET_ERR( point_in_volume( nx_vol, point, result, dir, history ), "Point in volume query failed" );
862  if( 1 == result ) exit_idx = 0;
863  }
864 
865  // if the negative distance is not the exit, try the nonnegative distance
866  if( -1 == exit_idx && 0 != facets[1] ) exit_idx = 1;
867 
868  // if the exit index is still unknown, the particle is lost
869  if( -1 == exit_idx )
870  {
871  next_surf = 0;
872 #ifdef MB_GQT_DEBUG
873  std::cout << "next surf hit = 0, dist = (undef)" << std::endl;
874 #endif
875  return MB_SUCCESS;
876  }
877 
878  // return the intersection
879  next_surf = surfs[exit_idx];
880  next_surf_dist = ( 0 > dists[exit_idx] ? 0 : dists[exit_idx] );
881 
882  if( history )
883  {
884  history->prev_facets.push_back( facets[exit_idx] );
885  }
886 
887 #ifdef MB_GQT_DEBUG
888  if( 0 > dists[exit_idx] )
889  {
890  std::cout << " OVERLAP track length=" << dists[exit_idx] << std::endl;
891  }
892  std::cout << " next_surf = " << next_surf // todo: use geomtopotool to get id by entity handle
893  << ", dist = " << next_surf_dist << " new_pt=";
894  for( int i = 0; i < 3; ++i )
895  {
896  std::cout << point[i] + dir[i] * next_surf_dist << " ";
897  }
898  std::cout << std::endl;
899 #endif
900 
901  return MB_SUCCESS;
902 }
903 
905  const double xyz[3],
906  int& result,
907  const double* uvw,
908  const RayHistory* history )
909 {
910  // take some stats that are independent of nps
911  if( counting ) ++n_pt_in_vol_calls;
912 
913  // early fail for piv - see if point inside the root level obb
914  // if its not even in the box dont bother doing anything else
915  MB_CHK_SET_ERR( point_in_box( volume, xyz, result ), "failed to query point in box" );
916  if( !result )
917  {
918  result = 0;
919  return MB_SUCCESS;
920  }
921 
922  // get OBB Tree for volume
923  EntityHandle root;
924  MB_CHK_SET_ERR( geomTopoTool->get_root( volume, root ), "Failed to find the volume's obb tree root" );
925 
926  // Don't recreate these every call. These cannot be the same as the ray_fire
927  // vectors because both are used simultaneously.
928  std::vector< double > dists;
929  std::vector< EntityHandle > surfs;
930  std::vector< EntityHandle > facets;
931  std::vector< int > dirs;
932 
933  // if uvw is not given or is full of zeros, use a random direction
934  double u = 0, v = 0, w = 0;
935 
936  if( uvw )
937  {
938  u = uvw[0];
939  v = uvw[1], w = uvw[2];
940  }
941 
942  if( u == 0 && v == 0 && w == 0 )
943  {
944  u = rand();
945  v = rand();
946  w = rand();
947  const double magnitude = sqrt( u * u + v * v + w * w );
948  u /= magnitude;
949  v /= magnitude;
950  w /= magnitude;
951  }
952 
953  const double ray_direction[] = { u, v, w };
954 
955  // if overlaps, ray must be cast to infinity and all RTIs must be returned
956  const double large = 1e15;
957  double ray_length = large;
958 
959  // If overlaps occur, the pt is inside if traveling along the ray from the
960  // origin, there are ever more exits than entrances. In lieu of implementing
961  // that, all intersections to infinity are required if overlaps occur (expensive)
962  int min_tolerance_intersections;
963  if( 0 != overlapThickness )
964  {
965  min_tolerance_intersections = -1;
966  // only the first intersection is needed if overlaps do not occur (cheap)
967  }
968  else
969  {
970  min_tolerance_intersections = 1;
971  }
972 
973  // Get intersection(s) of forward and reverse orientation. Do not return
974  // glancing intersections or previous facets.
975  GQT_IntRegCtxt int_reg_ctxt( geomTopoTool->obb_tree(), xyz, ray_direction, numericalPrecision,
976  min_tolerance_intersections, &root, &volume, &senseTag, NULL,
977  history ? &( history->prev_facets ) : NULL );
978 
979  OrientedBoxTreeTool::IntersectSearchWindow search_win( &ray_length, (double*)NULL );
980  MB_CHK_SET_ERR( geomTopoTool->obb_tree()->ray_intersect_sets( dists, surfs, facets, root, numericalPrecision, xyz,
981  ray_direction, search_win, int_reg_ctxt ),
982  "Ray fire query failed" );
983 
984  // determine orientation of all intersections
985  // 1 for entering, 0 for leaving, -1 for tangent
986  // Tangent intersections are not returned from ray_tri_intersect.
987  dirs.resize( dists.size() );
988  for( unsigned i = 0; i < dists.size(); ++i )
989  {
990  MB_CHK_SET_ERR( boundary_case( volume, dirs[i], u, v, w, facets[i], surfs[i] ),
991  "Failed to resolve boundary case" );
992  }
993 
994  // count all crossings
995  if( 0 != overlapThickness )
996  {
997  int sum = 0;
998  for( unsigned i = 0; i < dirs.size(); ++i )
999  {
1000  if( 1 == dirs[i] )
1001  sum += 1; // +1 for entering
1002  else if( 0 == dirs[i] )
1003  sum -= 1; // -1 for leaving
1004  else if( -1 == dirs[i] )
1005  { // 0 for tangent
1006  std::cout << "direction==tangent" << std::endl;
1007  sum += 0;
1008  }
1009  else
1010  {
1011  MB_SET_ERR( MB_FAILURE, "Error: unknown direction" );
1012  }
1013  }
1014 
1015  // inside/outside depends on the sum
1016  if( 0 < sum )
1017  result = 0; // pt is outside (for all vols)
1018  else if( 0 > sum )
1019  result = 1; // pt is inside (for all vols)
1020  else if( geomTopoTool->is_implicit_complement( volume ) )
1021  result = 1; // pt is inside (for impl_compl_vol)
1022  else
1023  result = 0; // pt is outside (for all other vols)
1024 
1025  // Only use the first crossing
1026  }
1027  else
1028  {
1029  if( dirs.empty() )
1030  {
1031  result = 0; // pt is outside
1032  }
1033  else
1034  {
1035  int smallest = std::min_element( dists.begin(), dists.end() ) - dists.begin();
1036  if( 1 == dirs[smallest] )
1037  result = 0; // pt is outside
1038  else if( 0 == dirs[smallest] )
1039  result = 1; // pt is inside
1040  else if( -1 == dirs[smallest] )
1041  {
1042  // Should not be here because Plucker ray-triangle test does not
1043  // return coplanar rays as intersections.
1044  std::cerr << "direction==tangent" << std::endl;
1045  result = -1;
1046  }
1047  else
1048  {
1049  MB_SET_ERR( MB_FAILURE, "Error: unknown direction" );
1050  }
1051  }
1052  }
1053 
1054 #ifdef MB_GQT_DEBUG
1055  std::cout << "pt_in_vol: result=" << result << " xyz=" << xyz[0] << " " << xyz[1] << " " << xyz[2] << " uvw=" << u
1056  << " " << v << " " << w << " vol_id=" << volume
1057  << std::endl; // todo: use geomtopotool to get id by entity handle
1058 #endif
1059 
1060  return MB_SUCCESS;
1061 }
1062 
1063 /**
1064  * \brief For the volume pointed to and the point wished to be tested, returns
1065  * whether the point is inside or outside the bounding box of the volume.
1066  * inside = 0, not inside, inside = 1, inside
1067  */
1068 ErrorCode GeomQueryTool::point_in_box( EntityHandle volume, const double point[3], int& inside )
1069 {
1070  double minpt[3];
1071  double maxpt[3];
1072  MB_CHK_SET_ERR( geomTopoTool->get_bounding_coords( volume, minpt, maxpt ),
1073  "Failed to get the bounding coordinates of the volume" );
1074 
1075  // early exits
1076  if( point[0] > maxpt[0] || point[0] < minpt[0] )
1077  {
1078  inside = 0;
1079  return MB_SUCCESS;
1080  }
1081  if( point[1] > maxpt[1] || point[1] < minpt[1] )
1082  {
1083  inside = 0;
1084  return MB_SUCCESS;
1085  }
1086  if( point[2] > maxpt[2] || point[2] < minpt[2] )
1087  {
1088  inside = 0;
1089  return MB_SUCCESS;
1090  }
1091  inside = 1;
1092  return MB_SUCCESS;
1093 }
1094 
1096  const EntityHandle surface,
1097  const double xyz[3],
1098  const double uvw[3],
1099  int& result,
1100  const RayHistory* history )
1101 {
1102  int dir;
1103 
1104  if( history && history->prev_facets.size() )
1105  {
1106  // the current facet is already available
1107  MB_CHK_SET_ERR( boundary_case( volume, dir, uvw[0], uvw[1], uvw[2], history->prev_facets.back(), surface ),
1108  "Failed to resolve the boundary case" );
1109  }
1110  else
1111  {
1112  // Get OBB Tree for surface
1113  EntityHandle root;
1114  MB_CHK_SET_ERR( geomTopoTool->get_root( volume, root ), "Failed to get the volume's OBB tree root" );
1115 
1116  // Get closest triangle on surface
1117  const CartVect point( xyz );
1118  CartVect nearest;
1119  EntityHandle facet_out;
1120  // look up nearest facet
1121  MB_CHK_SET_ERR( geomTopoTool->obb_tree()->closest_to_location( point.array(), root, nearest.array(),
1122  facet_out ),
1123  "Failed to find the closest point to location" );
1124 
1125  MB_CHK_SET_ERR( boundary_case( volume, dir, uvw[0], uvw[1], uvw[2], facet_out, surface ),
1126  "Failed to resolve the boundary case" );
1127  }
1128 
1129  result = dir;
1130 
1131  return MB_SUCCESS;
1132 }
1133 
1134 // use spherical area test to determine inside/outside of a polyhedron.
1135 ErrorCode GeomQueryTool::point_in_volume_slow( EntityHandle volume, const double xyz[3], int& result )
1136 {
1137  Range faces;
1138  std::vector< EntityHandle > surfs;
1139  std::vector< int > senses;
1140  double sum = 0.0;
1141  const CartVect point( xyz );
1142 
1143  MB_CHK_SET_ERR( MBI->get_child_meshsets( volume, surfs ), "Failed to get the volume's child surfaces" );
1144 
1145  senses.resize( surfs.size() );
1146  MB_CHK_SET_ERR( geomTopoTool->get_surface_senses( volume, surfs.size(), &surfs[0], &senses[0] ),
1147  "Failed to get the volume's surface senses" );
1148 
1149  for( unsigned i = 0; i < surfs.size(); ++i )
1150  {
1151  if( !senses[i] ) // skip non-manifold surfaces
1152  continue;
1153 
1154  double surf_area = 0.0, face_area;
1155  faces.clear();
1156  MB_CHK_SET_ERR( MBI->get_entities_by_dimension( surfs[i], 2, faces ),
1157  "Failed to get the surface entities by dimension" );
1158 
1159  for( Range::iterator j = faces.begin(); j != faces.end(); ++j )
1160  {
1161  MB_CHK_SET_ERR( poly_solid_angle( *j, point, face_area ), "Failed to determin the polygon's solid angle" );
1162 
1163  surf_area += face_area;
1164  }
1165 
1166  sum += senses[i] * surf_area;
1167  }
1168 
1169  result = fabs( sum ) > 2.0 * M_PI;
1170  return MB_SUCCESS;
1171 }
1172 
1173 ErrorCode GeomQueryTool::find_volume( const double xyz[3], EntityHandle& volume, const double* dir )
1174 {
1175  volume = 0;
1176 
1177  EntityHandle global_surf_tree_root = geomTopoTool->get_one_vol_root();
1178 
1179  // fast check - make sure point is in the implicit complement bounding box
1180  int ic_result;
1181  EntityHandle ic_handle;
1183  "Failed to get the implicit complement handle" );
1184 
1185  MB_CHK_SET_ERR( point_in_box( ic_handle, xyz, ic_result ), "Failed to check implicit complement for containment" );
1186  if( ic_result == 0 )
1187  {
1188  volume = 0;
1189  return MB_ENTITY_NOT_FOUND;
1190  }
1191 
1192  // if geomTopoTool doesn't have a global tree, use a loop over vols (slow)
1193  if( !global_surf_tree_root )
1194  {
1195  return find_volume_slow( xyz, volume, dir );
1196  }
1197 
1198  moab::CartVect uvw( 0.0 );
1199 
1200  if( dir )
1201  {
1202  uvw[0] = dir[0];
1203  uvw[1] = dir[1];
1204  uvw[2] = dir[2];
1205  }
1206 
1207  if( uvw == 0.0 )
1208  {
1209  uvw[0] = rand();
1210  uvw[1] = rand();
1211  uvw[2] = rand();
1212  }
1213 
1214  // always normalize direction
1215  uvw.normalize();
1216 
1217  // fire a ray along dir and get surface
1218  const double huge_val = std::numeric_limits< double >::max();
1219  double pos_ray_len = huge_val;
1220  double neg_ray_len = -huge_val;
1221 
1222  // RIS output data
1223  std::vector< double > dists;
1224  std::vector< EntityHandle > surfs;
1225  std::vector< EntityHandle > facets;
1226 
1227  FindVolumeIntRegCtxt find_vol_reg_ctxt;
1228  OrientedBoxTreeTool::IntersectSearchWindow search_win( &pos_ray_len, &neg_ray_len );
1229  MB_CHK_SET_ERR( geomTopoTool->obb_tree()->ray_intersect_sets( dists, surfs, facets, global_surf_tree_root,
1230  numericalPrecision, xyz, uvw.array(), search_win,
1231  find_vol_reg_ctxt ),
1232  "Failed in global tree ray fire" );
1233 
1234  // if there was no intersection, no volume is found
1235  if( surfs.size() == 0 || surfs[0] == 0 )
1236  {
1237  volume = 0;
1238  return MB_ENTITY_NOT_FOUND;
1239  }
1240 
1241  // get the positive distance facet, surface hit
1242  EntityHandle facet = facets[0];
1243  EntityHandle surf = surfs[0];
1244 
1245  // get these now, we're going to use them no matter what
1246  EntityHandle fwd_vol, bwd_vol;
1247  MB_CHK_SET_ERR( geomTopoTool->get_surface_senses( surf, fwd_vol, bwd_vol ), "Failed to get sense data" );
1248 
1249  // define parent volumes
1250  EntityHandle parent_vols[2];
1251  parent_vols[0] = fwd_vol;
1252  parent_vols[1] = bwd_vol;
1253 
1254  // get triangle normal
1255  std::vector< EntityHandle > conn;
1256  CartVect coords[3];
1257  MB_CHK_SET_ERR( MBI->get_connectivity( &facet, 1, conn ), "Failed to get triangle connectivity" );
1258 
1259  MB_CHK_SET_ERR( MBI->get_coords( &conn[0], 3, coords[0].array() ), "Failed to get triangle coordinates" );
1260 
1261  CartVect normal = ( coords[1] - coords[0] ) * ( coords[2] - coords[0] );
1262  normal.normalize();
1263 
1264  // reverse direction if a hit in the negative direction is found
1265  if( dists[0] < 0 )
1266  {
1267  uvw *= -1;
1268  }
1269 
1270  // if this is a "forward" intersection return the first sense entity
1271  // otherwise return the second, "reverse" sense entity
1272  double dot_prod = uvw % normal;
1273  int idx = dot_prod > 0.0 ? 0 : 1;
1274 
1275  if( dot_prod == 0.0 )
1276  {
1277  std::cerr << "Tangent dot product in find_volume. Shouldn't be here." << std::endl;
1278  volume = 0;
1279  return MB_FAILURE;
1280  }
1281 
1282  volume = parent_vols[idx];
1283 
1284  return MB_SUCCESS;
1285 }
1286 
1287 ErrorCode GeomQueryTool::find_volume_slow( const double xyz[3], EntityHandle& volume, const double* dir )
1288 {
1289  volume = 0;
1290  // get all volumes
1291  Range all_vols;
1292  MB_CHK_SET_ERR( geomTopoTool->get_gsets_by_dimension( 3, all_vols ), "Failed to get all volumes in the model" );
1293 
1294  Range::iterator it;
1295  int result = 0;
1296  for( it = all_vols.begin(); it != all_vols.end(); it++ )
1297  {
1298  MB_CHK_SET_ERR( point_in_volume( *it, xyz, result, dir ), "Failed in point in volume loop" );
1299  if( result )
1300  {
1301  volume = *it;
1302  break;
1303  }
1304  }
1305  return volume ? MB_SUCCESS : MB_ENTITY_NOT_FOUND;
1306 }
1307 
1308 // detemine distance to nearest surface
1310  const double coords[3],
1311  double& result,
1312  EntityHandle* closest_surface )
1313 {
1314  // Get OBB Tree for volume
1315  EntityHandle root;
1316  MB_CHK_SET_ERR( geomTopoTool->get_root( volume, root ), "Failed to get the volume's obb tree root" );
1317 
1318  // Get closest triangles in volume
1319  const CartVect point( coords );
1320  CartVect nearest;
1321  EntityHandle facet_out;
1322 
1323  MB_CHK_SET_ERR( geomTopoTool->obb_tree()->closest_to_location( point.array(), root, nearest.array(), facet_out,
1324  closest_surface ),
1325  "Failed to get the closest intersection to location" );
1326  // calculate distance between point and nearest facet
1327  result = ( point - nearest ).length();
1328 
1329  return MB_SUCCESS;
1330 }
1331 
1332 // calculate volume of polyhedron
1334 {
1335  std::vector< EntityHandle > surfaces;
1336  result = 0.0;
1337 
1338  // don't try to calculate volume of implicit complement
1339  if( geomTopoTool->is_implicit_complement( volume ) )
1340  {
1341  result = 1.0;
1342  return MB_SUCCESS;
1343  }
1344 
1345  // get surfaces from volume
1346  MB_CHK_SET_ERR( MBI->get_child_meshsets( volume, surfaces ), "Failed to get the volume's child surfaces" );
1347 
1348  // get surface senses
1349  std::vector< int > senses( surfaces.size() );
1350  MB_CHK_SET_ERR( geomTopoTool->get_surface_senses( volume, surfaces.size(), &surfaces[0], &senses[0] ),
1351  "Failed to retrieve surface-volume sense data. Cannot calculate volume" );
1352 
1353  for( unsigned i = 0; i < surfaces.size(); ++i )
1354  {
1355  // skip non-manifold surfaces
1356  if( !senses[i] ) continue;
1357 
1358  // get triangles in surface
1359  Range triangles;
1360  MB_CHK_SET_ERR( MBI->get_entities_by_dimension( surfaces[i], 2, triangles ),
1361  "Failed to get the surface triangles" );
1362 
1363  if( !triangles.all_of_type( MBTRI ) )
1364  {
1365  std::cout << "WARNING: Surface " << surfaces[i] // todo: use geomtopotool to get id by entity handle
1366  << " contains non-triangle elements. Volume calculation may be incorrect." << std::endl;
1367  triangles.clear();
1368  MB_CHK_SET_ERR( MBI->get_entities_by_type( surfaces[i], MBTRI, triangles ),
1369  "Failed to get the surface triangles" );
1370  }
1371 
1372  // calculate signed volume beneath surface (x 6.0)
1373  double surf_sum = 0.0;
1374  const EntityHandle* conn;
1375  int len;
1376  CartVect coords[3];
1377  for( Range::iterator j = triangles.begin(); j != triangles.end(); ++j )
1378  {
1379  MB_CHK_SET_ERR( MBI->get_connectivity( *j, conn, len, true ),
1380  "Failed to get the connectivity of the current triangle" );
1381  if( 3 != len )
1382  {
1383  MB_SET_ERR( MB_FAILURE, "Incorrect connectivity length for triangle" );
1384  }
1385  MB_CHK_SET_ERR( MBI->get_coords( conn, 3, coords[0].array() ),
1386  "Failed to get the coordinates of the current triangle's vertices" );
1387 
1388  coords[1] -= coords[0];
1389  coords[2] -= coords[0];
1390  surf_sum += ( coords[0] % ( coords[1] * coords[2] ) );
1391  }
1392  result += senses[i] * surf_sum;
1393  }
1394 
1395  result /= 6.0;
1396  return MB_SUCCESS;
1397 }
1398 
1399 // sum area of elements in surface
1401 {
1402  // get triangles in surface
1403  Range triangles;
1404  MB_CHK_SET_ERR( MBI->get_entities_by_dimension( surface, 2, triangles ), "Failed to get the surface entities" );
1405  if( !triangles.all_of_type( MBTRI ) )
1406  {
1407  std::cout << "WARNING: Surface " << surface // todo: use geomtopotool to get id by entity handle
1408  << " contains non-triangle elements. Area calculation may be incorrect." << std::endl;
1409  triangles.clear();
1410  MB_CHK_SET_ERR( MBI->get_entities_by_type( surface, MBTRI, triangles ),
1411  "Failed to the surface's triangle entities" );
1412  }
1413 
1414  // calculate sum of area of triangles
1415  result = 0.0;
1416  const EntityHandle* conn;
1417  int len;
1418  CartVect coords[3];
1419  for( Range::iterator j = triangles.begin(); j != triangles.end(); ++j )
1420  {
1421  MB_CHK_SET_ERR( MBI->get_connectivity( *j, conn, len, true ),
1422  "Failed to get the current triangle's connectivity" );
1423  if( 3 != len )
1424  {
1425  MB_SET_ERR( MB_FAILURE, "Incorrect connectivity length for triangle" );
1426  }
1427  MB_CHK_SET_ERR( MBI->get_coords( conn, 3, coords[0].array() ),
1428  "Failed to get the current triangle's vertex coordinates" );
1429 
1430  // calculated area using cross product of triangle edges
1431  CartVect v1 = coords[1] - coords[0];
1432  CartVect v2 = coords[2] - coords[0];
1433  CartVect xp = v1 * v2;
1434  result += xp.length();
1435  }
1436  result *= 0.5;
1437  return MB_SUCCESS;
1438 }
1439 
1441  const double in_pt[3],
1442  double angle[3],
1443  const RayHistory* history )
1444 {
1445  EntityHandle root;
1446  MB_CHK_SET_ERR( geomTopoTool->get_root( surf, root ), "Failed to get the surface's obb tree root" );
1447 
1448  std::vector< EntityHandle > facets;
1449 
1450  // if no history or history empty, use nearby facets
1451  if( !history || ( history->prev_facets.size() == 0 ) )
1452  {
1454  "Failed to get closest intersection to location" );
1455  }
1456  // otherwise use most recent facet in history
1457  else
1458  {
1459  facets.push_back( history->prev_facets.back() );
1460  }
1461 
1462  CartVect coords[3], normal( 0.0 );
1463  const EntityHandle* conn;
1464  int len;
1465  for( unsigned i = 0; i < facets.size(); ++i )
1466  {
1467  MB_CHK_SET_ERR( MBI->get_connectivity( facets[i], conn, len ), "Failed to get facet connectivity" );
1468  if( 3 != len )
1469  {
1470  MB_SET_ERR( MB_FAILURE, "Incorrect connectivity length for triangle" );
1471  }
1472 
1473  MB_CHK_SET_ERR( MBI->get_coords( conn, 3, coords[0].array() ), "Failed to get vertex coordinates" );
1474 
1475  coords[1] -= coords[0];
1476  coords[2] -= coords[0];
1477  normal += coords[1] * coords[2];
1478  }
1479 
1480  normal.normalize();
1481  normal.get( angle );
1482 
1483  return MB_SUCCESS;
1484 }
1485 
1486 /* SECTION II (private) */
1487 
1488 // If point is on boundary, then this function is called to
1489 // discriminate cases in which the ray is entering or leaving.
1490 // result= 1 -> inside volume or entering volume
1491 // result= 0 -> outside volume or leaving volume
1492 // result=-1 -> on boundary with null or tangent uvw
1494  int& result,
1495  double u,
1496  double v,
1497  double w,
1498  EntityHandle facet,
1499  EntityHandle surface )
1500 {
1501  // test to see if uvw is provided
1502  if( u <= 1.0 && v <= 1.0 && w <= 1.0 )
1503  {
1504 
1505  const CartVect ray_vector( u, v, w );
1506  CartVect coords[3], normal( 0.0 );
1507  const EntityHandle* conn;
1508  int len, sense_out;
1509 
1510  MB_CHK_SET_ERR( MBI->get_connectivity( facet, conn, len ), "Failed to get the triangle's connectivity" );
1511  if( 3 != len )
1512  {
1513  MB_SET_ERR( MB_FAILURE, "Incorrect connectivity length for triangle" );
1514  }
1515 
1516  MB_CHK_SET_ERR( MBI->get_coords( conn, 3, coords[0].array() ), "Failed to get vertex coordinates" );
1517 
1518  MB_CHK_SET_ERR( geomTopoTool->get_sense( surface, volume, sense_out ),
1519  "Failed to get the surface's sense with respect to it's volume" );
1520 
1521  coords[1] -= coords[0];
1522  coords[2] -= coords[0];
1523  normal = sense_out * ( coords[1] * coords[2] );
1524 
1525  double sense = ray_vector % normal;
1526 
1527  if( sense < 0.0 )
1528  {
1529  result = 1; // inside or entering
1530  }
1531  else if( sense > 0.0 )
1532  {
1533  result = 0; // outside or leaving
1534  }
1535  else if( sense == 0.0 )
1536  {
1537  result = -1; // tangent, therefore on boundary
1538  }
1539  else
1540  {
1541  result = -1; // failure
1542  MB_SET_ERR( MB_FAILURE, "Failed to resolve boundary case" );
1543  }
1544 
1545  // if uvw not provided, return on_boundary.
1546  }
1547  else
1548  {
1549  result = -1; // on boundary
1550  return MB_SUCCESS;
1551  }
1552 
1553  return MB_SUCCESS;
1554 }
1555 
1556 // point_in_volume_slow, including poly_solid_angle helper subroutine
1557 // are adapted from "Point in Polyhedron Testing Using Spherical Polygons", Paulo Cezar
1558 // Pinto Carvalho and Paulo Roma Cavalcanti, _Graphics Gems V_, pg. 42. Original algorithm
1559 // was described in "An Efficient Point In Polyhedron Algorithm", Jeff Lane, Bob Magedson,
1560 // and Mike Rarick, _Computer Vision, Graphics, and Image Processing 26_, pg. 118-225, 1984.
1561 
1562 // helper function for point_in_volume_slow. calculate area of a polygon
1563 // projected into a unit-sphere space
1565 {
1566  // Get connectivity
1567  const EntityHandle* conn;
1568  int len;
1569  MB_CHK_SET_ERR( MBI->get_connectivity( face, conn, len, true ), "Failed to get the connectivity of the polygon" );
1570 
1571  // Allocate space to store vertices
1572  CartVect coords_static[4];
1573  std::vector< CartVect > coords_dynamic;
1574  CartVect* coords = coords_static;
1575  if( (unsigned)len > ( sizeof( coords_static ) / sizeof( coords_static[0] ) ) )
1576  {
1577  coords_dynamic.resize( len );
1578  coords = &coords_dynamic[0];
1579  }
1580 
1581  // get coordinates
1582  MB_CHK_SET_ERR( MBI->get_coords( conn, len, coords->array() ),
1583  "Failed to get the coordinates of the polygon vertices" );
1584 
1585  // calculate normal
1586  CartVect norm( 0.0 ), v1, v0 = coords[1] - coords[0];
1587  for( int i = 2; i < len; ++i )
1588  {
1589  v1 = coords[i] - coords[0];
1590  norm += v0 * v1;
1591  v0 = v1;
1592  }
1593 
1594  // calculate area
1595  double s, ang;
1596  area = 0.0;
1597  CartVect r, n1, n2, b, a = coords[len - 1] - coords[0];
1598  for( int i = 0; i < len; ++i )
1599  {
1600  r = coords[i] - point;
1601  b = coords[( i + 1 ) % len] - coords[i];
1602  n1 = a * r; // = norm1 (magnitude is important)
1603  n2 = r * b; // = norm2 (magnitude is important)
1604  s = ( n1 % n2 ) / ( n1.length() * n2.length() ); // = cos(angle between norm1,norm2)
1605  ang = s <= -1.0 ? M_PI : s >= 1.0 ? 0.0 : acos( s ); // = acos(s)
1606  s = ( b * a ) % norm; // =orientation of triangle wrt point
1607  area += s > 0.0 ? M_PI - ang : M_PI + ang;
1608  a = -b;
1609  }
1610 
1611  area -= M_PI * ( len - 2 );
1612  if( ( norm % r ) > 0 ) area = -area;
1613  return MB_SUCCESS;
1614 }
1615 
1616 void GeomQueryTool::set_overlap_thickness( double new_thickness )
1617 {
1618  if( new_thickness < 0 || new_thickness > 100 )
1619  {
1620  std::cerr << "Invalid overlap_thickness = " << new_thickness << std::endl;
1621  }
1622  else
1623  {
1624  overlapThickness = new_thickness;
1625  }
1626  if( verbose ) std::cout << "Set overlap thickness = " << overlapThickness << std::endl;
1627 }
1628 
1629 void GeomQueryTool::set_numerical_precision( double new_precision )
1630 {
1631  if( new_precision <= 0 || new_precision > 1 )
1632  {
1633  std::cerr << "Invalid numerical_precision = " << numericalPrecision << std::endl;
1634  }
1635  else
1636  {
1637  numericalPrecision = new_precision;
1638  }
1639  if( verbose ) std::cout << "Set numerical precision = " << numericalPrecision << std::endl;
1640 }
1641 
1642 } // namespace moab