1 #include "moab/GeomQueryTool.hpp"
2
3 #ifdef WIN32
4 #define _USE_MATH_DEFINES
5 #endif
6 #include <string>
7 #include <iostream>
8 #include <fstream>
9 #include <sstream>
10 #include <limits>
11 #include <algorithm>
12 #include <set>
13
14 #include <cctype>
15 #include <cstring>
16 #include <cstdlib>
17 #include <cstdio>
18
19 #include "moab/OrientedBoxTreeTool.hpp"
20
21 #ifdef __DEBUG
22 #define MB_GQT_DEBUG
23 #endif
24
25 namespace moab
26 {
27
28
44
45 class FindVolumeIntRegCtxt : public OrientedBoxTreeTool::IntRegCtxt
46 {
47
48 public:
49
50 FindVolumeIntRegCtxt()
51 {
52
53
54 intersections.push_back( std::numeric_limits< double >::max() );
55 sets.push_back( 0 );
56 facets.push_back( 0 );
57 }
58
59 ErrorCode register_intersection( EntityHandle set,
60 EntityHandle tri,
61 double dist,
62 OrientedBoxTreeTool::IntersectSearchWindow& search_win,
63 GeomUtil::intersection_type )
64 {
65
66
67 double abs_dist = fabs( dist );
68 if( abs_dist < fabs( intersections[0] ) )
69 {
70 intersections[0] = dist;
71 sets[0] = set;
72 facets[0] = tri;
73
74
75 pos = abs_dist;
76 neg = -abs_dist;
77 search_win.first = &pos;
78 search_win.second = &neg;
79 }
80
81 return MB_SUCCESS;
82 }
83
84
85 double pos;
86 double neg;
87 };
88
89
122
123 class GQT_IntRegCtxt : public OrientedBoxTreeTool::IntRegCtxt
124 {
125
126 private:
127
128 OrientedBoxTreeTool* tool;
129 const CartVect ray_origin;
130 const CartVect ray_direction;
131 const double tol;
134 const int minTolInt;
135
136
137 const EntityHandle* rootSet;
138 const EntityHandle* geomVol;
139 const Tag* senseTag;
141 const int* desiredOrient;
145
146
147 const std::vector< EntityHandle >* prevFacets;
149
150
151 std::vector< std::vector< EntityHandle > > neighborhoods;
152 std::vector< EntityHandle > neighborhood;
153
154 void add_intersection( EntityHandle set,
155 EntityHandle tri,
156 double dist,
157 OrientedBoxTreeTool::IntersectSearchWindow& search_win );
158 void append_intersection( EntityHandle set, EntityHandle facet, double dist );
159 void set_intersection( int len_idx, EntityHandle set, EntityHandle facet, double dist );
160 void add_mode1_intersection( EntityHandle set,
161 EntityHandle facet,
162 double dist,
163 OrientedBoxTreeTool::IntersectSearchWindow& search_win );
164 bool edge_node_piercing_intersect( const EntityHandle tri,
165 const CartVect& ray_direction,
166 const GeomUtil::intersection_type int_type,
167 const std::vector< EntityHandle >& close_tris,
168 const std::vector< int >& close_senses,
169 const Interface* MBI,
170 std::vector< EntityHandle >* neighborhood_tris = 0 );
171
172 bool in_prevFacets( const EntityHandle tri );
173 bool in_neighborhoods( const EntityHandle tri );
174
175 public:
176 GQT_IntRegCtxt( OrientedBoxTreeTool* obbtool,
177 const double ray_point[3],
178 const double ray_dir[3],
179 double tolerance,
180 int min_tolerance_intersections,
181 const EntityHandle* root_set,
182 const EntityHandle* geom_volume,
183 const Tag* sense_tag,
184 const int* desired_orient,
185 const std::vector< EntityHandle >* prev_facets )
186 : tool( obbtool ), ray_origin( ray_point ), ray_direction( ray_dir ), tol( tolerance ),
187 minTolInt( min_tolerance_intersections ), rootSet( root_set ), geomVol( geom_volume ), senseTag( sense_tag ),
188 desiredOrient( desired_orient ), prevFacets( prev_facets ){
189
190 };
191
192 virtual ErrorCode register_intersection( EntityHandle set,
193 EntityHandle triangle,
194 double distance,
195 OrientedBoxTreeTool::IntersectSearchWindow&,
196 GeomUtil::intersection_type int_type );
197
198 virtual ErrorCode update_orient( EntityHandle set, int* surfTriOrient );
199 virtual const int* getDesiredOrient()
200 {
201 return desiredOrient;
202 };
203 };
204
205 ErrorCode GQT_IntRegCtxt::update_orient( EntityHandle set, int* surfTriOrient )
206 {
207
208 ErrorCode rval;
209
210
211
212 if( geomVol && senseTag && desiredOrient && surfTriOrient )
213 {
214 if( 1 != *desiredOrient && -1 != *desiredOrient )
215 {
216 std::cerr << "error: desired orientation must be 1 (forward) or -1 (reverse)" << std::endl;
217 }
218 EntityHandle vols[2];
219 rval = tool->get_moab_instance()->tag_get_data( *senseTag, &set, 1, vols );
220 assert( MB_SUCCESS == rval );
221 if( MB_SUCCESS != rval ) return rval;
222 if( vols[0] == vols[1] )
223 {
224 std::cerr << "error: surface has positive and negative sense wrt same volume" << std::endl;
225 return MB_FAILURE;
226 }
227
228
229 if( *geomVol == vols[0] )
230 {
231 *surfTriOrient = *desiredOrient * 1;
232 }
233 else if( *geomVol == vols[1] )
234 {
235 *surfTriOrient = *desiredOrient * ( -1 );
236 }
237 else
238 {
239 assert( false );
240 return MB_FAILURE;
241 }
242 }
243
244 return MB_SUCCESS;
245 }
246
247 bool GQT_IntRegCtxt::in_prevFacets( const EntityHandle tri )
248 {
249 return ( prevFacets && ( ( *prevFacets ).end() != find( ( *prevFacets ).begin(), ( *prevFacets ).end(), tri ) ) );
250 }
251
252 bool GQT_IntRegCtxt::in_neighborhoods( const EntityHandle tri )
253 {
254 bool same_neighborhood = false;
255 for( unsigned i = 0; i < neighborhoods.size(); ++i )
256 {
257 if( neighborhoods[i].end() != find( neighborhoods[i].begin(), neighborhoods[i].end(), tri ) )
258 {
259 same_neighborhood = true;
260 continue;
261 }
262 }
263 return same_neighborhood;
264 }
265
266
278 bool GQT_IntRegCtxt::edge_node_piercing_intersect( const EntityHandle tri,
279 const CartVect& ray_dir,
280 const GeomUtil::intersection_type int_type,
281 const std::vector< EntityHandle >& close_tris,
282 const std::vector< int >& close_senses,
283 const Interface* MBI,
284 std::vector< EntityHandle >* neighborhood_tris )
285 {
286
287
288 const EntityHandle* conn = NULL;
289 int len = 0;
290 ErrorCode rval = MBI->get_connectivity( tri, conn, len );MB_CHK_ERR_RET_VAL( rval, false );
291
292 if( 3 != len ) return false;
293
294
295 std::vector< EntityHandle > adj_tris;
296 std::vector< int > adj_senses;
297
298
299 if( GeomUtil::NODE0 == int_type || GeomUtil::NODE1 == int_type || GeomUtil::NODE2 == int_type )
300 {
301
302
303 EntityHandle node;
304 if( GeomUtil::NODE0 == int_type )
305 node = conn[0];
306 else if( GeomUtil::NODE1 == int_type )
307 node = conn[1];
308 else
309 node = conn[2];
310
311
312 for( unsigned i = 0; i < close_tris.size(); ++i )
313 {
314 const EntityHandle* con = NULL;
315 rval = MBI->get_connectivity( close_tris[i], con, len );MB_CHK_ERR_RET_VAL( rval, false );
316 if( 3 != len ) return false;
317
318 if( node == con[0] || node == con[1] || node == con[2] )
319 {
320 adj_tris.push_back( close_tris[i] );
321 adj_senses.push_back( close_senses[i] );
322 }
323 }
324 if( adj_tris.empty() )
325 {
326 std::cerr << "error: no tris are adjacent to the node" << std::endl;
327 return false;
328 }
329
330 }
331 else if( GeomUtil::EDGE0 == int_type || GeomUtil::EDGE1 == int_type || GeomUtil::EDGE2 == int_type )
332 {
333
334
335 EntityHandle endpts[2];
336 if( GeomUtil::EDGE0 == int_type )
337 {
338 endpts[0] = conn[0];
339 endpts[1] = conn[1];
340 }
341 else if( GeomUtil::EDGE1 == int_type )
342 {
343 endpts[0] = conn[1];
344 endpts[1] = conn[2];
345 }
346 else
347 {
348 endpts[0] = conn[2];
349 endpts[1] = conn[0];
350 }
351
352
353 for( unsigned i = 0; i < close_tris.size(); ++i )
354 {
355 const EntityHandle* con = NULL;
356 rval = MBI->get_connectivity( close_tris[i], con, len );MB_CHK_ERR_RET_VAL( rval, false );
357 if( 3 != len ) return false;
358
359
360 if( ( endpts[0] == con[0] && endpts[1] == con[1] ) || ( endpts[0] == con[1] && endpts[1] == con[0] ) ||
361 ( endpts[0] == con[1] && endpts[1] == con[2] ) || ( endpts[0] == con[2] && endpts[1] == con[1] ) ||
362 ( endpts[0] == con[2] && endpts[1] == con[0] ) || ( endpts[0] == con[0] && endpts[1] == con[2] ) )
363 {
364 adj_tris.push_back( close_tris[i] );
365 adj_senses.push_back( close_senses[i] );
366 }
367 }
368
369 if( 2 != adj_tris.size() )
370 {
371 std::cerr << "error: edge of a manifold must be topologically adjacent to exactly 2 tris" << std::endl;
372 MBI->list_entities( endpts, 2 );
373 return true;
374 }
375 }
376 else
377 {
378 std::cerr << "error: special case not an node/edge intersection" << std::endl;
379 return false;
380 }
381
382
383
384 if( neighborhood_tris ) ( *neighborhood_tris ).assign( adj_tris.begin(), adj_tris.end() );
385
386
387
388
389
390 int sign = 0;
391 for( unsigned i = 0; i < adj_tris.size(); ++i )
392 {
393 const EntityHandle* con = NULL;
394 rval = MBI->get_connectivity( adj_tris[i], con, len );MB_CHK_ERR_RET_VAL( rval, false );
395 if( 3 != len ) return false;
396
397 CartVect coords[3];
398 rval = MBI->get_coords( con, len, coords[0].array() );MB_CHK_ERR_RET_VAL( rval, false );
399
400
401 CartVect v0 = coords[1] - coords[0];
402 CartVect v1 = coords[2] - coords[0];
403 CartVect norm = adj_senses[i] * ( v0 * v1 );
404 double dot_prod = norm % ray_dir;
405
406
407 if( 0 == sign && 0 != dot_prod )
408 {
409 if( 0 < dot_prod )
410 sign = 1;
411 else
412 sign = -1;
413 }
414
415
416
417 if( 0 != sign && 0 > sign * dot_prod ) return false;
418 }
419 return true;
420 }
421
422 ErrorCode GQT_IntRegCtxt::register_intersection( EntityHandle set,
423 EntityHandle t,
424 double int_dist,
425 OrientedBoxTreeTool::IntersectSearchWindow& search_win,
426 GeomUtil::intersection_type int_type )
427 {
428 ErrorCode rval;
429
430
431
432 if( in_prevFacets( t ) ) return MB_SUCCESS;
433
434
435
436 if( in_neighborhoods( t ) ) return MB_SUCCESS;
437
438 neighborhood.clear();
439
440
441
442
443
444
445 if( GeomUtil::INTERIOR != int_type && rootSet && geomVol && senseTag )
446 {
447
448 std::vector< EntityHandle > close_tris;
449 std::vector< int > close_senses;
450 rval = tool->get_close_tris( ray_origin + int_dist * ray_direction, tol, rootSet, geomVol, senseTag, close_tris,
451 close_senses );
452
453 if( MB_SUCCESS != rval ) return rval;
454
455 if( !edge_node_piercing_intersect( t, ray_direction, int_type, close_tris, close_senses,
456 tool->get_moab_instance(), &neighborhood ) )
457 return MB_SUCCESS;
458 }
459 else
460 {
461 neighborhood.push_back( t );
462 }
463
464
465
466
467 add_intersection( set, t, int_dist, search_win );
468
469 return MB_SUCCESS;
470 }
471
472 void GQT_IntRegCtxt::append_intersection( EntityHandle set, EntityHandle facet, double dist )
473 {
474 intersections.push_back( dist );
475 sets.push_back( set );
476 facets.push_back( facet );
477 neighborhoods.push_back( neighborhood );
478 return;
479 }
480
481 void GQT_IntRegCtxt::set_intersection( int len_idx, EntityHandle set, EntityHandle facet, double dist )
482 {
483 intersections[len_idx] = dist;
484 sets[len_idx] = set;
485 facets[len_idx] = facet;
486 return;
487 }
488
489
494 void GQT_IntRegCtxt::add_mode1_intersection( EntityHandle set,
495 EntityHandle facet,
496 double dist,
497 OrientedBoxTreeTool::IntersectSearchWindow& search_win )
498 {
499 if( 2 != intersections.size() )
500 {
501 intersections.resize( 2, 0 );
502 sets.resize( 2, 0 );
503 facets.resize( 2, 0 );
504
505 intersections[0] = -std::numeric_limits< double >::max();
506 }
507
508
509 if( 0.0 > dist )
510 {
511 set_intersection( 0, set, facet, dist );
512 search_win.second = &intersections[0];
513
514 }
515 else
516 {
517 set_intersection( 1, set, facet, dist );
518 search_win.first = &intersections[1];
519
520 if( dist < -*( search_win.second ) )
521 {
522 set_intersection( 0, 0, 0, -intersections[1] );
523 search_win.second = &intersections[0];
524 }
525 }
526
527
528
529 return;
530 }
531
532 void GQT_IntRegCtxt::add_intersection( EntityHandle set,
533 EntityHandle facet,
534 double dist,
535 OrientedBoxTreeTool::IntersectSearchWindow& search_win )
536 {
537
538
539
540 if( search_win.second && search_win.first )
541 {
542 return add_mode1_intersection( set, facet, dist, search_win );
543 }
544
545
546
552
553
554 if( minTolInt < 0 && dist > -tol )
555 {
556 append_intersection( set, facet, dist );
557 neighborhoods.push_back( neighborhood );
558 return;
559 }
560
561
562
563
564
565 int len_idx = -1;
566 if( search_win.first && search_win.first >= &intersections[0] &&
567 search_win.first < &intersections[0] + intersections.size() )
568 len_idx = search_win.first - &intersections[0];
569
570
571
572 if( dist <= tol )
573 {
574
575 if( len_idx >= 0 )
576 {
577
578
579 if( (int)intersections.size() >= minTolInt )
580 {
581 set_intersection( len_idx, set, facet, dist );
582
583
584 search_win.first = &tol;
585 }
586
587 else
588 {
589 append_intersection( set, facet, dist );
590 search_win.first = &intersections[len_idx];
591 }
592 }
593
594 else
595 {
596 append_intersection( set, facet, dist );
597
598
599
600 if( (int)intersections.size() >= minTolInt ) search_win.first = &tol;
601 }
602 }
603
604
605
606 else if( len_idx >= 0 )
607 {
608 if( dist <= *search_win.first )
609 {
610 set_intersection( len_idx, set, facet, dist );
611 }
612 }
613
614
615 else if( (int)intersections.size() < minTolInt )
616 {
617 append_intersection( set, facet, dist );
618
619
620 search_win.first = &intersections.back();
621 }
622 }
623
624 GeomQueryTool::GeomQueryTool( Interface* impl,
625 bool find_geomsets,
626 EntityHandle modelRootSet,
627 bool p_rootSets_vector,
628 bool restore_rootSets,
629 bool trace_counting,
630 double overlap_thickness,
631 double numerical_precision )
632 : verbose( false ), owns_gtt( true )
633 {
634 geomTopoTool = new GeomTopoTool( impl, find_geomsets, modelRootSet, p_rootSets_vector, restore_rootSets );
635
636 senseTag = geomTopoTool->get_sense_tag();
637
638 obbTreeTool = geomTopoTool->obb_tree();
639 MBI = geomTopoTool->get_moab_instance();
640
641 counting = trace_counting;
642 overlapThickness = overlap_thickness;
643 numericalPrecision = numerical_precision;
644
645
646 n_pt_in_vol_calls = 0;
647 n_ray_fire_calls = 0;
648 }
649
650 GeomQueryTool::GeomQueryTool( GeomTopoTool* geomtopotool,
651 bool trace_counting,
652 double overlap_thickness,
653 double numerical_precision )
654 : verbose( false ), owns_gtt( false )
655 {
656
657 geomTopoTool = geomtopotool;
658
659 senseTag = geomTopoTool->get_sense_tag();
660
661 obbTreeTool = geomTopoTool->obb_tree();
662 MBI = geomTopoTool->get_moab_instance();
663
664 counting = trace_counting;
665 overlapThickness = overlap_thickness;
666 numericalPrecision = numerical_precision;
667
668
669 n_pt_in_vol_calls = 0;
670 n_ray_fire_calls = 0;
671 }
672
673 GeomQueryTool::~GeomQueryTool()
674 {
675 if( owns_gtt )
676 {
677 delete geomTopoTool;
678 }
679 }
680
681 ErrorCode GeomQueryTool::initialize()
682 {
683
684 ErrorCode rval;
685
686 rval = geomTopoTool->find_geomsets();MB_CHK_SET_ERR( rval, "Failed to find geometry sets" );
687
688 rval = geomTopoTool->setup_implicit_complement();MB_CHK_SET_ERR( rval, "Couldn't setup the implicit complement" );
689
690 rval = geomTopoTool->construct_obb_trees();MB_CHK_SET_ERR( rval, "Failed to construct OBB trees" );
691
692 return MB_SUCCESS;
693 }
694
695 void GeomQueryTool::RayHistory::reset()
696 {
697 prev_facets.clear();
698 }
699
700 void GeomQueryTool::RayHistory::reset_to_last_intersection()
701 {
702
703 if( prev_facets.size() > 1 )
704 {
705 prev_facets[0] = prev_facets.back();
706 prev_facets.resize( 1 );
707 }
708 }
709
710 void GeomQueryTool::RayHistory::rollback_last_intersection()
711 {
712 if( prev_facets.size() ) prev_facets.pop_back();
713 }
714
715 ErrorCode GeomQueryTool::RayHistory::get_last_intersection( EntityHandle& last_facet_hit ) const
716 {
717 if( prev_facets.size() > 0 )
718 {
719 last_facet_hit = prev_facets.back();
720 return MB_SUCCESS;
721 }
722 else
723 {
724 return MB_ENTITY_NOT_FOUND;
725 }
726 }
727
728 bool GeomQueryTool::RayHistory::in_history( EntityHandle ent ) const
729 {
730 return std::find( prev_facets.begin(), prev_facets.end(), ent ) != prev_facets.end();
731 }
732
733 void GeomQueryTool::RayHistory::add_entity( EntityHandle ent )
734 {
735 prev_facets.push_back( ent );
736 }
737
738 ErrorCode GeomQueryTool::ray_fire( const EntityHandle volume,
739 const double point[3],
740 const double dir[3],
741 EntityHandle& next_surf,
742 double& next_surf_dist,
743 RayHistory* history,
744 double user_dist_limit,
745 int ray_orientation,
746 OrientedBoxTreeTool::TrvStats* stats )
747 {
748
749
750 if( counting )
751 {
752 ++n_ray_fire_calls;
753 if( 0 == n_ray_fire_calls % 10000000 )
754 {
755 std::cout << "n_ray_fires=" << n_ray_fire_calls << " n_pt_in_vols=" << n_pt_in_vol_calls << std::endl;
756 }
757 }
758
759 #ifdef MB_GQT_DEBUG
760 std::cout << "ray_fire:"
761 << " xyz=" << point[0] << " " << point[1] << " " << point[2] << " uvw=" << dir[0] << " " << dir[1] << " "
762 << dir[2] << " entity_handle=" << volume << std::endl;
763 #endif
764
765 const double huge_val = std::numeric_limits< double >::max();
766 double dist_limit = huge_val;
767 if( user_dist_limit > 0 ) dist_limit = user_dist_limit;
768
769
770 std::vector< double > dists;
771 std::vector< EntityHandle > surfs;
772 std::vector< EntityHandle > facets;
773
774 EntityHandle root;
775 ErrorCode rval = geomTopoTool->get_root( volume, root );MB_CHK_SET_ERR( rval, "Failed to get the obb tree root of the volume" );
776
777
778 double neg_ray_len;
779 if( 0 == overlapThickness )
780 {
781 neg_ray_len = -numericalPrecision;
782 }
783 else
784 {
785 neg_ray_len = -overlapThickness;
786 }
787
788
789 double nonneg_ray_len = dist_limit;
790
791
792
793 if( nonneg_ray_len < -neg_ray_len ) nonneg_ray_len = -neg_ray_len;
794 if( 0 > nonneg_ray_len || 0 <= neg_ray_len )
795 {
796 MB_SET_ERR( MB_FAILURE, "Incorrect ray length provided" );
797 }
798
799
800 const int min_tolerance_intersections = 0;
801
802
803
804 GQT_IntRegCtxt int_reg_ctxt( geomTopoTool->obb_tree(), point, dir, numericalPrecision, min_tolerance_intersections,
805 &root, &volume, &senseTag, &ray_orientation,
806 history ? &( history->prev_facets ) : NULL );
807
808 OrientedBoxTreeTool::IntersectSearchWindow search_win( &nonneg_ray_len, &neg_ray_len );
809 rval = geomTopoTool->obb_tree()->ray_intersect_sets( dists, surfs, facets, root, numericalPrecision, point, dir,
810 search_win, int_reg_ctxt, stats );
811
812 MB_CHK_SET_ERR( rval, "Ray query failed" );
813
814
815
816
817
818 if( dists.empty() )
819 {
820 next_surf = 0;
821 #ifdef MB_GQT_DEBUG
822 std::cout << " next_surf=0 dist=(undef)" << std::endl;
823 #endif
824 return MB_SUCCESS;
825 }
826
827
828
829
830 if( 2 != dists.size() || 2 != facets.size() )
831 {
832 MB_SET_ERR( MB_FAILURE, "Incorrect number of facets/distances" );
833 }
834 if( 0.0 < dists[0] || 0.0 > dists[1] )
835 {
836 MB_SET_ERR( MB_FAILURE, "Invalid intersection distance signs" );
837 }
838
839
840
841 if( ( 0 != facets[0] && 0 != facets[1] ) && ( -dists[0] > dists[1] ) )
842 {
843 MB_SET_ERR( MB_FAILURE, "Invalid intersection distance values" );
844 }
845
846
847
848 int exit_idx = -1;
849 if( 0 != facets[0] )
850 {
851
852 std::vector< EntityHandle > vols;
853 EntityHandle nx_vol;
854 rval = MBI->get_parent_meshsets( surfs[0], vols );MB_CHK_SET_ERR( rval, "Failed to get the parent meshsets" );
855 if( 2 != vols.size() )
856 {
857 MB_SET_ERR( MB_FAILURE, "Invaid number of parent volumes found" );
858 }
859 if( vols.front() == volume )
860 {
861 nx_vol = vols.back();
862 }
863 else
864 {
865 nx_vol = vols.front();
866 }
867
868
869
870
871 int result;
872 rval = point_in_volume( nx_vol, point, result, dir, history );MB_CHK_SET_ERR( rval, "Point in volume query failed" );
873 if( 1 == result ) exit_idx = 0;
874 }
875
876
877 if( -1 == exit_idx && 0 != facets[1] ) exit_idx = 1;
878
879
880 if( -1 == exit_idx )
881 {
882 next_surf = 0;
883 #ifdef MB_GQT_DEBUG
884 std::cout << "next surf hit = 0, dist = (undef)" << std::endl;
885 #endif
886 return MB_SUCCESS;
887 }
888
889
890 next_surf = surfs[exit_idx];
891 next_surf_dist = ( 0 > dists[exit_idx] ? 0 : dists[exit_idx] );
892
893 if( history )
894 {
895 history->prev_facets.push_back( facets[exit_idx] );
896 }
897
898 #ifdef MB_GQT_DEBUG
899 if( 0 > dists[exit_idx] )
900 {
901 std::cout << " OVERLAP track length=" << dists[exit_idx] << std::endl;
902 }
903 std::cout << " next_surf = " << next_surf
904 << ", dist = " << next_surf_dist << " new_pt=";
905 for( int i = 0; i < 3; ++i )
906 {
907 std::cout << point[i] + dir[i] * next_surf_dist << " ";
908 }
909 std::cout << std::endl;
910 #endif
911
912 return MB_SUCCESS;
913 }
914
915 ErrorCode GeomQueryTool::point_in_volume( const EntityHandle volume,
916 const double xyz[3],
917 int& result,
918 const double* uvw,
919 const RayHistory* history )
920 {
921
922 if( counting ) ++n_pt_in_vol_calls;
923
924
925
926 ErrorCode rval = point_in_box( volume, xyz, result );
927 if( !result )
928 {
929 result = 0;
930 return MB_SUCCESS;
931 }
932
933
934 EntityHandle root;
935 rval = geomTopoTool->get_root( volume, root );MB_CHK_SET_ERR( rval, "Failed to find the volume's obb tree root" );
936
937
938
939 std::vector< double > dists;
940 std::vector< EntityHandle > surfs;
941 std::vector< EntityHandle > facets;
942 std::vector< int > dirs;
943
944
945 double u = 0, v = 0, w = 0;
946
947 if( uvw )
948 {
949 u = uvw[0];
950 v = uvw[1], w = uvw[2];
951 }
952
953 if( u == 0 && v == 0 && w == 0 )
954 {
955 u = rand();
956 v = rand();
957 w = rand();
958 const double magnitude = sqrt( u * u + v * v + w * w );
959 u /= magnitude;
960 v /= magnitude;
961 w /= magnitude;
962 }
963
964 const double ray_direction[] = { u, v, w };
965
966
967 const double large = 1e15;
968 double ray_length = large;
969
970
971
972
973 int min_tolerance_intersections;
974 if( 0 != overlapThickness )
975 {
976 min_tolerance_intersections = -1;
977
978 }
979 else
980 {
981 min_tolerance_intersections = 1;
982 }
983
984
985
986 GQT_IntRegCtxt int_reg_ctxt( geomTopoTool->obb_tree(), xyz, ray_direction, numericalPrecision,
987 min_tolerance_intersections, &root, &volume, &senseTag, NULL,
988 history ? &( history->prev_facets ) : NULL );
989
990 OrientedBoxTreeTool::IntersectSearchWindow search_win( &ray_length, (double*)NULL );
991 rval = geomTopoTool->obb_tree()->ray_intersect_sets( dists, surfs, facets, root, numericalPrecision, xyz,
992 ray_direction, search_win, int_reg_ctxt );MB_CHK_SET_ERR( rval, "Ray fire query failed" );
993
994
995
996
997 dirs.resize( dists.size() );
998 for( unsigned i = 0; i < dists.size(); ++i )
999 {
1000 rval = boundary_case( volume, dirs[i], u, v, w, facets[i], surfs[i] );MB_CHK_SET_ERR( rval, "Failed to resolve boundary case" );
1001 }
1002
1003
1004 if( 0 != overlapThickness )
1005 {
1006 int sum = 0;
1007 for( unsigned i = 0; i < dirs.size(); ++i )
1008 {
1009 if( 1 == dirs[i] )
1010 sum += 1;
1011 else if( 0 == dirs[i] )
1012 sum -= 1;
1013 else if( -1 == dirs[i] )
1014 {
1015 std::cout << "direction==tangent" << std::endl;
1016 sum += 0;
1017 }
1018 else
1019 {
1020 MB_SET_ERR( MB_FAILURE, "Error: unknown direction" );
1021 }
1022 }
1023
1024
1025 if( 0 < sum )
1026 result = 0;
1027 else if( 0 > sum )
1028 result = 1;
1029 else if( geomTopoTool->is_implicit_complement( volume ) )
1030 result = 1;
1031 else
1032 result = 0;
1033
1034
1035 }
1036 else
1037 {
1038 if( dirs.empty() )
1039 {
1040 result = 0;
1041 }
1042 else
1043 {
1044 int smallest = std::min_element( dists.begin(), dists.end() ) - dists.begin();
1045 if( 1 == dirs[smallest] )
1046 result = 0;
1047 else if( 0 == dirs[smallest] )
1048 result = 1;
1049 else if( -1 == dirs[smallest] )
1050 {
1051
1052
1053 std::cerr << "direction==tangent" << std::endl;
1054 result = -1;
1055 }
1056 else
1057 {
1058 MB_SET_ERR( MB_FAILURE, "Error: unknown direction" );
1059 }
1060 }
1061 }
1062
1063 #ifdef MB_GQT_DEBUG
1064 std::cout << "pt_in_vol: result=" << result << " xyz=" << xyz[0] << " " << xyz[1] << " " << xyz[2] << " uvw=" << u
1065 << " " << v << " " << w << " vol_id=" << volume
1066 << std::endl;
1067 #endif
1068
1069 return MB_SUCCESS;
1070 }
1071
1072
1077 ErrorCode GeomQueryTool::point_in_box( EntityHandle volume, const double point[3], int& inside )
1078 {
1079 double minpt[3];
1080 double maxpt[3];
1081 ErrorCode rval = geomTopoTool->get_bounding_coords( volume, minpt, maxpt );MB_CHK_SET_ERR( rval, "Failed to get the bounding coordinates of the volume" );
1082
1083
1084 if( point[0] > maxpt[0] || point[0] < minpt[0] )
1085 {
1086 inside = 0;
1087 return rval;
1088 }
1089 if( point[1] > maxpt[1] || point[1] < minpt[1] )
1090 {
1091 inside = 0;
1092 return rval;
1093 }
1094 if( point[2] > maxpt[2] || point[2] < minpt[2] )
1095 {
1096 inside = 0;
1097 return rval;
1098 }
1099 inside = 1;
1100 return rval;
1101 }
1102
1103 ErrorCode GeomQueryTool::test_volume_boundary( const EntityHandle volume,
1104 const EntityHandle surface,
1105 const double xyz[3],
1106 const double uvw[3],
1107 int& result,
1108 const RayHistory* history )
1109 {
1110 ErrorCode rval;
1111 int dir;
1112
1113 if( history && history->prev_facets.size() )
1114 {
1115
1116 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" );
1117 }
1118 else
1119 {
1120
1121
1122
1123 EntityHandle root;
1124 rval = geomTopoTool->get_root( volume, root );MB_CHK_SET_ERR( rval, "Failed to get the volume's OBB tree root" );
1125
1126
1127 const CartVect point( xyz );
1128 CartVect nearest;
1129 EntityHandle facet_out;
1130 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" );
1131
1132 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" );
1133 }
1134
1135 result = dir;
1136
1137 return MB_SUCCESS;
1138 }
1139
1140
1141 ErrorCode GeomQueryTool::point_in_volume_slow( EntityHandle volume, const double xyz[3], int& result )
1142 {
1143 ErrorCode rval;
1144 Range faces;
1145 std::vector< EntityHandle > surfs;
1146 std::vector< int > senses;
1147 double sum = 0.0;
1148 const CartVect point( xyz );
1149
1150 rval = MBI->get_child_meshsets( volume, surfs );MB_CHK_SET_ERR( rval, "Failed to get the volume's child surfaces" );
1151
1152 senses.resize( surfs.size() );
1153 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" );
1154
1155 for( unsigned i = 0; i < surfs.size(); ++i )
1156 {
1157 if( !senses[i] )
1158 continue;
1159
1160 double surf_area = 0.0, face_area;
1161 faces.clear();
1162 rval = MBI->get_entities_by_dimension( surfs[i], 2, faces );MB_CHK_SET_ERR( rval, "Failed to get the surface entities by dimension" );
1163
1164 for( Range::iterator j = faces.begin(); j != faces.end(); ++j )
1165 {
1166 rval = poly_solid_angle( *j, point, face_area );MB_CHK_SET_ERR( rval, "Failed to determin the polygon's solid angle" );
1167
1168 surf_area += face_area;
1169 }
1170
1171 sum += senses[i] * surf_area;
1172 }
1173
1174 result = fabs( sum ) > 2.0 * M_PI;
1175 return MB_SUCCESS;
1176 }
1177
1178 ErrorCode GeomQueryTool::find_volume( const double xyz[3], EntityHandle& volume, const double* dir )
1179 {
1180 ErrorCode rval;
1181 volume = 0;
1182
1183 EntityHandle global_surf_tree_root = geomTopoTool->get_one_vol_root();
1184
1185
1186 int ic_result;
1187 EntityHandle ic_handle;
1188 rval = geomTopoTool->get_implicit_complement( ic_handle );MB_CHK_SET_ERR( rval, "Failed to get the implicit complement handle" );
1189
1190 rval = point_in_box( ic_handle, xyz, ic_result );MB_CHK_SET_ERR( rval, "Failed to check implicit complement for containment" );
1191 if( ic_result == 0 )
1192 {
1193 volume = 0;
1194 return MB_ENTITY_NOT_FOUND;
1195 }
1196
1197
1198 if( !global_surf_tree_root )
1199 {
1200 rval = find_volume_slow( xyz, volume, dir );
1201 return rval;
1202 }
1203
1204 moab::CartVect uvw( 0.0 );
1205
1206 if( dir )
1207 {
1208 uvw[0] = dir[0];
1209 uvw[1] = dir[1];
1210 uvw[2] = dir[2];
1211 }
1212
1213 if( uvw == 0.0 )
1214 {
1215 uvw[0] = rand();
1216 uvw[1] = rand();
1217 uvw[2] = rand();
1218 }
1219
1220
1221 uvw.normalize();
1222
1223
1224 const double huge_val = std::numeric_limits< double >::max();
1225 double pos_ray_len = huge_val;
1226 double neg_ray_len = -huge_val;
1227
1228
1229 std::vector< double > dists;
1230 std::vector< EntityHandle > surfs;
1231 std::vector< EntityHandle > facets;
1232
1233 FindVolumeIntRegCtxt find_vol_reg_ctxt;
1234 OrientedBoxTreeTool::IntersectSearchWindow search_win( &pos_ray_len, &neg_ray_len );
1235 rval =
1236 geomTopoTool->obb_tree()->ray_intersect_sets( dists, surfs, facets, global_surf_tree_root, numericalPrecision,
1237 xyz, uvw.array(), search_win, find_vol_reg_ctxt );MB_CHK_SET_ERR( rval, "Failed in global tree ray fire" );
1238
1239
1240 if( surfs.size() == 0 || surfs[0] == 0 )
1241 {
1242 volume = 0;
1243 return MB_ENTITY_NOT_FOUND;
1244 }
1245
1246
1247 EntityHandle facet = facets[0];
1248 EntityHandle surf = surfs[0];
1249
1250
1251 EntityHandle fwd_vol, bwd_vol;
1252 rval = geomTopoTool->get_surface_senses( surf, fwd_vol, bwd_vol );MB_CHK_SET_ERR( rval, "Failed to get sense data" );
1253 EntityHandle parent_vols[2];
1254 parent_vols[0] = fwd_vol;
1255 parent_vols[1] = bwd_vol;
1256
1257
1258 std::vector< EntityHandle > conn;
1259 CartVect coords[3];
1260 rval = MBI->get_connectivity( &facet, 1, conn );MB_CHK_SET_ERR( rval, "Failed to get triangle connectivity" );
1261
1262 rval = MBI->get_coords( &conn[0], 3, coords[0].array() );MB_CHK_SET_ERR( rval, "Failed to get triangle coordinates" );
1263
1264 CartVect normal = ( coords[1] - coords[0] ) * ( coords[2] - coords[0] );
1265 normal.normalize();
1266
1267
1268 if( dists[0] < 0 )
1269 {
1270 uvw *= -1;
1271 }
1272
1273
1274
1275 double dot_prod = uvw % normal;
1276 int idx = dot_prod > 0.0 ? 0 : 1;
1277
1278 if( dot_prod == 0.0 )
1279 {
1280 std::cerr << "Tangent dot product in find_volume. Shouldn't be here." << std::endl;
1281 volume = 0;
1282 return MB_FAILURE;
1283 }
1284
1285 volume = parent_vols[idx];
1286
1287 return MB_SUCCESS;
1288 }
1289
1290 ErrorCode GeomQueryTool::find_volume_slow( const double xyz[3], EntityHandle& volume, const double* dir )
1291 {
1292 ErrorCode rval;
1293 volume = 0;
1294
1295 Range all_vols;
1296 rval = geomTopoTool->get_gsets_by_dimension( 3, all_vols );MB_CHK_SET_ERR( rval, "Failed to get all volumes in the model" );
1297
1298 Range::iterator it;
1299 int result = 0;
1300 for( it = all_vols.begin(); it != all_vols.end(); it++ )
1301 {
1302 rval = point_in_volume( *it, xyz, result, dir );MB_CHK_SET_ERR( rval, "Failed in point in volume loop" );
1303 if( result )
1304 {
1305 volume = *it;
1306 break;
1307 }
1308 }
1309 return volume ? MB_SUCCESS : MB_ENTITY_NOT_FOUND;
1310 }
1311
1312
1313 ErrorCode GeomQueryTool::closest_to_location( EntityHandle volume,
1314 const double coords[3],
1315 double& result,
1316 EntityHandle* closest_surface )
1317 {
1318
1319 EntityHandle root;
1320 ErrorCode rval = geomTopoTool->get_root( volume, root );MB_CHK_SET_ERR( rval, "Failed to get the volume's obb tree root" );
1321
1322
1323 const CartVect point( coords );
1324 CartVect nearest;
1325 EntityHandle facet_out;
1326
1327 rval = geomTopoTool->obb_tree()->closest_to_location( point.array(), root, nearest.array(), facet_out,
1328 closest_surface );MB_CHK_SET_ERR( rval, "Failed to get the closest intersection to location" );
1329
1330 result = ( point - nearest ).length();
1331
1332 return MB_SUCCESS;
1333 }
1334
1335
1336 ErrorCode GeomQueryTool::measure_volume( EntityHandle volume, double& result )
1337 {
1338 ErrorCode rval;
1339 std::vector< EntityHandle > surfaces;
1340 result = 0.0;
1341
1342
1343 if( geomTopoTool->is_implicit_complement( volume ) )
1344 {
1345 result = 1.0;
1346 return MB_SUCCESS;
1347 }
1348
1349
1350 rval = MBI->get_child_meshsets( volume, surfaces );MB_CHK_SET_ERR( rval, "Failed to get the volume's child surfaces" );
1351
1352
1353 std::vector< int > senses( surfaces.size() );
1354 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" );
1355
1356 for( unsigned i = 0; i < surfaces.size(); ++i )
1357 {
1358
1359 if( !senses[i] ) continue;
1360
1361
1362 Range triangles;
1363 rval = MBI->get_entities_by_dimension( surfaces[i], 2, triangles );MB_CHK_SET_ERR( rval, "Failed to get the surface triangles" );
1364
1365 if( !triangles.all_of_type( MBTRI ) )
1366 {
1367 std::cout << "WARNING: Surface " << surfaces[i]
1368 << " contains non-triangle elements. Volume calculation may be incorrect." << std::endl;
1369 triangles.clear();
1370 rval = MBI->get_entities_by_type( surfaces[i], MBTRI, triangles );MB_CHK_SET_ERR( rval, "Failed to get the surface triangles" );
1371 }
1372
1373
1374 double surf_sum = 0.0;
1375 const EntityHandle* conn;
1376 int len;
1377 CartVect coords[3];
1378 for( Range::iterator j = triangles.begin(); j != triangles.end(); ++j )
1379 {
1380 rval = MBI->get_connectivity( *j, conn, len, true );MB_CHK_SET_ERR( rval, "Failed to get the connectivity of the current triangle" );
1381 if( 3 != len )
1382 {
1383 MB_SET_ERR( MB_FAILURE, "Incorrect connectivity length for triangle" );
1384 }
1385 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" );
1386
1387 coords[1] -= coords[0];
1388 coords[2] -= coords[0];
1389 surf_sum += ( coords[0] % ( coords[1] * coords[2] ) );
1390 }
1391 result += senses[i] * surf_sum;
1392 }
1393
1394 result /= 6.0;
1395 return MB_SUCCESS;
1396 }
1397
1398
1399 ErrorCode GeomQueryTool::measure_area( EntityHandle surface, double& result )
1400 {
1401
1402 Range triangles;
1403 ErrorCode rval = MBI->get_entities_by_dimension( surface, 2, triangles );MB_CHK_SET_ERR( rval, "Failed to get the surface entities" );
1404 if( !triangles.all_of_type( MBTRI ) )
1405 {
1406 std::cout << "WARNING: Surface " << surface
1407 << " contains non-triangle elements. Area calculation may be incorrect." << std::endl;
1408 triangles.clear();
1409 rval = MBI->get_entities_by_type( surface, MBTRI, triangles );MB_CHK_SET_ERR( rval, "Failed to the surface's triangle entities" );
1410 }
1411
1412
1413 result = 0.0;
1414 const EntityHandle* conn;
1415 int len;
1416 CartVect coords[3];
1417 for( Range::iterator j = triangles.begin(); j != triangles.end(); ++j )
1418 {
1419 rval = MBI->get_connectivity( *j, conn, len, true );MB_CHK_SET_ERR( rval, "Failed to get the current triangle's connectivity" );
1420 if( 3 != len )
1421 {
1422 MB_SET_ERR( MB_FAILURE, "Incorrect connectivity length for triangle" );
1423 }
1424 rval = MBI->get_coords( conn, 3, coords[0].array() );MB_CHK_SET_ERR( rval, "Failed to get the current triangle's vertex coordinates" );
1425
1426
1427 CartVect v1 = coords[1] - coords[0];
1428 CartVect v2 = coords[2] - coords[0];
1429 CartVect xp = v1 * v2;
1430 result += xp.length();
1431 }
1432 result *= 0.5;
1433 return MB_SUCCESS;
1434 }
1435
1436 ErrorCode GeomQueryTool::get_normal( EntityHandle surf,
1437 const double in_pt[3],
1438 double angle[3],
1439 const RayHistory* history )
1440 {
1441 EntityHandle root;
1442 ErrorCode rval = geomTopoTool->get_root( surf, root );MB_CHK_SET_ERR( rval, "Failed to get the surface's obb tree root" );
1443
1444 std::vector< EntityHandle > facets;
1445
1446
1447 if( !history || ( history->prev_facets.size() == 0 ) )
1448 {
1449 rval = geomTopoTool->obb_tree()->closest_to_location( in_pt, root, numericalPrecision, facets );MB_CHK_SET_ERR( rval, "Failed to get closest intersection to location" );
1450 }
1451
1452 else
1453 {
1454 facets.push_back( history->prev_facets.back() );
1455 }
1456
1457 CartVect coords[3], normal( 0.0 );
1458 const EntityHandle* conn;
1459 int len;
1460 for( unsigned i = 0; i < facets.size(); ++i )
1461 {
1462 rval = MBI->get_connectivity( facets[i], conn, len );MB_CHK_SET_ERR( rval, "Failed to get facet connectivity" );
1463 if( 3 != len )
1464 {
1465 MB_SET_ERR( MB_FAILURE, "Incorrect connectivity length for triangle" );
1466 }
1467
1468 rval = MBI->get_coords( conn, 3, coords[0].array() );MB_CHK_SET_ERR( rval, "Failed to get vertex coordinates" );
1469
1470 coords[1] -= coords[0];
1471 coords[2] -= coords[0];
1472 normal += coords[1] * coords[2];
1473 }
1474
1475 normal.normalize();
1476 normal.get( angle );
1477
1478 return MB_SUCCESS;
1479 }
1480
1481
1482
1483
1484
1485
1486
1487
1488 ErrorCode GeomQueryTool::boundary_case( EntityHandle volume,
1489 int& result,
1490 double u,
1491 double v,
1492 double w,
1493 EntityHandle facet,
1494 EntityHandle surface )
1495 {
1496 ErrorCode rval;
1497
1498
1499 if( u <= 1.0 && v <= 1.0 && w <= 1.0 )
1500 {
1501
1502 const CartVect ray_vector( u, v, w );
1503 CartVect coords[3], normal( 0.0 );
1504 const EntityHandle* conn;
1505 int len, sense_out;
1506
1507 rval = MBI->get_connectivity( facet, conn, len );MB_CHK_SET_ERR( rval, "Failed to get the triangle's connectivity" );
1508 if( 3 != len )
1509 {
1510 MB_SET_ERR( MB_FAILURE, "Incorrect connectivity length for triangle" );
1511 }
1512
1513 rval = MBI->get_coords( conn, 3, coords[0].array() );MB_CHK_SET_ERR( rval, "Failed to get vertex coordinates" );
1514
1515 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" );
1516
1517 coords[1] -= coords[0];
1518 coords[2] -= coords[0];
1519 normal = sense_out * ( coords[1] * coords[2] );
1520
1521 double sense = ray_vector % normal;
1522
1523 if( sense < 0.0 )
1524 {
1525 result = 1;
1526 }
1527 else if( sense > 0.0 )
1528 {
1529 result = 0;
1530 }
1531 else if( sense == 0.0 )
1532 {
1533 result = -1;
1534 }
1535 else
1536 {
1537 result = -1;
1538 MB_SET_ERR( MB_FAILURE, "Failed to resolve boundary case" );
1539 }
1540
1541
1542 }
1543 else
1544 {
1545 result = -1;
1546 return MB_SUCCESS;
1547 }
1548
1549 return MB_SUCCESS;
1550 }
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560 ErrorCode GeomQueryTool::poly_solid_angle( EntityHandle face, const CartVect& point, double& area )
1561 {
1562 ErrorCode rval;
1563
1564
1565 const EntityHandle* conn;
1566 int len;
1567 rval = MBI->get_connectivity( face, conn, len, true );MB_CHK_SET_ERR( rval, "Failed to get the connectivity of the polygon" );
1568
1569
1570 CartVect coords_static[4];
1571 std::vector< CartVect > coords_dynamic;
1572 CartVect* coords = coords_static;
1573 if( (unsigned)len > ( sizeof( coords_static ) / sizeof( coords_static[0] ) ) )
1574 {
1575 coords_dynamic.resize( len );
1576 coords = &coords_dynamic[0];
1577 }
1578
1579
1580 rval = MBI->get_coords( conn, len, coords->array() );MB_CHK_SET_ERR( rval, "Failed to get the coordinates of the polygon vertices" );
1581
1582
1583 CartVect norm( 0.0 ), v1, v0 = coords[1] - coords[0];
1584 for( int i = 2; i < len; ++i )
1585 {
1586 v1 = coords[i] - coords[0];
1587 norm += v0 * v1;
1588 v0 = v1;
1589 }
1590
1591
1592 double s, ang;
1593 area = 0.0;
1594 CartVect r, n1, n2, b, a = coords[len - 1] - coords[0];
1595 for( int i = 0; i < len; ++i )
1596 {
1597 r = coords[i] - point;
1598 b = coords[( i + 1 ) % len] - coords[i];
1599 n1 = a * r;
1600 n2 = r * b;
1601 s = ( n1 % n2 ) / ( n1.length() * n2.length() );
1602 ang = s <= -1.0 ? M_PI : s >= 1.0 ? 0.0 : acos( s );
1603 s = ( b * a ) % norm;
1604 area += s > 0.0 ? M_PI - ang : M_PI + ang;
1605 a = -b;
1606 }
1607
1608 area -= M_PI * ( len - 2 );
1609 if( ( norm % r ) > 0 ) area = -area;
1610 return MB_SUCCESS;
1611 }
1612
1613 void GeomQueryTool::set_overlap_thickness( double new_thickness )
1614 {
1615
1616 if( new_thickness < 0 || new_thickness > 100 )
1617 {
1618 std::cerr << "Invalid overlap_thickness = " << new_thickness << std::endl;
1619 }
1620 else
1621 {
1622 overlapThickness = new_thickness;
1623 }
1624 if( verbose ) std::cout << "Set overlap thickness = " << overlapThickness << std::endl;
1625 }
1626
1627 void GeomQueryTool::set_numerical_precision( double new_precision )
1628 {
1629
1630 if( new_precision <= 0 || new_precision > 1 )
1631 {
1632 std::cerr << "Invalid numerical_precision = " << numericalPrecision << std::endl;
1633 }
1634 else
1635 {
1636 numericalPrecision = new_precision;
1637 }
1638 if( verbose ) std::cout << "Set numerical precision = " << numericalPrecision << std::endl;
1639 }
1640
1641 }