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