MOAB: Mesh Oriented datABase  (version 5.4.1)
GeomQueryTool.cpp
Go to the documentation of this file.
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
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines