MOAB: Mesh Oriented datABase
(version 5.4.1)
|
00001 #include "moab/Core.hpp" 00002 #include "moab/Range.hpp" 00003 #include "moab/OrientedBoxTreeTool.hpp" 00004 #include "moab/OrientedBox.hpp" 00005 #include "MBTagConventions.hpp" 00006 #include "moab/GeomUtil.hpp" 00007 #include "moab/CN.hpp" 00008 00009 #ifdef MOAB_HAVE_MPI 00010 #include "moab_mpi.h" 00011 #endif 00012 00013 #include "../TestUtil.hpp" 00014 #include <iostream> 00015 #include <sstream> 00016 #include <cstdlib> 00017 #include <limits> 00018 #include <cstdio> 00019 #include <set> 00020 #include <map> 00021 00022 using namespace moab; 00023 00024 struct RayTest 00025 { 00026 const char* description; 00027 unsigned expected_hits; 00028 CartVect point, direction; 00029 }; 00030 00031 static const char* NAME = "obb_test"; 00032 00033 std::map< std::string, std::vector< RayTest > > default_files_tests; 00034 typedef std::map< std::string, std::vector< RayTest > >::iterator ray_test_itr; 00035 00036 void initialize_default_files() 00037 { 00038 size_t num_tests; 00039 std::vector< RayTest > tests; 00040 std::string file = STRINGIFY( MESHDIR ) "/3k-tri-sphere.vtk"; 00041 RayTest set1[] = { { "triangle interior ", 1, CartVect( 0, 0, 0 ), CartVect( 99.8792, -5, 0.121729 ) }, 00042 { "triangle edge ", 2, CartVect( 0, 0, 0 ), CartVect( 4.99167, 0, 99.7502 ) }, 00043 { "triangle node ", 6, CartVect( 0, 0, 0 ), CartVect( 0, 0, 100 ) } }; 00044 00045 num_tests = sizeof( set1 ) / sizeof( set1[0] ); 00046 tests.insert( tests.begin(), &set1[0], &set1[num_tests] ); 00047 default_files_tests[file] = tests; 00048 tests.clear(); 00049 00050 #ifdef MOAB_HAVE_HDF5 00051 file = STRINGIFY( MESHDIR ) "/3k-tri-cube.h5m"; 00052 #else 00053 file = STRINGIFY( MESHDIR ) "/3k-tri-cube.vtk"; 00054 #endif 00055 00056 RayTest set2[] = { { "interior triangle interior ", 1, CartVect( 0, 0, 0 ), CartVect( 0, 0, 5 ) }, 00057 { "interior triangle edge ", 2, CartVect( 0, 0, 0 ), CartVect( -0.25, -0.05, 5 ) }, 00058 { "interior triangle node ", 5, CartVect( 0, 0, 0 ), CartVect( -0.3, -0.3, 5 ) }, 00059 { "edge triangle interior ", 1, CartVect( 0, 0, 0 ), CartVect( 5, 2.9, 2.9 ) }, 00060 { "edge triangle edge ", 2, CartVect( 0, 0, 0 ), CartVect( 5, 5, 2.9 ) }, 00061 { "edge triangle node ", 6, CartVect( 0, 0, 0 ), CartVect( 5, 5, 3 ) }, 00062 { "corner triangle interior ", 1, CartVect( 0, 0, 0 ), CartVect( 5, 4.9, 4.9 ) }, 00063 { "corner triangle edge ", 2, CartVect( 0, 0, 0 ), CartVect( 5, 5, 4.9 ) }, 00064 { "corner triangle node ", 3, CartVect( 0, 0, 0 ), CartVect( 5, 5, 5 ) } }; 00065 00066 num_tests = sizeof( set2 ) / sizeof( set2[0] ); 00067 tests.insert( tests.begin(), &set2[0], &set2[num_tests] ); 00068 default_files_tests[file] = tests; 00069 tests.clear(); 00070 } 00071 00072 // Another possible file 00073 // STRINGIFY(MESHDIR) "/4k-tri-plane.vtk", 00074 00075 static void usage( const char* error, const char* opt ) 00076 { 00077 const char* default_message = "Invalid option"; 00078 if( opt && !error ) error = default_message; 00079 00080 std::ostream& str = error ? std::cerr : std::cout; 00081 if( error ) 00082 { 00083 str << error; 00084 if( opt ) str << ": " << opt; 00085 str << std::endl; 00086 } 00087 00088 str << "Usage: " << NAME << " [output opts.] [settings] [file ...]" << std::endl; 00089 str << " " << NAME << " -h" << std::endl; 00090 if( !error ) 00091 str << " If no file is specified the defautl test files will be used" << std::endl 00092 << " -h print help text. " << std::endl 00093 << " -v verbose output (may be specified multiple times) " << std::endl 00094 << " -q quiet (minimal output) " << std::endl 00095 << " -f <x>:<y>:<z>:<i>:<j>:<k> Do ray fire" << std::endl 00096 << " -c write box geometry to Cubit journal file." << std::endl 00097 << " -k write leaf contents to vtk files." << std::endl 00098 << " -K write contents of leaf boxes intersected by rays to vtk file." << std::endl 00099 << " -o <name> Save file containing tree and triangles. Mesh tag \"OBB_ROOT\"." << std::endl 00100 << " -t <real> specify tolerance" << std::endl 00101 << " -n <int> specify max entities per leaf node " << std::endl 00102 << " -l <int> specify max tree levels" << std::endl 00103 << " -r <real> specify worst cell split ratio" << std::endl 00104 << " -R <real> specify best cell split ratio" << std::endl 00105 << " -s force construction of surface tree" << std::endl 00106 << " -S do not build surface tree." << std::endl 00107 << " (Default: surface tree if file contains multiple surfaces" << std::endl 00108 << " -u use unordered (Range) meshsets for tree nodes" << std::endl 00109 << " -U use ordered (vector) meshsets for tree nodes" << std::endl 00110 << " Verbosity (-q sets to 0, each -v increments, default is 1):" << std::endl 00111 << " 0 - no output" << std::endl 00112 << " 1 - status messages and error summary" << std::endl 00113 << " 2 - print tree statistics " << std::endl 00114 << " 3 - output errors for each node" << std::endl 00115 << " 4 - print tree" << std::endl 00116 << " 5 - print tree w/ contents of each node" << std::endl 00117 << " See documentation for OrientedBoxTreeTool::Settings for " << std::endl 00118 << " a description of tree generation settings." << std::endl; 00119 exit( !!error ); 00120 } 00121 00122 static const char* get_option( int& i, int argc, char* argv[] ) 00123 { 00124 ++i; 00125 if( i == argc ) usage( "Expected argument following option", argv[i - 1] ); 00126 return argv[i]; 00127 } 00128 00129 static int get_int_option( int& i, int argc, char* argv[] ) 00130 { 00131 const char* str = get_option( i, argc, argv ); 00132 char* end_ptr; 00133 long val = strtol( str, &end_ptr, 0 ); 00134 if( !*str || *end_ptr ) usage( "Expected integer following option", argv[i - 1] ); 00135 return val; 00136 } 00137 00138 static double get_double_option( int& i, int argc, char* argv[] ) 00139 { 00140 const char* str = get_option( i, argc, argv ); 00141 char* end_ptr; 00142 double val = strtod( str, &end_ptr ); 00143 if( !*str || *end_ptr ) usage( "Expected real number following option", argv[i - 1] ); 00144 return val; 00145 } 00146 00147 static bool do_file( const char* filename ); 00148 00149 enum TriOption 00150 { 00151 DISABLE, 00152 ENABLE, 00153 AUTO 00154 }; 00155 00156 static int verbosity = 1; 00157 static OrientedBoxTreeTool::Settings settings; 00158 static double tolerance = 1e-6; 00159 static bool write_cubit = false; 00160 static bool write_vtk = false; 00161 static bool write_ray_vtk = false; 00162 static std::vector< CartVect > rays; 00163 static const char* save_file_name = 0; 00164 static TriOption surfTree = AUTO; 00165 00166 static void parse_ray( int& i, int argc, char* argv[] ); 00167 00168 int main( int argc, char* argv[] ) 00169 { 00170 #ifdef MOAB_HAVE_MPI 00171 int fail = MPI_Init( &argc, &argv ); 00172 if( fail ) return fail; 00173 #endif 00174 00175 initialize_default_files(); 00176 00177 std::vector< const char* > file_names; 00178 bool flags = true; 00179 for( int i = 1; i < argc; ++i ) 00180 { 00181 if( flags && argv[i][0] == '-' ) 00182 { 00183 if( !argv[i][1] || argv[i][2] ) usage( 0, argv[i] ); 00184 switch( argv[i][1] ) 00185 { 00186 default: 00187 usage( 0, argv[i] ); 00188 break; 00189 case '-': 00190 flags = false; 00191 break; 00192 case 'v': 00193 ++verbosity; 00194 break; 00195 case 'q': 00196 verbosity = 0; 00197 break; 00198 case 'h': 00199 usage( 0, 0 ); 00200 break; 00201 case 'c': 00202 write_cubit = true; 00203 break; 00204 case 'k': 00205 write_vtk = true; 00206 break; 00207 case 'K': 00208 write_ray_vtk = true; 00209 break; 00210 case 's': 00211 surfTree = ENABLE; 00212 break; 00213 case 'S': 00214 surfTree = DISABLE; 00215 break; 00216 case 'u': 00217 settings.set_options = MESHSET_SET; 00218 break; 00219 case 'U': 00220 settings.set_options = MESHSET_ORDERED; 00221 break; 00222 case 'o': 00223 save_file_name = get_option( i, argc, argv ); 00224 break; 00225 case 'n': 00226 settings.max_leaf_entities = get_int_option( i, argc, argv ); 00227 break; 00228 case 'l': 00229 settings.max_depth = get_int_option( i, argc, argv ); 00230 break; 00231 case 'r': 00232 settings.worst_split_ratio = get_double_option( i, argc, argv ); 00233 break; 00234 case 'R': 00235 settings.best_split_ratio = get_double_option( i, argc, argv ); 00236 break; 00237 case 't': 00238 tolerance = get_double_option( i, argc, argv ); 00239 break; 00240 case 'f': 00241 parse_ray( i, argc, argv ); 00242 break; 00243 } 00244 } 00245 else 00246 { 00247 file_names.push_back( argv[i] ); 00248 } 00249 } 00250 00251 if( verbosity ) 00252 { 00253 Core core; 00254 std::string version; 00255 core.impl_version( &version ); 00256 std::cout << version << std::endl; 00257 if( verbosity > 1 ) 00258 std::cout << "max_leaf_entities: " << settings.max_leaf_entities << std::endl 00259 << "max_depth: " << settings.max_depth << std::endl 00260 << "worst_split_ratio: " << settings.worst_split_ratio << std::endl 00261 << "best_split_ratio: " << settings.best_split_ratio << std::endl 00262 << "tolerance: " << tolerance << std::endl 00263 << "set type: " 00264 << ( ( settings.set_options & MESHSET_ORDERED ) ? "ordered" : "set" ) << std::endl 00265 << std::endl; 00266 } 00267 00268 if( !settings.valid() || tolerance < 0.0 ) 00269 { 00270 std::cerr << "Invalid settings specified." << std::endl; 00271 return 2; 00272 } 00273 00274 if( file_names.empty() ) 00275 { 00276 std::cerr << "No file(s) specified." << std::endl; 00277 for( ray_test_itr file = default_files_tests.begin(); file != default_files_tests.end(); file++ ) 00278 { 00279 std::cerr << "Using default file \"" << file->first << '"' << std::endl; 00280 file_names.push_back( file->first.c_str() ); 00281 } 00282 } 00283 00284 if( save_file_name && file_names.size() != 1 ) 00285 { 00286 std::cout << "Only one input file allowed if \"-o\" option is specified." << std::endl; 00287 std::cout << "Only testing with single file " << file_names[0] << std::endl; 00288 file_names.erase( ++file_names.begin(), file_names.end() ); 00289 } 00290 00291 int exit_val = 0; 00292 for( unsigned j = 0; j < file_names.size(); ++j ) 00293 if( !do_file( file_names[j] ) ) ++exit_val; 00294 00295 #ifdef MOAB_HAVE_MPI 00296 fail = MPI_Finalize(); 00297 if( fail ) return fail; 00298 #endif 00299 00300 return exit_val ? exit_val + 2 : 0; 00301 } 00302 00303 static void parse_ray( int& i, int argc, char* argv[] ) 00304 { 00305 CartVect point, direction; 00306 if( 6 != sscanf( get_option( i, argc, argv ), "%lf:%lf:%lf:%lf:%lf:%lf", &point[0], &point[1], &point[2], 00307 &direction[0], &direction[1], &direction[2] ) ) 00308 usage( "Expected ray specified as <x>:<y>:<z>:<i>:<j>:<k>", 0 ); 00309 direction.normalize(); 00310 rays.push_back( point ); 00311 rays.push_back( direction ); 00312 } 00313 00314 class TreeValidator : public OrientedBoxTreeTool::Op 00315 { 00316 private: 00317 Interface* const instance; 00318 OrientedBoxTreeTool* const tool; 00319 const bool printing; 00320 const double epsilon; 00321 bool surfaces; 00322 std::ostream& stream; 00323 OrientedBoxTreeTool::Settings settings; 00324 00325 void print( EntityHandle handle, const char* string ) 00326 { 00327 if( printing ) stream << instance->id_from_handle( handle ) << ": " << string << std::endl; 00328 } 00329 00330 ErrorCode error( EntityHandle handle, const char* string ) 00331 { 00332 ++error_count; 00333 print( handle, string ); 00334 return MB_SUCCESS; 00335 } 00336 00337 public: 00338 unsigned entity_count; 00339 00340 unsigned loose_box_count; 00341 unsigned child_outside_count; 00342 unsigned entity_outside_count; 00343 unsigned num_entities_outside; 00344 unsigned non_ortho_count; 00345 unsigned error_count; 00346 unsigned empty_leaf_count; 00347 unsigned non_empty_non_leaf_count; 00348 unsigned entity_invalid_count; 00349 unsigned unsorted_axis_count; 00350 unsigned non_unit_count; 00351 unsigned duplicate_entity_count; 00352 unsigned bad_outer_radius_count; 00353 unsigned missing_surface_count; 00354 unsigned multiple_surface_count; 00355 std::set< EntityHandle > seen; 00356 int surface_depth; 00357 EntityHandle surface_handle; 00358 00359 TreeValidator( Interface* instance_ptr, 00360 OrientedBoxTreeTool* tool_ptr, 00361 bool print_errors, 00362 std::ostream& str, 00363 double tol, 00364 bool surfs, 00365 OrientedBoxTreeTool::Settings s ) 00366 : instance( instance_ptr ), tool( tool_ptr ), printing( print_errors ), epsilon( tol ), surfaces( surfs ), 00367 stream( str ), settings( s ), entity_count( 0 ), loose_box_count( 0 ), child_outside_count( 0 ), 00368 entity_outside_count( 0 ), num_entities_outside( 0 ), non_ortho_count( 0 ), error_count( 0 ), 00369 empty_leaf_count( 0 ), non_empty_non_leaf_count( 0 ), entity_invalid_count( 0 ), unsorted_axis_count( 0 ), 00370 non_unit_count( 0 ), duplicate_entity_count( 0 ), bad_outer_radius_count( 0 ), missing_surface_count( 0 ), 00371 multiple_surface_count( 0 ), surface_depth( -1 ), surface_handle( 0 ) 00372 { 00373 } 00374 00375 bool is_valid() const 00376 { 00377 return 0 == loose_box_count + child_outside_count + entity_outside_count + num_entities_outside + 00378 non_ortho_count + error_count + empty_leaf_count + non_empty_non_leaf_count + 00379 entity_invalid_count + unsorted_axis_count + non_unit_count + duplicate_entity_count + 00380 bad_outer_radius_count + missing_surface_count + multiple_surface_count; 00381 } 00382 00383 virtual ErrorCode visit( EntityHandle node, int depth, bool& descend ); 00384 00385 virtual ErrorCode leaf( EntityHandle ) 00386 { 00387 return MB_SUCCESS; 00388 } 00389 }; 00390 00391 ErrorCode TreeValidator::visit( EntityHandle node, int depth, bool& descend ) 00392 { 00393 ErrorCode rval; 00394 descend = true; 00395 00396 Range contents; 00397 rval = instance->get_entities_by_handle( node, contents ); 00398 if( MB_SUCCESS != rval ) return error( node, "Error getting contents of tree node. Corrupt tree?" ); 00399 entity_count += contents.size(); 00400 00401 if( surfaces ) 00402 { 00403 // if no longer in subtree for previous surface, clear 00404 if( depth <= surface_depth ) surface_depth = -1; 00405 00406 EntityHandle surface = 0; 00407 Range::iterator surf_iter = contents.lower_bound( MBENTITYSET ); 00408 if( surf_iter != contents.end() ) 00409 { 00410 surface = *surf_iter; 00411 contents.erase( surf_iter ); 00412 } 00413 00414 if( surface ) 00415 { 00416 if( surface_depth >= 0 ) 00417 { 00418 ++multiple_surface_count; 00419 print( node, "Multiple surfaces in encountered in same subtree." ); 00420 } 00421 else 00422 { 00423 surface_depth = depth; 00424 surface_handle = surface; 00425 } 00426 } 00427 } 00428 00429 std::vector< EntityHandle > children; 00430 rval = tool->get_moab_instance()->get_child_meshsets( node, children ); 00431 if( MB_SUCCESS != rval || ( !children.empty() && children.size() != 2 ) ) 00432 return error( node, "Error getting children. Corrupt tree?" ); 00433 00434 OrientedBox box; 00435 rval = tool->box( node, box ); 00436 if( MB_SUCCESS != rval ) return error( node, "Error getting oriented box from tree node. Corrupt tree?" ); 00437 00438 if( children.empty() && contents.empty() ) 00439 { 00440 ++empty_leaf_count; 00441 print( node, "Empty leaf node.\n" ); 00442 } 00443 else if( !children.empty() && !contents.empty() ) 00444 { 00445 ++non_empty_non_leaf_count; 00446 print( node, "Non-leaf node is not empty." ); 00447 } 00448 00449 if( surfaces && children.empty() && surface_depth < 0 ) 00450 { 00451 ++missing_surface_count; 00452 print( node, "Reached leaf node w/out encountering any surface set." ); 00453 } 00454 00455 double dot_epsilon = epsilon * ( box.axis( 0 ) + box.axis( 1 ) + box.axis( 2 ) ).length(); 00456 if( box.axis( 0 ) % box.axis( 1 ) > dot_epsilon || box.axis( 0 ) % box.axis( 2 ) > dot_epsilon || 00457 box.axis( 1 ) % box.axis( 2 ) > dot_epsilon ) 00458 { 00459 ++non_ortho_count; 00460 print( node, "Box axes are not orthogonal" ); 00461 } 00462 00463 if( !children.empty() ) 00464 { 00465 for( int i = 0; i < 2; ++i ) 00466 { 00467 OrientedBox other_box; 00468 rval = tool->box( children[i], other_box ); 00469 if( MB_SUCCESS != rval ) 00470 return error( children[i], " Error getting oriented box from tree node. Corrupt tree?" ); 00471 // else if (!box.contained( other_box, epsilon )) { 00472 // ++child_outside_count; 00473 // print( children[i], "Parent box does not contain child box." ); 00474 // char string[64]; 00475 // sprintf(string, " Volume ratio is %f", other_box.volume()/box.volume() ); 00476 // print( children [i], string ); 00477 // } 00478 else 00479 { 00480 double vol_ratio = other_box.volume() / box.volume(); 00481 if( vol_ratio > 2.0 ) 00482 { 00483 char string[64]; 00484 sprintf( string, "child/parent volume ratio is %f", vol_ratio ); 00485 print( children[i], string ); 00486 sprintf( string, " child/parent area ratio is %f", other_box.area() / box.area() ); 00487 print( children[i], string ); 00488 } 00489 } 00490 } 00491 } 00492 00493 bool bad_element = false; 00494 bool bad_element_handle = false; 00495 bool bad_element_conn = false; 00496 bool duplicate_element = false; 00497 int num_outside = 0; 00498 bool boundary[6] = { false, false, false, false, false, false }; 00499 for( Range::iterator it = contents.begin(); it != contents.end(); ++it ) 00500 { 00501 EntityType type = instance->type_from_handle( *it ); 00502 int dim = CN::Dimension( type ); 00503 if( dim != 2 ) 00504 { 00505 bad_element = true; 00506 continue; 00507 } 00508 00509 const EntityHandle* conn; 00510 int conn_len; 00511 rval = instance->get_connectivity( *it, conn, conn_len ); 00512 if( MB_SUCCESS != rval ) 00513 { 00514 bad_element_handle = true; 00515 continue; 00516 } 00517 00518 std::vector< CartVect > coords( conn_len ); 00519 rval = instance->get_coords( conn, conn_len, coords[0].array() ); 00520 if( MB_SUCCESS != rval ) 00521 { 00522 bad_element_conn = true; 00523 continue; 00524 } 00525 00526 bool outside = false; 00527 for( std::vector< CartVect >::iterator j = coords.begin(); j != coords.end(); ++j ) 00528 { 00529 if( !box.contained( *j, epsilon ) ) 00530 outside = true; 00531 else 00532 for( int d = 0; d < 3; ++d ) 00533 { 00534 #if MB_ORIENTED_BOX_UNIT_VECTORS 00535 double n = box.axis( d ) % ( *j - box.center ); 00536 if( fabs( n - box.length[d] ) <= epsilon ) boundary[2 * d] = true; 00537 if( fabs( n + box.length[d] ) <= epsilon ) boundary[2 * d + 1] = true; 00538 #else 00539 double ln = box.axis( d ).length(); 00540 CartVect v1 = *j - box.center - box.axis[d]; 00541 CartVect v2 = *j - box.center + box.axis[d]; 00542 if( fabs( v1 % box.axis[d] ) <= ln * epsilon ) boundary[2 * d] = true; 00543 if( fabs( v2 % box.axis[d] ) <= ln * epsilon ) boundary[2 * d + 1] = true; 00544 #endif 00545 } 00546 } 00547 if( outside ) ++num_outside; 00548 00549 if( !seen.insert( *it ).second ) 00550 { 00551 duplicate_element = true; 00552 ++duplicate_entity_count; 00553 } 00554 } 00555 00556 CartVect alength( box.axis( 0 ).length(), box.axis( 1 ).length(), box.axis( 2 ).length() ); 00557 #if MB_ORIENTED_BOX_UNIT_VECTORS 00558 CartVect length = box.length; 00559 #else 00560 CartVect length = alength; 00561 #endif 00562 00563 if( length[0] > length[1] || length[0] > length[2] || length[1] > length[2] ) 00564 { 00565 ++unsorted_axis_count; 00566 print( node, "Box axes are not ordered from shortest to longest." ); 00567 } 00568 00569 #if MB_ORIENTED_BOX_UNIT_VECTORS 00570 if( fabs( alength[0] - 1.0 ) > epsilon || fabs( alength[1] - 1.0 ) > epsilon || fabs( alength[2] - 1.0 ) > epsilon ) 00571 { 00572 ++non_unit_count; 00573 print( node, "Box axes are not unit vectors." ); 00574 } 00575 #endif 00576 00577 #if MB_ORIENTED_BOX_OUTER_RADIUS 00578 if( fabs( length.length() - box.radius ) > tolerance ) 00579 { 00580 ++bad_outer_radius_count; 00581 print( node, "Box has incorrect outer radius." ); 00582 } 00583 #endif 00584 00585 if( depth + 1 < settings.max_depth && contents.size() > (unsigned)( 4 * settings.max_leaf_entities ) ) 00586 { 00587 char string[64]; 00588 sprintf( string, "leaf at depth %d with %u entities", depth, (unsigned)contents.size() ); 00589 print( node, string ); 00590 } 00591 00592 bool all_boundaries = true; 00593 for( int f = 0; f < 6; ++f ) 00594 all_boundaries = all_boundaries && boundary[f]; 00595 00596 if( bad_element ) 00597 { 00598 ++entity_invalid_count; 00599 print( node, "Set contained an entity with an inappropriate dimension." ); 00600 } 00601 if( bad_element_handle ) 00602 { 00603 ++error_count; 00604 print( node, "Error querying face contained in set." ); 00605 } 00606 if( bad_element_conn ) 00607 { 00608 ++error_count; 00609 print( node, "Error querying connectivity of element." ); 00610 } 00611 if( duplicate_element ) 00612 { 00613 print( node, "Elements occur in multiple leaves of tree." ); 00614 } 00615 if( num_outside > 0 ) 00616 { 00617 ++entity_outside_count; 00618 num_entities_outside += num_outside; 00619 if( printing ) 00620 stream << instance->id_from_handle( node ) << ": " << num_outside << " elements outside box." << std::endl; 00621 } 00622 else if( !all_boundaries && !contents.empty() ) 00623 { 00624 ++loose_box_count; 00625 print( node, "Box does not fit contained elements tightly." ); 00626 } 00627 00628 return MB_SUCCESS; 00629 } 00630 00631 class CubitWriter : public OrientedBoxTreeTool::Op 00632 { 00633 public: 00634 CubitWriter( FILE* file_ptr, OrientedBoxTreeTool* tool_ptr ) : file( file_ptr ), tool( tool_ptr ) {} 00635 00636 ErrorCode visit( EntityHandle node, int depth, bool& descend ); 00637 ErrorCode leaf( EntityHandle ) 00638 { 00639 return MB_SUCCESS; 00640 } 00641 00642 private: 00643 FILE* file; 00644 OrientedBoxTreeTool* tool; 00645 }; 00646 00647 ErrorCode CubitWriter::visit( EntityHandle node, int, bool& descend ) 00648 { 00649 descend = true; 00650 OrientedBox box; 00651 ErrorCode rval = tool->box( node, box ); 00652 if( rval != MB_SUCCESS ) return rval; 00653 00654 double sign[] = { -1, 1 }; 00655 for( int i = 0; i < 2; ++i ) 00656 for( int j = 0; j < 2; ++j ) 00657 for( int k = 0; k < 2; ++k ) 00658 { 00659 #if MB_ORIENTED_BOX_UNIT_VECTORS 00660 CartVect corner = box.center + box.length[0] * sign[i] * box.axis( 0 ) + 00661 box.length[1] * sign[j] * box.axis( 1 ) + box.length[2] * sign[k] * box.axis( 2 ); 00662 #else 00663 CartVect corner = 00664 box.center + sign[i] * box.axis( 0 ) + sign[j] * box.axis( 1 ) + sign[k] * box.axis( 2 ); 00665 #endif 00666 fprintf( file, "create vertex %f %f %f\n", corner[0], corner[1], corner[2] ); 00667 } 00668 fprintf( file, "#{i=Id(\"vertex\")-7}\n" ); 00669 fprintf( file, "create surface vertex {i } {i+1} {i+3} {i+2}\n" ); 00670 fprintf( file, "create surface vertex {i+4} {i+5} {i+7} {i+6}\n" ); 00671 fprintf( file, "create surface vertex {i+1} {i+0} {i+4} {i+5}\n" ); 00672 fprintf( file, "create surface vertex {i+3} {i+2} {i+6} {i+7}\n" ); 00673 fprintf( file, "create surface vertex {i+2} {i+0} {i+4} {i+6}\n" ); 00674 fprintf( file, "create surface vertex {i+3} {i+1} {i+5} {i+7}\n" ); 00675 fprintf( file, "delete vertex {i}-{i+7}\n" ); 00676 fprintf( file, "#{s=Id(\"surface\")-5}\n" ); 00677 fprintf( file, "create volume surface {s} {s+1} {s+2} {s+3} {s+4} {s+5} noheal\n" ); 00678 int id = tool->get_moab_instance()->id_from_handle( node ); 00679 fprintf( file, "volume {Id(\"volume\")} name \"cell%d\"\n", id ); 00680 00681 return MB_SUCCESS; 00682 } 00683 00684 class VtkWriter : public OrientedBoxTreeTool::Op 00685 { 00686 public: 00687 VtkWriter( std::string base_name, Interface* interface ) : baseName( base_name ), instance( interface ) {} 00688 00689 ErrorCode visit( EntityHandle node, int depth, bool& descend ); 00690 00691 ErrorCode leaf( EntityHandle /*node*/ ) 00692 { 00693 return MB_SUCCESS; 00694 } 00695 00696 private: 00697 std::string baseName; 00698 Interface* instance; 00699 }; 00700 00701 ErrorCode VtkWriter::visit( EntityHandle node, int, bool& descend ) 00702 { 00703 descend = true; 00704 00705 ErrorCode rval; 00706 int count; 00707 rval = instance->get_number_entities_by_handle( node, count ); 00708 if( MB_SUCCESS != rval || 0 == count ) return rval; 00709 00710 int id = instance->id_from_handle( node ); 00711 char id_str[32]; 00712 sprintf( id_str, "%d", id ); 00713 00714 std::string file_name( baseName ); 00715 file_name += "."; 00716 file_name += id_str; 00717 file_name += ".vtk"; 00718 00719 return instance->write_mesh( file_name.c_str(), &node, 1 ); 00720 } 00721 00722 static bool do_ray_fire_test( OrientedBoxTreeTool& tool, 00723 EntityHandle root_set, 00724 const char* filename, 00725 bool have_surface_tree ); 00726 00727 static bool do_closest_point_test( OrientedBoxTreeTool& tool, EntityHandle root_set, bool have_surface_tree ); 00728 00729 static ErrorCode save_tree( Interface* instance, const char* filename, EntityHandle tree_root ); 00730 00731 static bool do_file( const char* filename ) 00732 { 00733 ErrorCode rval; 00734 Core instance; 00735 Interface* const iface = &instance; 00736 OrientedBoxTreeTool tool( iface ); 00737 bool haveSurfTree = false; 00738 00739 if( verbosity ) std::cout << filename << std::endl << "------" << std::endl; 00740 00741 rval = iface->load_mesh( filename ); 00742 if( MB_SUCCESS != rval ) 00743 { 00744 if( verbosity ) std::cout << "Failed to read file: \"" << filename << '"' << std::endl; 00745 return false; 00746 } 00747 00748 // IF building from surfaces, get surfaces. 00749 // If AUTO and less than two surfaces, don't build from surfaces. 00750 Range surfaces; 00751 if( surfTree != DISABLE ) 00752 { 00753 Tag surftag; 00754 rval = iface->tag_get_handle( GEOM_DIMENSION_TAG_NAME, 1, MB_TYPE_INTEGER, surftag ); 00755 if( MB_SUCCESS == rval ) 00756 { 00757 int dim = 2; 00758 const void* tagvalues[] = { &dim }; 00759 rval = iface->get_entities_by_type_and_tag( 0, MBENTITYSET, &surftag, tagvalues, 1, surfaces ); 00760 if( MB_SUCCESS != rval && MB_ENTITY_NOT_FOUND != rval ) return false; 00761 } 00762 else if( MB_TAG_NOT_FOUND != rval ) 00763 return false; 00764 00765 if( ENABLE == surfTree && surfaces.empty() ) 00766 { 00767 std::cerr << "No Surfaces found." << std::endl; 00768 return false; 00769 } 00770 00771 haveSurfTree = ( ENABLE == surfTree ) || ( surfaces.size() > 1 ); 00772 } 00773 00774 EntityHandle root; 00775 Range entities; 00776 if( !haveSurfTree ) 00777 { 00778 rval = iface->get_entities_by_dimension( 0, 2, entities ); 00779 if( MB_SUCCESS != rval ) 00780 { 00781 std::cerr << "get_entities_by_dimension( 2 ) failed." << std::endl; 00782 return false; 00783 } 00784 00785 if( entities.empty() ) 00786 { 00787 if( verbosity ) std::cout << "File \"" << filename << "\" contains no 2D elements" << std::endl; 00788 return false; 00789 } 00790 00791 if( verbosity ) std::cout << "Building tree from " << entities.size() << " 2D elements" << std::endl; 00792 00793 rval = tool.build( entities, root, &settings ); 00794 if( MB_SUCCESS != rval ) 00795 { 00796 if( verbosity ) std::cout << "Failed to build tree." << std::endl; 00797 return false; 00798 } 00799 } 00800 else 00801 { 00802 00803 if( verbosity ) std::cout << "Building tree from " << surfaces.size() << " surfaces" << std::endl; 00804 00805 // Build subtree for each surface, get list of all entities to use later 00806 Range surf_trees, surf_tris; 00807 EntityHandle surf_root; 00808 for( Range::iterator s = surfaces.begin(); s != surfaces.end(); ++s ) 00809 { 00810 surf_tris.clear(); 00811 rval = iface->get_entities_by_dimension( *s, 2, surf_tris ); 00812 if( MB_SUCCESS != rval ) return false; 00813 rval = tool.build( surf_tris, surf_root, &settings ); 00814 if( MB_SUCCESS != rval ) 00815 { 00816 if( verbosity ) std::cout << "Failed to build tree for surface." << std::endl; 00817 return false; 00818 } 00819 surf_trees.insert( surf_root ); 00820 entities.merge( surf_tris ); 00821 rval = iface->add_entities( surf_root, &*s, 1 ); 00822 if( MB_SUCCESS != rval ) return false; 00823 } 00824 00825 rval = tool.join_trees( surf_trees, root, &settings ); 00826 if( MB_SUCCESS != rval ) 00827 { 00828 if( verbosity ) std::cout << "Failed to build tree." << std::endl; 00829 return false; 00830 } 00831 00832 if( verbosity ) 00833 std::cout << "Built tree from " << surfaces.size() << " surfaces" 00834 << " (" << entities.size() - surfaces.size() << " elements)" << std::endl; 00835 00836 entities.merge( surfaces ); 00837 } 00838 00839 if( write_cubit ) 00840 { 00841 std::string name = filename; 00842 name += ".boxes.jou"; 00843 FILE* ptr = fopen( name.c_str(), "w+" ); 00844 if( !ptr ) 00845 { 00846 perror( name.c_str() ); 00847 } 00848 else 00849 { 00850 if( verbosity ) std::cout << "Writing: " << name << std::endl; 00851 fprintf( ptr, "graphics off\n" ); 00852 CubitWriter op( ptr, &tool ); 00853 tool.preorder_traverse( root, op ); 00854 fprintf( ptr, "graphics on\n" ); 00855 fclose( ptr ); 00856 } 00857 } 00858 00859 if( write_vtk ) 00860 { 00861 VtkWriter op( filename, iface ); 00862 if( verbosity ) 00863 std::cout << "Writing leaf contents as : " << filename << ".xxx.vtk where 'xxx' is the set id" << std::endl; 00864 tool.preorder_traverse( root, op ); 00865 } 00866 00867 bool print_errors = ( verbosity > 2 ), print_contents = ( verbosity > 4 ); 00868 if( verbosity > 3 ) tool.print( root, std::cout, print_contents ); 00869 if( verbosity > 1 ) 00870 { 00871 rval = tool.stats( root, std::cout ); 00872 if( MB_SUCCESS != rval ) std::cout << "****** Failed to get tree statistics ******" << std::endl; 00873 } 00874 00875 TreeValidator op( iface, &tool, print_errors, std::cout, tolerance, haveSurfTree, settings ); 00876 rval = tool.preorder_traverse( root, op ); 00877 bool result = op.is_valid(); 00878 if( MB_SUCCESS != rval ) 00879 { 00880 result = false; 00881 if( verbosity ) std::cout << "Errors traversing tree. Corrupt tree?" << std::endl; 00882 } 00883 00884 bool missing = ( op.entity_count != entities.size() ); 00885 if( missing ) result = false; 00886 00887 if( verbosity ) 00888 { 00889 if( result ) 00890 std::cout << std::endl << "No errors detected." << std::endl; 00891 else 00892 std::cout << std::endl << "*********************** ERROR SUMMARY **********************" << std::endl; 00893 if( op.child_outside_count ) 00894 std::cout << "* " << op.child_outside_count << " child boxes not contained in parent." << std::endl; 00895 if( op.entity_outside_count ) 00896 std::cout << "* " << op.entity_outside_count << " nodes containing entities outside of box." << std::endl; 00897 if( op.num_entities_outside ) 00898 std::cout << "* " << op.num_entities_outside << " entities outside boxes." << std::endl; 00899 if( op.empty_leaf_count ) std::cout << "* " << op.empty_leaf_count << " empty leaf nodes." << std::endl; 00900 if( op.non_empty_non_leaf_count ) 00901 std::cout << "* " << op.non_empty_non_leaf_count << " non-leaf nodes containing entities." << std::endl; 00902 if( op.duplicate_entity_count ) 00903 std::cout << "* " << op.duplicate_entity_count << " duplicate entities in leaves." << std::endl; 00904 if( op.missing_surface_count ) 00905 std::cout << "* " << op.missing_surface_count << " leaves outside surface subtrees." << std::endl; 00906 if( op.multiple_surface_count ) 00907 std::cout << "* " << op.multiple_surface_count << " surfaces within other surface subtrees." << std::endl; 00908 if( op.non_ortho_count ) 00909 std::cout << "* " << op.non_ortho_count << " boxes with non-orthononal axes." << std::endl; 00910 if( op.non_unit_count ) std::cout << "* " << op.non_unit_count << " boxes with non-unit axes." << std::endl; 00911 if( op.bad_outer_radius_count ) 00912 std::cout << "* " << op.bad_outer_radius_count << " boxes incorrect outer radius." << std::endl; 00913 if( op.unsorted_axis_count ) 00914 std::cout << "* " << op.unsorted_axis_count << " boxes axes in unsorted order." << std::endl; 00915 if( op.loose_box_count ) 00916 std::cout << "* " << op.loose_box_count << " boxes that do not fit the contained entities tightly." 00917 << std::endl; 00918 if( op.error_count + op.entity_invalid_count ) 00919 std::cout << "* " << op.error_count + op.entity_invalid_count << " other errors while traversing tree." 00920 << std::endl; 00921 if( missing ) 00922 std::cout << "* tree built from " << entities.size() << " entities contains " << op.entity_count 00923 << " entities." << std::endl; 00924 if( !result ) std::cout << "************************************************************" << std::endl; 00925 } 00926 00927 if( result && save_file_name ) 00928 { 00929 if( MB_SUCCESS == save_tree( iface, save_file_name, root ) ) 00930 std::cerr << "Wrote '" << save_file_name << "'" << std::endl; 00931 else 00932 std::cerr << "FAILED TO WRITE '" << save_file_name << "'" << std::endl; 00933 } 00934 00935 if( !do_ray_fire_test( tool, root, filename, haveSurfTree ) ) 00936 { 00937 if( verbosity ) std::cout << "Ray fire test failed." << std::endl; 00938 result = false; 00939 } 00940 00941 if( !do_closest_point_test( tool, root, haveSurfTree ) ) 00942 { 00943 if( verbosity ) std::cout << "Closest point test failed" << std::endl; 00944 result = false; 00945 } 00946 00947 rval = tool.delete_tree( root ); 00948 if( MB_SUCCESS != rval ) 00949 { 00950 if( verbosity ) std::cout << "delete_tree failed." << std::endl; 00951 result = false; 00952 } 00953 00954 return result; 00955 } 00956 00957 static void count_non_tol( std::vector< double > intersections, int& non_tol_count, double& non_tol_dist ) 00958 { 00959 00960 for( size_t i = 0; i < intersections.size(); ++i ) 00961 { 00962 if( intersections[i] > tolerance ) 00963 { 00964 ++non_tol_count; 00965 if( intersections[i] < non_tol_dist ) non_tol_dist = intersections[i]; 00966 } 00967 } 00968 } 00969 00970 static bool check_ray_intersect_tris( OrientedBoxTreeTool& tool, 00971 EntityHandle root_set, 00972 RayTest& test, 00973 int& non_tol_count, 00974 double& non_tol_dist, 00975 OrientedBoxTreeTool::TrvStats& stats ) 00976 { 00977 ErrorCode rval; 00978 bool result = true; 00979 00980 non_tol_dist = std::numeric_limits< double >::max(); 00981 non_tol_count = 0; 00982 00983 std::vector< double > intersections; 00984 std::vector< EntityHandle > facets; 00985 rval = tool.ray_intersect_triangles( intersections, facets, root_set, tolerance, test.point.array(), 00986 test.direction.array(), 0, &stats ); 00987 if( MB_SUCCESS != rval ) 00988 { 00989 if( verbosity ) std::cout << " Call to OrientedBoxTreeTool::ray_intersect_triangles failed." << std::endl; 00990 result = false; 00991 } 00992 else 00993 { 00994 00995 if( intersections.size() != test.expected_hits ) 00996 { 00997 if( verbosity > 2 ) 00998 std::cout << " Expected " << test.expected_hits << " and got " << intersections.size() 00999 << " hits for ray fire of " << test.description << std::endl; 01000 if( verbosity > 3 ) 01001 { 01002 for( unsigned j = 0; j < intersections.size(); ++j ) 01003 std::cout << " " << intersections[j]; 01004 std::cout << std::endl; 01005 } 01006 result = false; 01007 } 01008 01009 count_non_tol( intersections, non_tol_count, non_tol_dist ); 01010 } 01011 return result; 01012 } 01013 01014 static bool check_ray_intersect_sets( OrientedBoxTreeTool& tool, 01015 EntityHandle root_set, 01016 RayTest& test, 01017 int& non_tol_count, 01018 double& non_tol_dist, 01019 OrientedBoxTreeTool::TrvStats& stats ) 01020 { 01021 01022 ErrorCode rval; 01023 bool result = true; 01024 01025 non_tol_dist = std::numeric_limits< double >::max(); 01026 non_tol_count = 0; 01027 01028 const int NUM_NON_TOL_INT = 1; 01029 01030 std::vector< double > intersections; 01031 std::vector< EntityHandle > surfs; 01032 std::vector< EntityHandle > facets; 01033 01034 OrientedBoxTreeTool::IntersectSearchWindow search_win; 01035 OrientedBoxTreeTool::IntRegCtxt int_reg_ctxt; 01036 rval = tool.ray_intersect_sets( intersections, surfs, facets, root_set, tolerance, test.point.array(), 01037 test.direction.array(), search_win, int_reg_ctxt, &stats ); 01038 01039 if( MB_SUCCESS != rval ) 01040 { 01041 if( verbosity ) std::cout << " Call to OrientedBoxTreeTool::ray_intersect_sets failed." << std::endl; 01042 result = false; 01043 } 01044 else 01045 { 01046 01047 if( surfs.size() != intersections.size() ) 01048 { 01049 if( verbosity ) std::cout << " ray_intersect_sets did not return sets for all intersections." << std::endl; 01050 result = false; 01051 } 01052 01053 count_non_tol( intersections, non_tol_count, non_tol_dist ); 01054 01055 if( non_tol_count > NUM_NON_TOL_INT ) 01056 { 01057 if( verbosity ) 01058 std::cout << " Requested " << NUM_NON_TOL_INT << "intersections " 01059 << " beyond tolerance. Got " << non_tol_count << std::endl; 01060 result = false; 01061 } 01062 } 01063 01064 return result; 01065 } 01066 01067 static bool do_ray_fire_test( OrientedBoxTreeTool& tool, 01068 EntityHandle root_set, 01069 const char* filename, 01070 bool haveSurfTree ) 01071 { 01072 if( verbosity > 1 ) std::cout << "beginning ray fire tests" << std::endl; 01073 01074 OrientedBox box; 01075 ErrorCode rval = tool.box( root_set, box ); 01076 if( MB_SUCCESS != rval ) 01077 { 01078 if( verbosity ) std::cerr << "Error getting box for tree root set" << std::endl; 01079 return false; 01080 } 01081 01082 /* Do standard ray fire tests */ 01083 std::cout << box << std::endl; 01084 01085 CartVect origin( 0., 0., 0. ); 01086 CartVect unitDiag( 1., 1., 1. ); 01087 std::vector< RayTest > tests; 01088 RayTest default_tests[] = { { "large axis through box", 2, box.center - 1.2 * box.scaled_axis( 2 ), box.axis( 2 ) }, 01089 { "small axis through box", 2, box.center - 1.2 * box.scaled_axis( 0 ), box.axis( 0 ) }, 01090 { "parallel miss", 0, box.center + 2.0 * box.scaled_axis( 1 ), box.axis( 2 ) }, 01091 { "skew miss", 0, box.center + box.dimensions(), box.dimensions() * box.axis( 2 ) } }; 01092 const size_t num_def_test = sizeof( default_tests ) / sizeof( default_tests[0] ); 01093 tests.insert( tests.begin(), &default_tests[0], &default_tests[0] + num_def_test ); 01094 tests.insert( tests.end(), default_files_tests[filename].begin(), default_files_tests[filename].end() ); 01095 01096 OrientedBoxTreeTool::TrvStats stats; 01097 01098 bool result = true; 01099 const size_t num_test = tests.size(); 01100 for( size_t i = 0; i < num_test; ++i ) 01101 { 01102 tests[i].direction.normalize(); 01103 if( verbosity > 2 ) 01104 { 01105 std::cout << ( 0 == i ? "** Common tests\n" : ( num_def_test == i ? "** File-specific tests\n" : "" ) ); 01106 std::cout << " " << tests[i].description << " " << tests[i].point << " " << tests[i].direction 01107 << std::endl; 01108 } 01109 01110 int rit_non_tol_count = 0; 01111 double rit_non_tol_dist = std::numeric_limits< double >::max(); 01112 if( !check_ray_intersect_tris( tool, root_set, tests[i], rit_non_tol_count, rit_non_tol_dist, stats ) ) 01113 { 01114 result = false; 01115 continue; 01116 } 01117 01118 if( !haveSurfTree ) continue; 01119 01120 int ris_non_tol_count = 0; 01121 double ris_non_tol_dist = std::numeric_limits< double >::max(); 01122 if( !check_ray_intersect_sets( tool, root_set, tests[i], ris_non_tol_count, ris_non_tol_dist, stats ) ) 01123 { 01124 result = false; 01125 continue; 01126 } 01127 01128 if( !rit_non_tol_count && ris_non_tol_count ) 01129 { 01130 if( verbosity ) 01131 std::cout << " ray_intersect_sets returned intersection not found by " 01132 "ray_intersect_triangles" 01133 << std::endl; 01134 result = false; 01135 continue; 01136 } 01137 else if( rit_non_tol_count && !ris_non_tol_count ) 01138 { 01139 if( verbosity ) 01140 std::cout << " ray_intersect_sets missed intersection found by ray_intersect_triangles" << std::endl; 01141 result = false; 01142 continue; 01143 } 01144 else if( rit_non_tol_count && ris_non_tol_count && fabs( rit_non_tol_dist - ris_non_tol_dist ) > tolerance ) 01145 { 01146 if( verbosity ) 01147 std::cout << " ray_intersect_sets and ray_intersect_triangles did not find same " 01148 "closest intersection" 01149 << std::endl; 01150 result = false; 01151 } 01152 } 01153 01154 /* Do ray fire for any user-specified rays */ 01155 01156 for( size_t i = 0; i < rays.size(); i += 2 ) 01157 { 01158 std::cout << rays[i] << "+" << rays[i + 1] << " : "; 01159 01160 if( !haveSurfTree ) 01161 { 01162 Range leaves; 01163 std::vector< double > intersections; 01164 std::vector< EntityHandle > intersection_facets; 01165 rval = tool.ray_intersect_boxes( leaves, root_set, tolerance, rays[i].array(), rays[i + 1].array(), 0, 01166 &stats ); 01167 if( MB_SUCCESS != rval ) 01168 { 01169 std::cout << "FAILED" << std::endl; 01170 result = false; 01171 continue; 01172 } 01173 01174 if( !leaves.empty() && write_ray_vtk ) 01175 { 01176 std::string num, name( filename ); 01177 std::stringstream s; 01178 s << ( i / 2 ); 01179 s >> num; 01180 name += "-ray"; 01181 name += num; 01182 name += ".vtk"; 01183 01184 std::vector< EntityHandle > sets( leaves.size() ); 01185 std::copy( leaves.begin(), leaves.end(), sets.begin() ); 01186 tool.get_moab_instance()->write_mesh( name.c_str(), &sets[0], sets.size() ); 01187 if( verbosity ) std::cout << "(Wrote " << name << ") "; 01188 } 01189 01190 rval = tool.ray_intersect_triangles( intersections, intersection_facets, leaves, tolerance, rays[i].array(), 01191 rays[i + 1].array(), 0 ); 01192 if( MB_SUCCESS != rval ) 01193 { 01194 std::cout << "FAILED" << std::endl; 01195 result = false; 01196 continue; 01197 } 01198 01199 if( intersections.empty() ) 01200 { 01201 std::cout << "(none)" << std::endl; 01202 continue; 01203 } 01204 01205 std::cout << intersections[0]; 01206 for( unsigned j = 1; j < intersections.size(); ++j ) 01207 std::cout << ", " << intersections[j]; 01208 std::cout << std::endl; 01209 01210 if( !leaves.empty() && write_cubit && verbosity > 2 ) 01211 { 01212 std::cout << " intersected boxes:"; 01213 for( Range::iterator i2 = leaves.begin(); i2 != leaves.end(); ++i2 ) 01214 std::cout << " " << tool.get_moab_instance()->id_from_handle( *i2 ); 01215 std::cout << std::endl; 01216 } 01217 } 01218 else 01219 { 01220 std::vector< double > intersections; 01221 std::vector< EntityHandle > surfaces; 01222 std::vector< EntityHandle > facets; 01223 01224 OrientedBoxTreeTool::IntersectSearchWindow search_win; 01225 OrientedBoxTreeTool::IntRegCtxt int_reg_ctxt; 01226 rval = tool.ray_intersect_sets( intersections, surfaces, facets, root_set, tolerance, rays[i].array(), 01227 rays[i + 1].array(), search_win, int_reg_ctxt, &stats ); 01228 01229 if( MB_SUCCESS != rval ) 01230 { 01231 if( verbosity ) std::cout << " Call to OrientedBoxTreeTool::ray_intersect_sets failed." << std::endl; 01232 result = false; 01233 continue; 01234 } 01235 01236 if( !surfaces.empty() && write_ray_vtk ) 01237 { 01238 std::string num, name( filename ); 01239 std::stringstream s; 01240 s << ( i / 2 ); 01241 s >> num; 01242 name += "-ray"; 01243 name += num; 01244 name += ".vtk"; 01245 01246 tool.get_moab_instance()->write_mesh( name.c_str(), &surfaces[0], surfaces.size() ); 01247 if( verbosity ) std::cout << "(Wrote " << name << ") "; 01248 } 01249 01250 if( intersections.size() != surfaces.size() ) 01251 { 01252 std::cout << "Mismatched output lists." << std::endl; 01253 result = false; 01254 continue; 01255 } 01256 01257 if( intersections.empty() ) 01258 { 01259 std::cout << "(none)" << std::endl; 01260 continue; 01261 } 01262 01263 Tag idtag = tool.get_moab_instance()->globalId_tag(); 01264 std::vector< int > ids( surfaces.size() ); 01265 rval = tool.get_moab_instance()->tag_get_data( idtag, &surfaces[0], surfaces.size(), &ids[0] ); 01266 if( MB_SUCCESS != rval ) 01267 { 01268 std::cout << "NO GLOBAL_ID TAG ON SURFACE." << std::endl; 01269 continue; 01270 } 01271 01272 // group by surfaces 01273 std::map< int, double > intmap; 01274 for( unsigned j = 0; j < intersections.size(); ++j ) 01275 intmap[ids[j]] = intersections[j]; 01276 01277 std::map< int, double >::iterator it = intmap.begin(); 01278 int prevsurf = it->first; 01279 std::cout << "Surf" << it->first << " " << it->second; 01280 for( ++it; it != intmap.end(); ++it ) 01281 { 01282 std::cout << ", "; 01283 if( it->first != prevsurf ) 01284 { 01285 prevsurf = it->first; 01286 std::cout << "Surf" << it->first << " "; 01287 } 01288 std::cout << it->second; 01289 } 01290 std::cout << std::endl; 01291 } 01292 } 01293 01294 if( verbosity > 1 ) 01295 { 01296 std::cout << "Traversal statistics for ray fire tests: " << std::endl; 01297 stats.print( std::cout ); 01298 } 01299 01300 return result; 01301 } 01302 01303 static ErrorCode save_tree( Interface* instance, const char* filename, EntityHandle tree_root ) 01304 { 01305 ErrorCode rval; 01306 Tag tag; 01307 01308 rval = instance->tag_get_handle( "OBB_ROOT", 1, MB_TYPE_HANDLE, tag, MB_TAG_SPARSE | MB_TAG_CREAT ); 01309 if( MB_SUCCESS != rval ) return rval; 01310 01311 const EntityHandle root = 0; 01312 rval = instance->tag_set_data( tag, &root, 1, &tree_root ); 01313 if( MB_SUCCESS != rval ) return rval; 01314 01315 return instance->write_mesh( filename ); 01316 } 01317 01318 static ErrorCode tri_coords( Interface* moab, EntityHandle tri, CartVect coords[3] ) 01319 { 01320 ErrorCode rval; 01321 const EntityHandle* conn; 01322 int len; 01323 01324 rval = moab->get_connectivity( tri, conn, len, true ); 01325 if( MB_SUCCESS != rval ) return rval; 01326 if( len != 3 ) return MB_FAILURE; 01327 rval = moab->get_coords( conn, 3, coords[0].array() ); 01328 return rval; 01329 } 01330 01331 static ErrorCode closest_point_in_triangles( Interface* moab, 01332 const CartVect& to_pos, 01333 CartVect& result_pos, 01334 EntityHandle& result_tri ) 01335 { 01336 ErrorCode rval; 01337 01338 Range triangles; 01339 rval = moab->get_entities_by_type( 0, MBTRI, triangles ); 01340 if( MB_SUCCESS != rval ) return rval; 01341 01342 if( triangles.empty() ) return MB_FAILURE; 01343 01344 Range::iterator i = triangles.begin(); 01345 CartVect coords[3]; 01346 rval = tri_coords( moab, *i, coords ); 01347 if( MB_SUCCESS != rval ) return rval; 01348 result_tri = *i; 01349 GeomUtil::closest_location_on_tri( to_pos, coords, result_pos ); 01350 CartVect diff = to_pos - result_pos; 01351 double shortest_dist_sqr = diff % diff; 01352 01353 for( ++i; i != triangles.end(); ++i ) 01354 { 01355 rval = tri_coords( moab, *i, coords ); 01356 if( MB_SUCCESS != rval ) return rval; 01357 CartVect pos; 01358 GeomUtil::closest_location_on_tri( to_pos, coords, pos ); 01359 diff = to_pos - pos; 01360 double dsqr = diff % diff; 01361 if( dsqr < shortest_dist_sqr ) 01362 { 01363 shortest_dist_sqr = dsqr; 01364 result_pos = pos; 01365 result_tri = *i; 01366 } 01367 } 01368 01369 return MB_SUCCESS; 01370 } 01371 01372 static bool tri_in_set( Interface* moab, EntityHandle set, EntityHandle tri ) 01373 { 01374 Range tris; 01375 ErrorCode rval = moab->get_entities_by_type( set, MBTRI, tris ); 01376 if( MB_SUCCESS != rval ) return false; 01377 Range::iterator i = tris.find( tri ); 01378 return i != tris.end(); 01379 } 01380 01381 static bool do_closest_point_test( OrientedBoxTreeTool& tool, EntityHandle root_set, bool have_surface_tree ) 01382 { 01383 if( verbosity > 1 ) std::cout << "beginning closest point tests" << std::endl; 01384 01385 ErrorCode rval; 01386 Interface* moab = tool.get_moab_instance(); 01387 EntityHandle set; 01388 EntityHandle* set_ptr = have_surface_tree ? &set : 0; 01389 bool result = true; 01390 01391 // get root box 01392 OrientedBox box; 01393 rval = tool.box( root_set, box ); 01394 if( MB_SUCCESS != rval ) 01395 { 01396 if( verbosity ) std::cerr << "Invalid tree in do_closest_point_test\n"; 01397 return false; 01398 } 01399 01400 OrientedBoxTreeTool::TrvStats stats; 01401 01402 // chose some points to test 01403 CartVect points[] = { box.center + box.scaled_axis( 0 ), box.center + 2 * box.scaled_axis( 1 ), 01404 box.center + 0.5 * box.scaled_axis( 2 ), 01405 box.center + -2 * box.scaled_axis( 0 ) + -2 * box.scaled_axis( 1 ) + 01406 -2 * box.scaled_axis( 2 ), 01407 CartVect( 100, 100, 100 ) }; 01408 const int num_pts = sizeof( points ) / sizeof( points[0] ); 01409 01410 // test each point 01411 for( int i = 0; i < num_pts; ++i ) 01412 { 01413 if( verbosity >= 3 ) std::cout << "Evaluating closest point to " << points[i] << std::endl; 01414 01415 CartVect n_result, t_result; 01416 EntityHandle n_tri = 0, t_tri; 01417 01418 // find closest point the slow way 01419 rval = closest_point_in_triangles( moab, points[i], n_result, n_tri ); 01420 if( MB_SUCCESS != rval ) 01421 { 01422 std::cerr << "Internal MOAB error in do_closest_point_test" << std::endl; 01423 result = false; 01424 continue; 01425 } 01426 01427 // find closest point using tree 01428 rval = tool.closest_to_location( points[i].array(), root_set, t_result.array(), t_tri, set_ptr, &stats ); 01429 if( MB_SUCCESS != rval ) 01430 { 01431 if( verbosity ) 01432 std::cout << "OrientedBoxTreeTool:: closest_to_location( " << points[i] << " ) FAILED!" << std::endl; 01433 result = false; 01434 continue; 01435 } 01436 01437 CartVect diff = t_result - n_result; 01438 if( diff.length() > tolerance ) 01439 { 01440 if( verbosity ) 01441 std::cout << "Closest point to " << points[i] << " INCORRECT! (" << t_result << " != " << n_result 01442 << ")" << std::endl; 01443 result = false; 01444 continue; 01445 } 01446 01447 if( t_tri != n_tri ) 01448 { 01449 // if result point is triangle, then OK because 01450 // already tested that it is the same location 01451 // as the expected value. We just have a case where 01452 // the point was on an edge or vertex. 01453 CartVect coords[3]; 01454 CartVect diff1( 1, 1, 1 ); 01455 rval = tri_coords( moab, t_tri, coords ); 01456 if( MB_SUCCESS == rval ) 01457 { 01458 GeomUtil::closest_location_on_tri( points[i], coords, n_result ); 01459 diff1 = n_result - t_result; 01460 } 01461 if( ( diff1 % diff1 ) > tolerance ) 01462 { 01463 if( verbosity ) 01464 std::cout << "Triangle closest to " << points[i] << " INCORRECT! (" << t_tri << " != " << n_tri 01465 << ")" << std::endl; 01466 result = false; 01467 continue; 01468 } 01469 } 01470 01471 if( set_ptr && !tri_in_set( moab, *set_ptr, t_tri ) ) 01472 { 01473 if( verbosity ) std::cout << "Surface closest to " << points[i] << " INCORRECT!" << std::endl; 01474 result = false; 01475 continue; 01476 } 01477 } 01478 01479 if( verbosity > 1 ) 01480 { 01481 std::cout << "Traversal statistics for closest point tests: " << std::endl; 01482 stats.print( std::cout ); 01483 } 01484 01485 return result; 01486 }