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