![]() |
Mesh Oriented datABase
(version 5.4.1)
Array-based unstructured mesh datastructure
|
00001 #include "moab/GeomQueryTool.hpp"
00002
00003 #ifdef WIN32 /* windows */
00004 #define _USE_MATH_DEFINES // For M_PI
00005 #endif
00006 #include
00007 #include
00008 #include
00009 #include
00010 #include
00011 #include
00012 #include
00013
00014 #include
00015 #include
00016 #include
00017 #include
00018
00019 #include "moab/OrientedBoxTreeTool.hpp"
00020
00021 const bool debug = false;
00022 #ifdef __DEBUG
00023 debug = true;
00024 #endif
00025
00026 namespace moab
00027 {
00028
00029 /** \class FindVolume_IntRegCtxt
00030 *
00031 *
00032 *\brief An intersection context used for finding a volume
00033 *
00034 * This context is used to find the nearest intersection location
00035 * and is intended for use with a global surface tree from
00036 * the GeomTopoTool.
00037 *
00038 * The behavior of this context is relatively simple in that it
00039 * returns only one intersection distance, surface, and facet.
00040 * Intersections of any orientation are accepted. The positive
00041 * value of the search window is limited to the current nearest
00042 * intersection distance.
00043 *
00044 */
00045
00046 class FindVolumeIntRegCtxt : public OrientedBoxTreeTool::IntRegCtxt
00047 {
00048
00049 public:
00050 // Constructor
00051 FindVolumeIntRegCtxt()
00052 {
00053 // initialize return vectors
00054 // only one hit is returned in this context
00055 intersections.push_back( std::numeric_limits< double >::max() );
00056 sets.push_back( 0 );
00057 facets.push_back( 0 );
00058 }
00059
00060 ErrorCode register_intersection( EntityHandle set,
00061 EntityHandle tri,
00062 double dist,
00063 OrientedBoxTreeTool::IntersectSearchWindow& search_win,
00064 GeomUtil::intersection_type /*it*/ )
00065 {
00066 // update dist, set, and triangle hit if
00067 // we found a new minimum distance
00068 double abs_dist = fabs( dist );
00069 if( abs_dist < fabs( intersections[0] ) )
00070 {
00071 intersections[0] = dist;
00072 sets[0] = set;
00073 facets[0] = tri;
00074
00075 // narrow search window based on the hit distance
00076 pos = abs_dist;
00077 neg = -abs_dist;
00078 search_win.first = &pos;
00079 search_win.second = &neg;
00080 }
00081
00082 return MB_SUCCESS;
00083 }
00084
00085 // storage for updated window values during search
00086 double pos;
00087 double neg;
00088 };
00089
00090 /** \class GQT_IntRegCtxt
00091 *
00092 *\brief An implementation of an Intersection Registration Context for use GQT ray-firing
00093 *
00094 * This context uses a variety of tests and conditions to confirm whether or
00095 * not to accumulate an intersection, to ensure robustness for ray firing.
00096 *
00097 * This context only accumulates intersections that are oriented parallel to
00098 * the 'desiredOrient', if provided, with respect to 'geomVol', using
00099 * information in the in 'senseTag'.
00100 *
00101 * This context only accumulates a single intersection out of a set of
00102 * multiple intersections that fall in the same 'neighborhood', where a
00103 * 'neighborhood' is defined as facets that share edges or vertices.
00104 *
00105 * This context only accumulates piercing intersections. This is relevant
00106 * for intersections that are found to be on an edge or vertex by the
00107 * Plucker test. Such intersections are piercing if the ray has the same
00108 * orientation w.r.t. to all fecets that share that edge or vertex.
00109 *
00110 * This context tests intersections against a list of 'prevFacets' to
00111 * prevent a ray from crossing the same facet more than once. The user is
00112 * responsible for ensuring that this list is reset when appropriate.
00113 *
00114 * This context accumulates all intersections within 'tol' of the
00115 * start of the ray and if the number of intersections within the
00116 * 'tol' of the ray start point is less than 'minTolInt', the next
00117 * closest intersection. If the desired result is only the closest
00118 * intersection, 'minTolInt' should be 0. This function will return all
00119 * intersections, regardless of distance from the start of the ray, if
00120 * 'minTolInt' is negative.
00121 *
00122 */
00123
00124 class GQT_IntRegCtxt : public OrientedBoxTreeTool::IntRegCtxt
00125 {
00126
00127 private:
00128 // Input
00129 OrientedBoxTreeTool* tool;
00130 const CartVect ray_origin;
00131 const CartVect ray_direction;
00132 const double tol; /* used for box.intersect_ray, radius of
00133 neighborhood for adjacent triangles,
00134 and old mode of add_intersection */
00135 const int minTolInt;
00136
00137 // Optional Input - to screen RTIs by orientation and edge/node intersection
00138 const EntityHandle* rootSet; /* used for sphere_intersect */
00139 const EntityHandle* geomVol; /* used for determining surface sense */
00140 const Tag* senseTag; /* allows screening by triangle orientation.
00141 both geomVol and senseTag must be used together. */
00142 const int* desiredOrient; /* points to desired orientation of ray with
00143 respect to surf normal, if this feature is used.
00144 Must point to -1 (reverse) or 1 (forward).
00145 geomVol and senseTag are needed for this feature */
00146
00147 // Optional Input - to avoid returning these as RTIs
00148 const std::vector< EntityHandle >* prevFacets; /* intersections on these triangles
00149 will not be returned */
00150
00151 // Other Variables
00152 std::vector< std::vector< EntityHandle > > neighborhoods;
00153 std::vector< EntityHandle > neighborhood;
00154
00155 void add_intersection( EntityHandle set,
00156 EntityHandle tri,
00157 double dist,
00158 OrientedBoxTreeTool::IntersectSearchWindow& search_win );
00159 void append_intersection( EntityHandle set, EntityHandle facet, double dist );
00160 void set_intersection( int len_idx, EntityHandle set, EntityHandle facet, double dist );
00161 void add_mode1_intersection( EntityHandle set,
00162 EntityHandle facet,
00163 double dist,
00164 OrientedBoxTreeTool::IntersectSearchWindow& search_win );
00165 bool edge_node_piercing_intersect( const EntityHandle tri,
00166 const CartVect& ray_direction,
00167 const GeomUtil::intersection_type int_type,
00168 const std::vector< EntityHandle >& close_tris,
00169 const std::vector< int >& close_senses,
00170 const Interface* MBI,
00171 std::vector< EntityHandle >* neighborhood_tris = 0 );
00172
00173 bool in_prevFacets( const EntityHandle tri );
00174 bool in_neighborhoods( const EntityHandle tri );
00175
00176 public:
00177 GQT_IntRegCtxt( OrientedBoxTreeTool* obbtool,
00178 const double ray_point[3],
00179 const double ray_dir[3],
00180 double tolerance,
00181 int min_tolerance_intersections,
00182 const EntityHandle* root_set,
00183 const EntityHandle* geom_volume,
00184 const Tag* sense_tag,
00185 const int* desired_orient,
00186 const std::vector< EntityHandle >* prev_facets )
00187 : tool( obbtool ), ray_origin( ray_point ), ray_direction( ray_dir ), tol( tolerance ),
00188 minTolInt( min_tolerance_intersections ), rootSet( root_set ), geomVol( geom_volume ), senseTag( sense_tag ),
00189 desiredOrient( desired_orient ), prevFacets( prev_facets ){
00190
00191 };
00192
00193 virtual ErrorCode register_intersection( EntityHandle set,
00194 EntityHandle triangle,
00195 double distance,
00196 OrientedBoxTreeTool::IntersectSearchWindow&,
00197 GeomUtil::intersection_type int_type );
00198
00199 virtual ErrorCode update_orient( EntityHandle set, int* surfTriOrient );
00200 virtual const int* getDesiredOrient()
00201 {
00202 return desiredOrient;
00203 };
00204 };
00205
00206 ErrorCode GQT_IntRegCtxt::update_orient( EntityHandle set, int* surfTriOrient )
00207 {
00208
00209 ErrorCode rval;
00210
00211 // Get desired orientation of surface wrt volume. Use this to return only
00212 // exit or entrance intersections.
00213 if( geomVol && senseTag && desiredOrient && surfTriOrient )
00214 {
00215 if( 1 != *desiredOrient && -1 != *desiredOrient )
00216 {
00217 std::cerr << "error: desired orientation must be 1 (forward) or -1 (reverse)" << std::endl;
00218 }
00219 EntityHandle vols[2];
00220 rval = tool->get_moab_instance()->tag_get_data( *senseTag, &set, 1, vols );
00221 assert( MB_SUCCESS == rval );
00222 if( MB_SUCCESS != rval ) return rval;
00223 if( vols[0] == vols[1] )
00224 {
00225 std::cerr << "error: surface has positive and negative sense wrt same volume" << std::endl;
00226 return MB_FAILURE;
00227 }
00228 // surfTriOrient will be used by plucker_ray_tri_intersect to avoid
00229 // intersections with wrong orientation.
00230 if( *geomVol == vols[0] )
00231 {
00232 *surfTriOrient = *desiredOrient * 1;
00233 }
00234 else if( *geomVol == vols[1] )
00235 {
00236 *surfTriOrient = *desiredOrient * ( -1 );
00237 }
00238 else
00239 {
00240 assert( false );
00241 return MB_FAILURE;
00242 }
00243 }
00244
00245 return MB_SUCCESS;
00246 }
00247
00248 bool GQT_IntRegCtxt::in_prevFacets( const EntityHandle tri )
00249 {
00250 return ( prevFacets && ( ( *prevFacets ).end() != find( ( *prevFacets ).begin(), ( *prevFacets ).end(), tri ) ) );
00251 }
00252
00253 bool GQT_IntRegCtxt::in_neighborhoods( const EntityHandle tri )
00254 {
00255 bool same_neighborhood = false;
00256 for( unsigned i = 0; i < neighborhoods.size(); ++i )
00257 {
00258 if( neighborhoods[i].end() != find( neighborhoods[i].begin(), neighborhoods[i].end(), tri ) )
00259 {
00260 same_neighborhood = true;
00261 continue;
00262 }
00263 }
00264 return same_neighborhood;
00265 }
00266
00267 /**\brief Determine if a ray-edge/node intersection is glancing or piercing.
00268 * This function avoids asking for upward adjacencies to prevent their
00269 * creation.
00270 *\param tri The intersected triangle
00271 *\param ray_dir The direction of the ray
00272 *\param int_type The type of intersection (EDGE0, EDGE1, NODE2, ...)
00273 *\param close_tris Vector of triangles in the proximity of the intersection
00274 *\param close_senses Vector of surface senses for tris in the proximity of
00275 * the intersection
00276 *\param neighborhood Vector of triangles in the topological neighborhood of the intersection
00277 *\return True if piercing, false otherwise.
00278 */
00279 bool GQT_IntRegCtxt::edge_node_piercing_intersect( const EntityHandle tri,
00280 const CartVect& ray_dir,
00281 const GeomUtil::intersection_type int_type,
00282 const std::vector< EntityHandle >& close_tris,
00283 const std::vector< int >& close_senses,
00284 const Interface* MBI,
00285 std::vector< EntityHandle >* neighborhood_tris )
00286 {
00287
00288 // get the node of the triangle
00289 const EntityHandle* conn = NULL;
00290 int len = 0;
00291 ErrorCode rval = MBI->get_connectivity( tri, conn, len );MB_CHK_ERR_RET_VAL( rval, false );
00292 // NOTE: original code is next line, but return type was wrong; returning true to get same net effect
00293 if( 3 != len ) return false;
00294
00295 // get adjacent tris (and keep their corresponding senses)
00296 std::vector< EntityHandle > adj_tris;
00297 std::vector< int > adj_senses;
00298
00299 // node intersection
00300 if( GeomUtil::NODE0 == int_type || GeomUtil::NODE1 == int_type || GeomUtil::NODE2 == int_type )
00301 {
00302
00303 // get the intersected node
00304 EntityHandle node;
00305 if( GeomUtil::NODE0 == int_type )
00306 node = conn[0];
00307 else if( GeomUtil::NODE1 == int_type )
00308 node = conn[1];
00309 else
00310 node = conn[2];
00311
00312 // get tris adjacent to node
00313 for( unsigned i = 0; i < close_tris.size(); ++i )
00314 {
00315 const EntityHandle* con = NULL;
00316 rval = MBI->get_connectivity( close_tris[i], con, len );MB_CHK_ERR_RET_VAL( rval, false );
00317 if( 3 != len ) return false;
00318
00319 if( node == con[0] || node == con[1] || node == con[2] )
00320 {
00321 adj_tris.push_back( close_tris[i] );
00322 adj_senses.push_back( close_senses[i] );
00323 }
00324 }
00325 if( adj_tris.empty() )
00326 {
00327 std::cerr << "error: no tris are adjacent to the node" << std::endl;
00328 return false;
00329 }
00330 // edge intersection
00331 }
00332 else if( GeomUtil::EDGE0 == int_type || GeomUtil::EDGE1 == int_type || GeomUtil::EDGE2 == int_type )
00333 {
00334
00335 // get the endpoints of the edge
00336 EntityHandle endpts[2];
00337 if( GeomUtil::EDGE0 == int_type )
00338 {
00339 endpts[0] = conn[0];
00340 endpts[1] = conn[1];
00341 }
00342 else if( GeomUtil::EDGE1 == int_type )
00343 {
00344 endpts[0] = conn[1];
00345 endpts[1] = conn[2];
00346 }
00347 else
00348 {
00349 endpts[0] = conn[2];
00350 endpts[1] = conn[0];
00351 }
00352
00353 // get tris adjacent to edge
00354 for( unsigned i = 0; i < close_tris.size(); ++i )
00355 {
00356 const EntityHandle* con = NULL;
00357 rval = MBI->get_connectivity( close_tris[i], con, len );MB_CHK_ERR_RET_VAL( rval, false );
00358 if( 3 != len ) return false;
00359
00360 // check both orientations in case close_tris are not on the same surface
00361 if( ( endpts[0] == con[0] && endpts[1] == con[1] ) || ( endpts[0] == con[1] && endpts[1] == con[0] ) ||
00362 ( endpts[0] == con[1] && endpts[1] == con[2] ) || ( endpts[0] == con[2] && endpts[1] == con[1] ) ||
00363 ( endpts[0] == con[2] && endpts[1] == con[0] ) || ( endpts[0] == con[0] && endpts[1] == con[2] ) )
00364 {
00365 adj_tris.push_back( close_tris[i] );
00366 adj_senses.push_back( close_senses[i] );
00367 }
00368 }
00369 // In a 2-manifold each edge is adjacent to exactly 2 tris
00370 if( 2 != adj_tris.size() )
00371 {
00372 std::cerr << "error: edge of a manifold must be topologically adjacent to exactly 2 tris" << std::endl;
00373 MBI->list_entities( endpts, 2 );
00374 return true;
00375 }
00376 }
00377 else
00378 {
00379 std::cerr << "error: special case not an node/edge intersection" << std::endl;
00380 return false;
00381 }
00382
00383 // The close tris were in proximity to the intersection. The adj_tris are
00384 // topologically adjacent to the intersection (the neighborhood).
00385 if( neighborhood_tris ) ( *neighborhood_tris ).assign( adj_tris.begin(), adj_tris.end() );
00386
00387 // determine glancing/piercing
00388 // If a desired_orientation was used in this call to ray_intersect_sets,
00389 // the plucker_ray_tri_intersect will have already used it. For a piercing
00390 // intersection, the normal of all tris must have the same orientation.
00391 int sign = 0;
00392 for( unsigned i = 0; i < adj_tris.size(); ++i )
00393 {
00394 const EntityHandle* con = NULL;
00395 rval = MBI->get_connectivity( adj_tris[i], con, len );MB_CHK_ERR_RET_VAL( rval, false );
00396 if( 3 != len ) return false;
00397
00398 CartVect coords[3];
00399 rval = MBI->get_coords( con, len, coords[0].array() );MB_CHK_ERR_RET_VAL( rval, false );
00400
00401 // get normal of triangle
00402 CartVect v0 = coords[1] - coords[0];
00403 CartVect v1 = coords[2] - coords[0];
00404 CartVect norm = adj_senses[i] * ( v0 * v1 );
00405 double dot_prod = norm % ray_dir;
00406
00407 // if the sign has not yet been decided, choose it
00408 if( 0 == sign && 0 != dot_prod )
00409 {
00410 if( 0 < dot_prod )
00411 sign = 1;
00412 else
00413 sign = -1;
00414 }
00415
00416 // intersection is glancing if tri and ray do not point in same direction
00417 // for every triangle
00418 if( 0 != sign && 0 > sign * dot_prod ) return false;
00419 }
00420 return true;
00421 }
00422
00423 ErrorCode GQT_IntRegCtxt::register_intersection( EntityHandle set,
00424 EntityHandle t,
00425 double int_dist,
00426 OrientedBoxTreeTool::IntersectSearchWindow& search_win,
00427 GeomUtil::intersection_type int_type )
00428 {
00429 ErrorCode rval;
00430
00431 // Do not accept intersections if they are in the vector of previously intersected
00432 // facets.
00433 if( in_prevFacets( t ) ) return MB_SUCCESS;
00434
00435 // Do not accept intersections if they are in the neighborhood of previous
00436 // intersections.
00437 if( in_neighborhoods( t ) ) return MB_SUCCESS;
00438
00439 neighborhood.clear();
00440
00441 // Handle special case of edge/node intersection. Accept piercing
00442 // intersections and reject glancing intersections.
00443 // The edge_node_intersection function needs to know surface sense wrt volume.
00444 // A less-robust implementation could work without sense information.
00445 // Would it ever be useful to accept a glancing intersection?
00446 if( GeomUtil::INTERIOR != int_type && rootSet && geomVol && senseTag )
00447 {
00448 // get triangles in the proximity of the intersection
00449 std::vector< EntityHandle > close_tris;
00450 std::vector< int > close_senses;
00451 rval = tool->get_close_tris( ray_origin + int_dist * ray_direction, tol, rootSet, geomVol, senseTag, close_tris,
00452 close_senses );
00453
00454 if( MB_SUCCESS != rval ) return rval;
00455
00456 if( !edge_node_piercing_intersect( t, ray_direction, int_type, close_tris, close_senses,
00457 tool->get_moab_instance(), &neighborhood ) )
00458 return MB_SUCCESS;
00459 }
00460 else
00461 {
00462 neighborhood.push_back( t );
00463 }
00464
00465 // NOTE: add_intersection may modify the 'neg_ray_len' and 'nonneg_ray_len'
00466 // members, which will affect subsequent calls to ray_tri_intersect
00467 // in this loop.
00468 add_intersection( set, t, int_dist, search_win );
00469
00470 return MB_SUCCESS;
00471 }
00472
00473 void GQT_IntRegCtxt::append_intersection( EntityHandle set, EntityHandle facet, double dist )
00474 {
00475 intersections.push_back( dist );
00476 sets.push_back( set );
00477 facets.push_back( facet );
00478 neighborhoods.push_back( neighborhood );
00479 return;
00480 }
00481
00482 void GQT_IntRegCtxt::set_intersection( int len_idx, EntityHandle set, EntityHandle facet, double dist )
00483 {
00484 intersections[len_idx] = dist;
00485 sets[len_idx] = set;
00486 facets[len_idx] = facet;
00487 return;
00488 }
00489
00490 /* Mode 1: Used if neg_ray_len and nonneg_ray_len are specified
00491 variables used: nonneg_ray_len, neg_ray_len
00492 variables not used: min_tol_int, tol
00493 1) keep the closest nonneg intersection and one negative intersection, if closer
00494 */
00495 void GQT_IntRegCtxt::add_mode1_intersection( EntityHandle set,
00496 EntityHandle facet,
00497 double dist,
00498 OrientedBoxTreeTool::IntersectSearchWindow& search_win )
00499 {
00500 if( 2 != intersections.size() )
00501 {
00502 intersections.resize( 2, 0 );
00503 sets.resize( 2, 0 );
00504 facets.resize( 2, 0 );
00505 // must initialize this for comparison below
00506 intersections[0] = -std::numeric_limits< double >::max();
00507 }
00508
00509 // negative case
00510 if( 0.0 > dist )
00511 {
00512 set_intersection( 0, set, facet, dist );
00513 search_win.second = &intersections[0];
00514 // nonnegative case
00515 }
00516 else
00517 {
00518 set_intersection( 1, set, facet, dist );
00519 search_win.first = &intersections[1];
00520 // if the intersection is closer than the negative one, remove the negative one
00521 if( dist < -*( search_win.second ) )
00522 {
00523 set_intersection( 0, 0, 0, -intersections[1] );
00524 search_win.second = &intersections[0];
00525 }
00526 }
00527 // std::cout << "add_intersection: dist = " << dist << " search_win.second=" <<
00528 // *search_win.second
00529 // << " search_win.first=" << *search_win.first << std::endl;
00530 return;
00531 }
00532
00533 void GQT_IntRegCtxt::add_intersection( EntityHandle set,
00534 EntityHandle facet,
00535 double dist,
00536 OrientedBoxTreeTool::IntersectSearchWindow& search_win )
00537 {
00538
00539 // Mode 1, detected by non-null neg_ray_len pointer
00540 // keep the closest nonneg intersection and one negative intersection, if closer
00541 if( search_win.second && search_win.first )
00542 {
00543 return add_mode1_intersection( set, facet, dist, search_win );
00544 }
00545
00546 // ---------------------------------------------------------------------------
00547 /* Mode 2: Used if neg_ray_len is not specified
00548 variables used: min_tol_int, tol, search_win.first
00549 variables not used: neg_ray_len
00550 1) if(min_tol_int<0) return all intersections
00551 2) otherwise return all inside tolerance and unless there are >min_tol_int
00552 inside of tolerance, return the closest outside of tolerance */
00553 // Mode 2
00554 // If minTolInt is less than zero, return all intersections
00555 if( minTolInt < 0 && dist > -tol )
00556 {
00557 append_intersection( set, facet, dist );
00558 neighborhoods.push_back( neighborhood );
00559 return;
00560 }
00561
00562 // Check if the 'len' pointer is pointing into the intersection
00563 // list. If this is the case, then the list contains, at that
00564 // location, an intersection greater than the tolerance away from
00565 // the base point of the ray.
00566 int len_idx = -1;
00567 if( search_win.first && search_win.first >= &intersections[0] &&
00568 search_win.first < &intersections[0] + intersections.size() )
00569 len_idx = search_win.first - &intersections[0];
00570
00571 // If the intersection is within tol of the ray base point, we
00572 // always add it to the list.
00573 if( dist <= tol )
00574 {
00575 // If the list contains an intersection outside the tolerance...
00576 if( len_idx >= 0 )
00577 {
00578 // If we no longer want an intersection outside the tolerance,
00579 // remove it.
00580 if( (int)intersections.size() >= minTolInt )
00581 {
00582 set_intersection( len_idx, set, facet, dist );
00583 // From now on, we want only intersections within the tolerance,
00584 // so update length accordingly
00585 search_win.first = &tol;
00586 }
00587 // Otherwise appended to the list and update pointer
00588 else
00589 {
00590 append_intersection( set, facet, dist );
00591 search_win.first = &intersections[len_idx];
00592 }
00593 }
00594 // Otherwise just append it
00595 else
00596 {
00597 append_intersection( set, facet, dist );
00598 // If we have all the intersections we want, set
00599 // length such that we will only find further intersections
00600 // within the tolerance
00601 if( (int)intersections.size() >= minTolInt ) search_win.first = &tol;
00602 }
00603 }
00604 // Otherwise the intersection is outside the tolerance
00605 // If we already have an intersection outside the tolerance and
00606 // this one is closer, replace the existing one with this one.
00607 else if( len_idx >= 0 )
00608 {
00609 if( dist <= *search_win.first )
00610 {
00611 set_intersection( len_idx, set, facet, dist );
00612 }
00613 }
00614 // Otherwise if we want an intersection outside the tolerance
00615 // and don'thave one yet, add it.
00616 else if( (int)intersections.size() < minTolInt )
00617 {
00618 append_intersection( set, facet, dist );
00619 // update length. this is currently the closest intersection, so
00620 // only want further intersections that are closer than this one.
00621 search_win.first = &intersections.back();
00622 }
00623 }
00624
00625 GeomQueryTool::GeomQueryTool( Interface* impl,
00626 bool find_geomsets,
00627 EntityHandle modelRootSet,
00628 bool p_rootSets_vector,
00629 bool restore_rootSets,
00630 bool trace_counting,
00631 double overlap_thickness,
00632 double numerical_precision )
00633 : owns_gtt( true )
00634 {
00635 geomTopoTool = new GeomTopoTool( impl, find_geomsets, modelRootSet, p_rootSets_vector, restore_rootSets );
00636
00637 senseTag = geomTopoTool->get_sense_tag();
00638
00639 obbTreeTool = geomTopoTool->obb_tree();
00640 MBI = geomTopoTool->get_moab_instance();
00641
00642 counting = trace_counting;
00643 overlapThickness = overlap_thickness;
00644 numericalPrecision = numerical_precision;
00645
00646 // reset query counters
00647 n_pt_in_vol_calls = 0;
00648 n_ray_fire_calls = 0;
00649 }
00650
00651 GeomQueryTool::GeomQueryTool( GeomTopoTool* geomtopotool,
00652 bool trace_counting,
00653 double overlap_thickness,
00654 double numerical_precision )
00655 : owns_gtt( false )
00656 {
00657
00658 geomTopoTool = geomtopotool;
00659
00660 senseTag = geomTopoTool->get_sense_tag();
00661
00662 obbTreeTool = geomTopoTool->obb_tree();
00663 MBI = geomTopoTool->get_moab_instance();
00664
00665 counting = trace_counting;
00666 overlapThickness = overlap_thickness;
00667 numericalPrecision = numerical_precision;
00668
00669 // reset query counters
00670 n_pt_in_vol_calls = 0;
00671 n_ray_fire_calls = 0;
00672 }
00673
00674 GeomQueryTool::~GeomQueryTool()
00675 {
00676 if( owns_gtt )
00677 {
00678 delete geomTopoTool;
00679 }
00680 }
00681
00682 ErrorCode GeomQueryTool::initialize()
00683 {
00684
00685 ErrorCode rval;
00686
00687 rval = geomTopoTool->find_geomsets();MB_CHK_SET_ERR( rval, "Failed to find geometry sets" );
00688
00689 rval = geomTopoTool->setup_implicit_complement();MB_CHK_SET_ERR( rval, "Couldn't setup the implicit complement" );
00690
00691 rval = geomTopoTool->construct_obb_trees();MB_CHK_SET_ERR( rval, "Failed to construct OBB trees" );
00692
00693 return MB_SUCCESS;
00694 }
00695
00696 void GeomQueryTool::RayHistory::reset()
00697 {
00698 prev_facets.clear();
00699 }
00700
00701 void GeomQueryTool::RayHistory::reset_to_last_intersection()
00702 {
00703
00704 if( prev_facets.size() > 1 )
00705 {
00706 prev_facets[0] = prev_facets.back();
00707 prev_facets.resize( 1 );
00708 }
00709 }
00710
00711 void GeomQueryTool::RayHistory::rollback_last_intersection()
00712 {
00713 if( prev_facets.size() ) prev_facets.pop_back();
00714 }
00715
00716 ErrorCode GeomQueryTool::RayHistory::get_last_intersection( EntityHandle& last_facet_hit ) const
00717 {
00718 if( prev_facets.size() > 0 )
00719 {
00720 last_facet_hit = prev_facets.back();
00721 return MB_SUCCESS;
00722 }
00723 else
00724 {
00725 return MB_ENTITY_NOT_FOUND;
00726 }
00727 }
00728
00729 bool GeomQueryTool::RayHistory::in_history( EntityHandle ent ) const
00730 {
00731 return std::find( prev_facets.begin(), prev_facets.end(), ent ) != prev_facets.end();
00732 }
00733
00734 void GeomQueryTool::RayHistory::add_entity( EntityHandle ent )
00735 {
00736 prev_facets.push_back( ent );
00737 }
00738
00739 ErrorCode GeomQueryTool::ray_fire( const EntityHandle volume,
00740 const double point[3],
00741 const double dir[3],
00742 EntityHandle& next_surf,
00743 double& next_surf_dist,
00744 RayHistory* history,
00745 double user_dist_limit,
00746 int ray_orientation,
00747 OrientedBoxTreeTool::TrvStats* stats )
00748 {
00749
00750 // take some stats that are independent of nps
00751 if( counting )
00752 {
00753 ++n_ray_fire_calls;
00754 if( 0 == n_ray_fire_calls % 10000000 )
00755 {
00756 std::cout << "n_ray_fires=" << n_ray_fire_calls << " n_pt_in_vols=" << n_pt_in_vol_calls << std::endl;
00757 }
00758 }
00759
00760 if( debug )
00761 {
00762 std::cout << "ray_fire:"
00763 << " xyz=" << point[0] << " " << point[1] << " " << point[2] << " uvw=" << dir[0] << " " << dir[1]
00764 << " " << dir[2] << " entity_handle=" << volume << std::endl;
00765 }
00766
00767 const double huge_val = std::numeric_limits< double >::max();
00768 double dist_limit = huge_val;
00769 if( user_dist_limit > 0 ) dist_limit = user_dist_limit;
00770
00771 // don't recreate these every call
00772 std::vector< double > dists;
00773 std::vector< EntityHandle > surfs;
00774 std::vector< EntityHandle > facets;
00775
00776 EntityHandle root;
00777 ErrorCode rval = geomTopoTool->get_root( volume, root );MB_CHK_SET_ERR( rval, "Failed to get the obb tree root of the volume" );
00778
00779 // check behind the ray origin for intersections
00780 double neg_ray_len;
00781 if( 0 == overlapThickness )
00782 {
00783 neg_ray_len = -numericalPrecision;
00784 }
00785 else
00786 {
00787 neg_ray_len = -overlapThickness;
00788 }
00789
00790 // optionally, limit the nonneg_ray_len with the distance to next collision.
00791 double nonneg_ray_len = dist_limit;
00792
00793 // the nonneg_ray_len should not be less than -neg_ray_len, or an overlap
00794 // may be missed due to optimization within ray_intersect_sets
00795 if( nonneg_ray_len < -neg_ray_len ) nonneg_ray_len = -neg_ray_len;
00796 if( 0 > nonneg_ray_len || 0 <= neg_ray_len )
00797 {
00798 MB_SET_ERR( MB_FAILURE, "Incorrect ray length provided" );
00799 }
00800
00801 // min_tolerance_intersections is passed but not used in this call
00802 const int min_tolerance_intersections = 0;
00803
00804 // numericalPrecision is used for box.intersect_ray and find triangles in the
00805 // neighborhood of edge/node intersections.
00806 GQT_IntRegCtxt int_reg_ctxt( geomTopoTool->obb_tree(), point, dir, numericalPrecision, min_tolerance_intersections,
00807 &root, &volume, &senseTag, &ray_orientation,
00808 history ? &( history->prev_facets ) : NULL );
00809
00810 OrientedBoxTreeTool::IntersectSearchWindow search_win( &nonneg_ray_len, &neg_ray_len );
00811 rval = geomTopoTool->obb_tree()->ray_intersect_sets( dists, surfs, facets, root, numericalPrecision, point, dir,
00812 search_win, int_reg_ctxt, stats );
00813
00814 MB_CHK_SET_ERR( rval, "Ray query failed" );
00815
00816 // If no distances are returned, the particle is lost unless the physics limit
00817 // is being used. If the physics limit is being used, there is no way to tell
00818 // if the particle is lost. To avoid ambiguity, DO NOT use the distance limit
00819 // unless you know lost particles do not occur.
00820 if( dists.empty() )
00821 {
00822 next_surf = 0;
00823 if( debug )
00824 {
00825 std::cout << " next_surf=0 dist=(undef)" << std::endl;
00826 }
00827 return MB_SUCCESS;
00828 }
00829
00830 // Assume that a (neg, nonneg) pair of RTIs could be returned,
00831 // however, only one or the other may exist. dists[] may be populated, but
00832 // intersections are ONLY indicated by nonzero surfs[] and facets[].
00833 if( 2 != dists.size() || 2 != facets.size() )
00834 {
00835 MB_SET_ERR( MB_FAILURE, "Incorrect number of facets/distances" );
00836 }
00837 if( 0.0 < dists[0] || 0.0 > dists[1] )
00838 {
00839 MB_SET_ERR( MB_FAILURE, "Invalid intersection distance signs" );
00840 }
00841
00842 // If both negative and nonnegative RTIs are returned, the negative RTI must
00843 // closer to the origin.
00844 if( ( 0 != facets[0] && 0 != facets[1] ) && ( -dists[0] > dists[1] ) )
00845 {
00846 MB_SET_ERR( MB_FAILURE, "Invalid intersection distance values" );
00847 }
00848
00849 // If an RTI is found at negative distance, perform a PMT to see if the
00850 // particle is inside an overlap.
00851 int exit_idx = -1;
00852 if( 0 != facets[0] )
00853 {
00854 // get the next volume
00855 std::vector< EntityHandle > vols;
00856 EntityHandle nx_vol;
00857 rval = MBI->get_parent_meshsets( surfs[0], vols );MB_CHK_SET_ERR( rval, "Failed to get the parent meshsets" );
00858 if( 2 != vols.size() )
00859 {
00860 MB_SET_ERR( MB_FAILURE, "Invaid number of parent volumes found" );
00861 }
00862 if( vols.front() == volume )
00863 {
00864 nx_vol = vols.back();
00865 }
00866 else
00867 {
00868 nx_vol = vols.front();
00869 }
00870 // Check to see if the point is actually in the next volume.
00871 // The list of previous facets is used to topologically identify the
00872 // "on_boundary" result of the PMT. This avoids a test that uses proximity
00873 // (a tolerance).
00874 int result;
00875 rval = point_in_volume( nx_vol, point, result, dir, history );MB_CHK_SET_ERR( rval, "Point in volume query failed" );
00876 if( 1 == result ) exit_idx = 0;
00877 }
00878
00879 // if the negative distance is not the exit, try the nonnegative distance
00880 if( -1 == exit_idx && 0 != facets[1] ) exit_idx = 1;
00881
00882 // if the exit index is still unknown, the particle is lost
00883 if( -1 == exit_idx )
00884 {
00885 next_surf = 0;
00886 if( debug )
00887 {
00888 std::cout << "next surf hit = 0, dist = (undef)" << std::endl;
00889 }
00890 return MB_SUCCESS;
00891 }
00892
00893 // return the intersection
00894 next_surf = surfs[exit_idx];
00895 next_surf_dist = ( 0 > dists[exit_idx] ? 0 : dists[exit_idx] );
00896
00897 if( history )
00898 {
00899 history->prev_facets.push_back( facets[exit_idx] );
00900 }
00901
00902 if( debug )
00903 {
00904 if( 0 > dists[exit_idx] )
00905 {
00906 std::cout << " OVERLAP track length=" << dists[exit_idx] << std::endl;
00907 }
00908 std::cout << " next_surf = " << next_surf // todo: use geomtopotool to get id by entity handle
00909 << ", dist = " << next_surf_dist << " new_pt=";
00910 for( int i = 0; i < 3; ++i )
00911 {
00912 std::cout << point[i] + dir[i] * next_surf_dist << " ";
00913 }
00914 std::cout << std::endl;
00915 }
00916
00917 return MB_SUCCESS;
00918 }
00919
00920 ErrorCode GeomQueryTool::point_in_volume( const EntityHandle volume,
00921 const double xyz[3],
00922 int& result,
00923 const double* uvw,
00924 const RayHistory* history )
00925 {
00926 // take some stats that are independent of nps
00927 if( counting ) ++n_pt_in_vol_calls;
00928
00929 // early fail for piv - see if point inside the root level obb
00930 // if its not even in the box dont bother doing anything else
00931 ErrorCode rval = point_in_box( volume, xyz, result );
00932 if( !result )
00933 {
00934 result = 0;
00935 return MB_SUCCESS;
00936 }
00937
00938 // get OBB Tree for volume
00939 EntityHandle root;
00940 rval = geomTopoTool->get_root( volume, root );MB_CHK_SET_ERR( rval, "Failed to find the volume's obb tree root" );
00941
00942 // Don't recreate these every call. These cannot be the same as the ray_fire
00943 // vectors because both are used simultaneously.
00944 std::vector< double > dists;
00945 std::vector< EntityHandle > surfs;
00946 std::vector< EntityHandle > facets;
00947 std::vector< int > dirs;
00948
00949 // if uvw is not given or is full of zeros, use a random direction
00950 double u = 0, v = 0, w = 0;
00951
00952 if( uvw )
00953 {
00954 u = uvw[0];
00955 v = uvw[1], w = uvw[2];
00956 }
00957
00958 if( u == 0 && v == 0 && w == 0 )
00959 {
00960 u = rand();
00961 v = rand();
00962 w = rand();
00963 const double magnitude = sqrt( u * u + v * v + w * w );
00964 u /= magnitude;
00965 v /= magnitude;
00966 w /= magnitude;
00967 }
00968
00969 const double ray_direction[] = { u, v, w };
00970
00971 // if overlaps, ray must be cast to infinity and all RTIs must be returned
00972 const double large = 1e15;
00973 double ray_length = large;
00974
00975 // If overlaps occur, the pt is inside if traveling along the ray from the
00976 // origin, there are ever more exits than entrances. In lieu of implementing
00977 // that, all intersections to infinity are required if overlaps occur (expensive)
00978 int min_tolerance_intersections;
00979 if( 0 != overlapThickness )
00980 {
00981 min_tolerance_intersections = -1;
00982 // only the first intersection is needed if overlaps do not occur (cheap)
00983 }
00984 else
00985 {
00986 min_tolerance_intersections = 1;
00987 }
00988
00989 // Get intersection(s) of forward and reverse orientation. Do not return
00990 // glancing intersections or previous facets.
00991 GQT_IntRegCtxt int_reg_ctxt( geomTopoTool->obb_tree(), xyz, ray_direction, numericalPrecision,
00992 min_tolerance_intersections, &root, &volume, &senseTag, NULL,
00993 history ? &( history->prev_facets ) : NULL );
00994
00995 OrientedBoxTreeTool::IntersectSearchWindow search_win( &ray_length, (double*)NULL );
00996 rval = geomTopoTool->obb_tree()->ray_intersect_sets( dists, surfs, facets, root, numericalPrecision, xyz,
00997 ray_direction, search_win, int_reg_ctxt );MB_CHK_SET_ERR( rval, "Ray fire query failed" );
00998
00999 // determine orientation of all intersections
01000 // 1 for entering, 0 for leaving, -1 for tangent
01001 // Tangent intersections are not returned from ray_tri_intersect.
01002 dirs.resize( dists.size() );
01003 for( unsigned i = 0; i < dists.size(); ++i )
01004 {
01005 rval = boundary_case( volume, dirs[i], u, v, w, facets[i], surfs[i] );MB_CHK_SET_ERR( rval, "Failed to resolve boundary case" );
01006 }
01007
01008 // count all crossings
01009 if( 0 != overlapThickness )
01010 {
01011 int sum = 0;
01012 for( unsigned i = 0; i < dirs.size(); ++i )
01013 {
01014 if( 1 == dirs[i] )
01015 sum += 1; // +1 for entering
01016 else if( 0 == dirs[i] )
01017 sum -= 1; // -1 for leaving
01018 else if( -1 == dirs[i] )
01019 { // 0 for tangent
01020 std::cout << "direction==tangent" << std::endl;
01021 sum += 0;
01022 }
01023 else
01024 {
01025 MB_SET_ERR( MB_FAILURE, "Error: unknown direction" );
01026 }
01027 }
01028
01029 // inside/outside depends on the sum
01030 if( 0 < sum )
01031 result = 0; // pt is outside (for all vols)
01032 else if( 0 > sum )
01033 result = 1; // pt is inside (for all vols)
01034 else if( geomTopoTool->is_implicit_complement( volume ) )
01035 result = 1; // pt is inside (for impl_compl_vol)
01036 else
01037 result = 0; // pt is outside (for all other vols)
01038
01039 // Only use the first crossing
01040 }
01041 else
01042 {
01043 if( dirs.empty() )
01044 {
01045 result = 0; // pt is outside
01046 }
01047 else
01048 {
01049 int smallest = std::min_element( dists.begin(), dists.end() ) - dists.begin();
01050 if( 1 == dirs[smallest] )
01051 result = 0; // pt is outside
01052 else if( 0 == dirs[smallest] )
01053 result = 1; // pt is inside
01054 else if( -1 == dirs[smallest] )
01055 {
01056 // Should not be here because Plucker ray-triangle test does not
01057 // return coplanar rays as intersections.
01058 std::cout << "direction==tangent" << std::endl;
01059 result = -1;
01060 }
01061 else
01062 {
01063 MB_SET_ERR( MB_FAILURE, "Error: unknown direction" );
01064 }
01065 }
01066 }
01067
01068 if( debug )
01069 std::cout << "pt_in_vol: result=" << result << " xyz=" << xyz[0] << " " << xyz[1] << " " << xyz[2]
01070 << " uvw=" << u << " " << v << " " << w << " vol_id=" << volume
01071 << std::endl; // todo: use geomtopotool to get id by entity handle
01072
01073 return MB_SUCCESS;
01074 }
01075
01076 /**
01077 * \brief For the volume pointed to and the point wished to be tested, returns
01078 * whether the point is inside or outside the bounding box of the volume.
01079 * inside = 0, not inside, inside = 1, inside
01080 */
01081 ErrorCode GeomQueryTool::point_in_box( EntityHandle volume, const double point[3], int& inside )
01082 {
01083 double minpt[3];
01084 double maxpt[3];
01085 ErrorCode rval = geomTopoTool->get_bounding_coords( volume, minpt, maxpt );MB_CHK_SET_ERR( rval, "Failed to get the bounding coordinates of the volume" );
01086
01087 // early exits
01088 if( point[0] > maxpt[0] || point[0] < minpt[0] )
01089 {
01090 inside = 0;
01091 return rval;
01092 }
01093 if( point[1] > maxpt[1] || point[1] < minpt[1] )
01094 {
01095 inside = 0;
01096 return rval;
01097 }
01098 if( point[2] > maxpt[2] || point[2] < minpt[2] )
01099 {
01100 inside = 0;
01101 return rval;
01102 }
01103 inside = 1;
01104 return rval;
01105 }
01106
01107 ErrorCode GeomQueryTool::test_volume_boundary( const EntityHandle volume,
01108 const EntityHandle surface,
01109 const double xyz[3],
01110 const double uvw[3],
01111 int& result,
01112 const RayHistory* history )
01113 {
01114 ErrorCode rval;
01115 int dir;
01116
01117 if( history && history->prev_facets.size() )
01118 {
01119 // the current facet is already available
01120 rval = boundary_case( volume, dir, uvw[0], uvw[1], uvw[2], history->prev_facets.back(), surface );MB_CHK_SET_ERR( rval, "Failed to resolve the boundary case" );
01121 }
01122 else
01123 {
01124 // look up nearest facet
01125
01126 // Get OBB Tree for surface
01127 EntityHandle root;
01128 rval = geomTopoTool->get_root( volume, root );MB_CHK_SET_ERR( rval, "Failed to get the volume's OBB tree root" );
01129
01130 // Get closest triangle on surface
01131 const CartVect point( xyz );
01132 CartVect nearest;
01133 EntityHandle facet_out;
01134 rval = geomTopoTool->obb_tree()->closest_to_location( point.array(), root, nearest.array(), facet_out );MB_CHK_SET_ERR( rval, "Failed to find the closest point to location" );
01135
01136 rval = boundary_case( volume, dir, uvw[0], uvw[1], uvw[2], facet_out, surface );MB_CHK_SET_ERR( rval, "Failed to resolve the boundary case" );
01137 }
01138
01139 result = dir;
01140
01141 return MB_SUCCESS;
01142 }
01143
01144 // use spherical area test to determine inside/outside of a polyhedron.
01145 ErrorCode GeomQueryTool::point_in_volume_slow( EntityHandle volume, const double xyz[3], int& result )
01146 {
01147 ErrorCode rval;
01148 Range faces;
01149 std::vector< EntityHandle > surfs;
01150 std::vector< int > senses;
01151 double sum = 0.0;
01152 const CartVect point( xyz );
01153
01154 rval = MBI->get_child_meshsets( volume, surfs );MB_CHK_SET_ERR( rval, "Failed to get the volume's child surfaces" );
01155
01156 senses.resize( surfs.size() );
01157 rval = geomTopoTool->get_surface_senses( volume, surfs.size(), &surfs[0], &senses[0] );MB_CHK_SET_ERR( rval, "Failed to get the volume's surface senses" );
01158
01159 for( unsigned i = 0; i < surfs.size(); ++i )
01160 {
01161 if( !senses[i] ) // skip non-manifold surfaces
01162 continue;
01163
01164 double surf_area = 0.0, face_area;
01165 faces.clear();
01166 rval = MBI->get_entities_by_dimension( surfs[i], 2, faces );MB_CHK_SET_ERR( rval, "Failed to get the surface entities by dimension" );
01167
01168 for( Range::iterator j = faces.begin(); j != faces.end(); ++j )
01169 {
01170 rval = poly_solid_angle( *j, point, face_area );MB_CHK_SET_ERR( rval, "Failed to determin the polygon's solid angle" );
01171
01172 surf_area += face_area;
01173 }
01174
01175 sum += senses[i] * surf_area;
01176 }
01177
01178 result = fabs( sum ) > 2.0 * M_PI;
01179 return MB_SUCCESS;
01180 }
01181
01182 ErrorCode GeomQueryTool::find_volume( const double xyz[3], EntityHandle& volume, const double* dir )
01183 {
01184 ErrorCode rval;
01185 volume = 0;
01186
01187 EntityHandle global_surf_tree_root = geomTopoTool->get_one_vol_root();
01188
01189 // fast check - make sure point is in the implicit complement bounding box
01190 int ic_result;
01191 EntityHandle ic_handle;
01192 rval = geomTopoTool->get_implicit_complement( ic_handle );MB_CHK_SET_ERR( rval, "Failed to get the implicit complement handle" );
01193
01194 rval = point_in_box( ic_handle, xyz, ic_result );MB_CHK_SET_ERR( rval, "Failed to check implicit complement for containment" );
01195 if( ic_result == 0 )
01196 {
01197 volume = 0;
01198 return MB_ENTITY_NOT_FOUND;
01199 }
01200
01201 // if geomTopoTool doesn't have a global tree, use a loop over vols (slow)
01202 if( !global_surf_tree_root )
01203 {
01204 rval = find_volume_slow( xyz, volume, dir );
01205 return rval;
01206 }
01207
01208 moab::CartVect uvw( 0.0 );
01209
01210 if( dir )
01211 {
01212 uvw[0] = dir[0];
01213 uvw[1] = dir[1];
01214 uvw[2] = dir[2];
01215 }
01216
01217 if( uvw == 0.0 )
01218 {
01219 uvw[0] = rand();
01220 uvw[1] = rand();
01221 uvw[2] = rand();
01222 }
01223
01224 // always normalize direction
01225 uvw.normalize();
01226
01227 // fire a ray along dir and get surface
01228 const double huge_val = std::numeric_limits< double >::max();
01229 double pos_ray_len = huge_val;
01230 double neg_ray_len = -huge_val;
01231
01232 // RIS output data
01233 std::vector< double > dists;
01234 std::vector< EntityHandle > surfs;
01235 std::vector< EntityHandle > facets;
01236
01237 FindVolumeIntRegCtxt find_vol_reg_ctxt;
01238 OrientedBoxTreeTool::IntersectSearchWindow search_win( &pos_ray_len, &neg_ray_len );
01239 rval =
01240 geomTopoTool->obb_tree()->ray_intersect_sets( dists, surfs, facets, global_surf_tree_root, numericalPrecision,
01241 xyz, uvw.array(), search_win, find_vol_reg_ctxt );MB_CHK_SET_ERR( rval, "Failed in global tree ray fire" );
01242
01243 // if there was no intersection, no volume is found
01244 if( surfs.size() == 0 || surfs[0] == 0 )
01245 {
01246 volume = 0;
01247 return MB_ENTITY_NOT_FOUND;
01248 }
01249
01250 // get the positive distance facet, surface hit
01251 EntityHandle facet = facets[0];
01252 EntityHandle surf = surfs[0];
01253
01254 // get these now, we're going to use them no matter what
01255 EntityHandle fwd_vol, bwd_vol;
01256 rval = geomTopoTool->get_surface_senses( surf, fwd_vol, bwd_vol );MB_CHK_SET_ERR( rval, "Failed to get sense data" );
01257 EntityHandle parent_vols[2];
01258 parent_vols[0] = fwd_vol;
01259 parent_vols[1] = bwd_vol;
01260
01261 // get triangle normal
01262 std::vector< EntityHandle > conn;
01263 CartVect coords[3];
01264 rval = MBI->get_connectivity( &facet, 1, conn );MB_CHK_SET_ERR( rval, "Failed to get triangle connectivity" );
01265
01266 rval = MBI->get_coords( &conn[0], 3, coords[0].array() );MB_CHK_SET_ERR( rval, "Failed to get triangle coordinates" );
01267
01268 CartVect normal = ( coords[1] - coords[0] ) * ( coords[2] - coords[0] );
01269 normal.normalize();
01270
01271 // reverse direction if a hit in the negative direction is found
01272 if( dists[0] < 0 )
01273 {
01274 uvw *= -1;
01275 }
01276
01277 // if this is a "forward" intersection return the first sense entity
01278 // otherwise return the second, "reverse" sense entity
01279 double dot_prod = uvw % normal;
01280 int idx = dot_prod > 0.0 ? 0 : 1;
01281
01282 if( dot_prod == 0.0 )
01283 {
01284 std::cerr << "Tangent dot product in find_volume. Shouldn't be here." << std::endl;
01285 volume = 0;
01286 return MB_FAILURE;
01287 }
01288
01289 volume = parent_vols[idx];
01290
01291 return MB_SUCCESS;
01292 }
01293
01294 ErrorCode GeomQueryTool::find_volume_slow( const double xyz[3], EntityHandle& volume, const double* dir )
01295 {
01296 ErrorCode rval;
01297 volume = 0;
01298 // get all volumes
01299 Range all_vols;
01300 rval = geomTopoTool->get_gsets_by_dimension( 3, all_vols );MB_CHK_SET_ERR( rval, "Failed to get all volumes in the model" );
01301
01302 Range::iterator it;
01303 int result = 0;
01304 for( it = all_vols.begin(); it != all_vols.end(); it++ )
01305 {
01306 rval = point_in_volume( *it, xyz, result, dir );MB_CHK_SET_ERR( rval, "Failed in point in volume loop" );
01307 if( result )
01308 {
01309 volume = *it;
01310 break;
01311 }
01312 }
01313 return volume ? MB_SUCCESS : MB_ENTITY_NOT_FOUND;
01314 }
01315
01316 // detemine distance to nearest surface
01317 ErrorCode GeomQueryTool::closest_to_location( EntityHandle volume,
01318 const double coords[3],
01319 double& result,
01320 EntityHandle* closest_surface )
01321 {
01322 // Get OBB Tree for volume
01323 EntityHandle root;
01324 ErrorCode rval = geomTopoTool->get_root( volume, root );MB_CHK_SET_ERR( rval, "Failed to get the volume's obb tree root" );
01325
01326 // Get closest triangles in volume
01327 const CartVect point( coords );
01328 CartVect nearest;
01329 EntityHandle facet_out;
01330
01331 rval = geomTopoTool->obb_tree()->closest_to_location( point.array(), root, nearest.array(), facet_out,
01332 closest_surface );MB_CHK_SET_ERR( rval, "Failed to get the closest intersection to location" );
01333 // calculate distance between point and nearest facet
01334 result = ( point - nearest ).length();
01335
01336 return MB_SUCCESS;
01337 }
01338
01339 // calculate volume of polyhedron
01340 ErrorCode GeomQueryTool::measure_volume( EntityHandle volume, double& result )
01341 {
01342 ErrorCode rval;
01343 std::vector< EntityHandle > surfaces;
01344 result = 0.0;
01345
01346 // don't try to calculate volume of implicit complement
01347 if( geomTopoTool->is_implicit_complement( volume ) )
01348 {
01349 result = 1.0;
01350 return MB_SUCCESS;
01351 }
01352
01353 // get surfaces from volume
01354 rval = MBI->get_child_meshsets( volume, surfaces );MB_CHK_SET_ERR( rval, "Failed to get the volume's child surfaces" );
01355
01356 // get surface senses
01357 std::vector< int > senses( surfaces.size() );
01358 rval = geomTopoTool->get_surface_senses( volume, surfaces.size(), &surfaces[0], &senses[0] );MB_CHK_SET_ERR( rval, "Failed to retrieve surface-volume sense data. Cannot calculate volume" );
01359
01360 for( unsigned i = 0; i < surfaces.size(); ++i )
01361 {
01362 // skip non-manifold surfaces
01363 if( !senses[i] ) continue;
01364
01365 // get triangles in surface
01366 Range triangles;
01367 rval = MBI->get_entities_by_dimension( surfaces[i], 2, triangles );MB_CHK_SET_ERR( rval, "Failed to get the surface triangles" );
01368
01369 if( !triangles.all_of_type( MBTRI ) )
01370 {
01371 std::cout << "WARNING: Surface " << surfaces[i] // todo: use geomtopotool to get id by entity handle
01372 << " contains non-triangle elements. Volume calculation may be incorrect." << std::endl;
01373 triangles.clear();
01374 rval = MBI->get_entities_by_type( surfaces[i], MBTRI, triangles );MB_CHK_SET_ERR( rval, "Failed to get the surface triangles" );
01375 }
01376
01377 // calculate signed volume beneath surface (x 6.0)
01378 double surf_sum = 0.0;
01379 const EntityHandle* conn;
01380 int len;
01381 CartVect coords[3];
01382 for( Range::iterator j = triangles.begin(); j != triangles.end(); ++j )
01383 {
01384 rval = MBI->get_connectivity( *j, conn, len, true );MB_CHK_SET_ERR( rval, "Failed to get the connectivity of the current triangle" );
01385 if( 3 != len )
01386 {
01387 MB_SET_ERR( MB_FAILURE, "Incorrect connectivity length for triangle" );
01388 }
01389 rval = MBI->get_coords( conn, 3, coords[0].array() );MB_CHK_SET_ERR( rval, "Failed to get the coordinates of the current triangle's vertices" );
01390
01391 coords[1] -= coords[0];
01392 coords[2] -= coords[0];
01393 surf_sum += ( coords[0] % ( coords[1] * coords[2] ) );
01394 }
01395 result += senses[i] * surf_sum;
01396 }
01397
01398 result /= 6.0;
01399 return MB_SUCCESS;
01400 }
01401
01402 // sum area of elements in surface
01403 ErrorCode GeomQueryTool::measure_area( EntityHandle surface, double& result )
01404 {
01405 // get triangles in surface
01406 Range triangles;
01407 ErrorCode rval = MBI->get_entities_by_dimension( surface, 2, triangles );MB_CHK_SET_ERR( rval, "Failed to get the surface entities" );
01408 if( !triangles.all_of_type( MBTRI ) )
01409 {
01410 std::cout << "WARNING: Surface " << surface // todo: use geomtopotool to get id by entity handle
01411 << " contains non-triangle elements. Area calculation may be incorrect." << std::endl;
01412 triangles.clear();
01413 rval = MBI->get_entities_by_type( surface, MBTRI, triangles );MB_CHK_SET_ERR( rval, "Failed to the surface's triangle entities" );
01414 }
01415
01416 // calculate sum of area of triangles
01417 result = 0.0;
01418 const EntityHandle* conn;
01419 int len;
01420 CartVect coords[3];
01421 for( Range::iterator j = triangles.begin(); j != triangles.end(); ++j )
01422 {
01423 rval = MBI->get_connectivity( *j, conn, len, true );MB_CHK_SET_ERR( rval, "Failed to get the current triangle's connectivity" );
01424 if( 3 != len )
01425 {
01426 MB_SET_ERR( MB_FAILURE, "Incorrect connectivity length for triangle" );
01427 }
01428 rval = MBI->get_coords( conn, 3, coords[0].array() );MB_CHK_SET_ERR( rval, "Failed to get the current triangle's vertex coordinates" );
01429
01430 // calculated area using cross product of triangle edges
01431 CartVect v1 = coords[1] - coords[0];
01432 CartVect v2 = coords[2] - coords[0];
01433 CartVect xp = v1 * v2;
01434 result += xp.length();
01435 }
01436 result *= 0.5;
01437 return MB_SUCCESS;
01438 }
01439
01440 ErrorCode GeomQueryTool::get_normal( EntityHandle surf,
01441 const double in_pt[3],
01442 double angle[3],
01443 const RayHistory* history )
01444 {
01445 EntityHandle root;
01446 ErrorCode rval = geomTopoTool->get_root( surf, root );MB_CHK_SET_ERR( rval, "Failed to get the surface's obb tree root" );
01447
01448 std::vector< EntityHandle > facets;
01449
01450 // if no history or history empty, use nearby facets
01451 if( !history || ( history->prev_facets.size() == 0 ) )
01452 {
01453 rval = geomTopoTool->obb_tree()->closest_to_location( in_pt, root, numericalPrecision, facets );MB_CHK_SET_ERR( rval, "Failed to get closest intersection to location" );
01454 }
01455 // otherwise use most recent facet in history
01456 else
01457 {
01458 facets.push_back( history->prev_facets.back() );
01459 }
01460
01461 CartVect coords[3], normal( 0.0 );
01462 const EntityHandle* conn;
01463 int len;
01464 for( unsigned i = 0; i < facets.size(); ++i )
01465 {
01466 rval = MBI->get_connectivity( facets[i], conn, len );MB_CHK_SET_ERR( rval, "Failed to get facet connectivity" );
01467 if( 3 != len )
01468 {
01469 MB_SET_ERR( MB_FAILURE, "Incorrect connectivity length for triangle" );
01470 }
01471
01472 rval = MBI->get_coords( conn, 3, coords[0].array() );MB_CHK_SET_ERR( rval, "Failed to get vertex coordinates" );
01473
01474 coords[1] -= coords[0];
01475 coords[2] -= coords[0];
01476 normal += coords[1] * coords[2];
01477 }
01478
01479 normal.normalize();
01480 normal.get( angle );
01481
01482 return MB_SUCCESS;
01483 }
01484
01485 /* SECTION II (private) */
01486
01487 // If point is on boundary, then this function is called to
01488 // discriminate cases in which the ray is entering or leaving.
01489 // result= 1 -> inside volume or entering volume
01490 // result= 0 -> outside volume or leaving volume
01491 // result=-1 -> on boundary with null or tangent uvw
01492 ErrorCode GeomQueryTool::boundary_case( EntityHandle volume,
01493 int& result,
01494 double u,
01495 double v,
01496 double w,
01497 EntityHandle facet,
01498 EntityHandle surface )
01499 {
01500 ErrorCode rval;
01501
01502 // test to see if uvw is provided
01503 if( u <= 1.0 && v <= 1.0 && w <= 1.0 )
01504 {
01505
01506 const CartVect ray_vector( u, v, w );
01507 CartVect coords[3], normal( 0.0 );
01508 const EntityHandle* conn;
01509 int len, sense_out;
01510
01511 rval = MBI->get_connectivity( facet, conn, len );MB_CHK_SET_ERR( rval, "Failed to get the triangle's connectivity" );
01512 if( 3 != len )
01513 {
01514 MB_SET_ERR( MB_FAILURE, "Incorrect connectivity length for triangle" );
01515 }
01516
01517 rval = MBI->get_coords( conn, 3, coords[0].array() );MB_CHK_SET_ERR( rval, "Failed to get vertex coordinates" );
01518
01519 rval = geomTopoTool->get_sense( surface, volume, sense_out );MB_CHK_SET_ERR( rval, "Failed to get the surface's sense with respect to it's volume" );
01520
01521 coords[1] -= coords[0];
01522 coords[2] -= coords[0];
01523 normal = sense_out * ( coords[1] * coords[2] );
01524
01525 double sense = ray_vector % normal;
01526
01527 if( sense < 0.0 )
01528 {
01529 result = 1; // inside or entering
01530 }
01531 else if( sense > 0.0 )
01532 {
01533 result = 0; // outside or leaving
01534 }
01535 else if( sense == 0.0 )
01536 {
01537 result = -1; // tangent, therefore on boundary
01538 }
01539 else
01540 {
01541 result = -1; // failure
01542 MB_SET_ERR( MB_FAILURE, "Failed to resolve boundary case" );
01543 }
01544
01545 // if uvw not provided, return on_boundary.
01546 }
01547 else
01548 {
01549 result = -1; // on boundary
01550 return MB_SUCCESS;
01551 }
01552
01553 return MB_SUCCESS;
01554 }
01555
01556 // point_in_volume_slow, including poly_solid_angle helper subroutine
01557 // are adapted from "Point in Polyhedron Testing Using Spherical Polygons", Paulo Cezar
01558 // Pinto Carvalho and Paulo Roma Cavalcanti, _Graphics Gems V_, pg. 42. Original algorithm
01559 // was described in "An Efficient Point In Polyhedron Algorithm", Jeff Lane, Bob Magedson,
01560 // and Mike Rarick, _Computer Vision, Graphics, and Image Processing 26_, pg. 118-225, 1984.
01561
01562 // helper function for point_in_volume_slow. calculate area of a polygon
01563 // projected into a unit-sphere space
01564 ErrorCode GeomQueryTool::poly_solid_angle( EntityHandle face, const CartVect& point, double& area )
01565 {
01566 ErrorCode rval;
01567
01568 // Get connectivity
01569 const EntityHandle* conn;
01570 int len;
01571 rval = MBI->get_connectivity( face, conn, len, true );MB_CHK_SET_ERR( rval, "Failed to get the connectivity of the polygon" );
01572
01573 // Allocate space to store vertices
01574 CartVect coords_static[4];
01575 std::vector< CartVect > coords_dynamic;
01576 CartVect* coords = coords_static;
01577 if( (unsigned)len > ( sizeof( coords_static ) / sizeof( coords_static[0] ) ) )
01578 {
01579 coords_dynamic.resize( len );
01580 coords = &coords_dynamic[0];
01581 }
01582
01583 // get coordinates
01584 rval = MBI->get_coords( conn, len, coords->array() );MB_CHK_SET_ERR( rval, "Failed to get the coordinates of the polygon vertices" );
01585
01586 // calculate normal
01587 CartVect norm( 0.0 ), v1, v0 = coords[1] - coords[0];
01588 for( int i = 2; i < len; ++i )
01589 {
01590 v1 = coords[i] - coords[0];
01591 norm += v0 * v1;
01592 v0 = v1;
01593 }
01594
01595 // calculate area
01596 double s, ang;
01597 area = 0.0;
01598 CartVect r, n1, n2, b, a = coords[len - 1] - coords[0];
01599 for( int i = 0; i < len; ++i )
01600 {
01601 r = coords[i] - point;
01602 b = coords[( i + 1 ) % len] - coords[i];
01603 n1 = a * r; // = norm1 (magnitude is important)
01604 n2 = r * b; // = norm2 (magnitude is important)
01605 s = ( n1 % n2 ) / ( n1.length() * n2.length() ); // = cos(angle between norm1,norm2)
01606 ang = s <= -1.0 ? M_PI : s >= 1.0 ? 0.0 : acos( s ); // = acos(s)
01607 s = ( b * a ) % norm; // =orientation of triangle wrt point
01608 area += s > 0.0 ? M_PI - ang : M_PI + ang;
01609 a = -b;
01610 }
01611
01612 area -= M_PI * ( len - 2 );
01613 if( ( norm % r ) > 0 ) area = -area;
01614 return MB_SUCCESS;
01615 }
01616
01617 void GeomQueryTool::set_overlap_thickness( double new_thickness )
01618 {
01619
01620 if( new_thickness < 0 || new_thickness > 100 )
01621 {
01622 std::cerr << "Invalid overlap_thickness = " << new_thickness << std::endl;
01623 }
01624 else
01625 {
01626 overlapThickness = new_thickness;
01627 }
01628 std::cout << "Set overlap thickness = " << overlapThickness << std::endl;
01629 }
01630
01631 void GeomQueryTool::set_numerical_precision( double new_precision )
01632 {
01633
01634 if( new_precision <= 0 || new_precision > 1 )
01635 {
01636 std::cerr << "Invalid numerical_precision = " << numericalPrecision << std::endl;
01637 }
01638 else
01639 {
01640 numericalPrecision = new_precision;
01641 }
01642
01643 std::cout << "Set numerical precision = " << numericalPrecision << std::endl;
01644 }
01645
01646 } // namespace moab