cgma
|
00001 //------------------------------------------------------------------------- 00002 // Filename : OctTree.cpp 00003 // 00004 // Purpose : Class for O(ln n) search for nodes within a specified 00005 // tolerance of a specified position. 00006 // 00007 // Special Notes : 00008 // 00009 // Creator : Jason Kraftcheck 00010 // 00011 // Creation Date : 01/30/02 00012 //------------------------------------------------------------------------- 00013 00014 #include "OctTree.hpp" 00015 #include "OctTreeCell.hpp" 00016 #include "DLIList.hpp" 00017 #include "CubitDefines.h" 00018 #include "GeometryDefines.h" 00019 00020 const int DEFAULT_MIN_NODES_PER_BOX = 6; 00021 // The default value to use for the minimum number of 00022 // nodes in a box, if none is specified in the constructr. 00023 00024 const int OCT_TREE_BOX_PAGE_SIZE = 8192; 00025 // The size of memory blocks to allocate for holding 00026 // oct-tree nodes. 00027 00028 const int OCT_TREE_CHUNK_SIZE = 8 * sizeof(OctTreeCell<int,int>); 00029 // Do not change. Size of a child array for a oct-tree node. 00030 00031 const int OCT_TREE_CHUNK_PER_PAGE = OCT_TREE_BOX_PAGE_SIZE / 00032 OCT_TREE_CHUNK_SIZE; 00033 // Do not change. Number of arrays of 8 oct-tree nodes 00034 // (child arrays) fit in a page. 00035 00036 const int OCT_TREE_ALLOC_COUNT = OCT_TREE_CHUNK_PER_PAGE * 8; 00037 // When allocating the page as one big array of oct-tree nodes, 00038 // the size of the array to request. Will be sligthtly smaller 00039 // than the requested page because page size probably is not a 00040 // multiple of OCT_TREE_CHUNK_SIZE. That's a good thing though, 00041 // because then the std-c and c++ memory allocators have a little 00042 // extra space for internal data. 00043 00044 //------------------------------------------------------------------------- 00045 // Purpose : Constructor 00046 // 00047 // Special Notes : 00048 // 00049 // Creator : Jason Kraftcheck 00050 // 00051 // Creation Date : 01/30/02 00052 //------------------------------------------------------------------------- 00053 template<class X, class E> 00054 OctTree<X,E>::OctTree( DLIList<X*>& nodes, 00055 double tolerance, 00056 int min_nodes_per_box, 00057 double min_box_dimension ) 00058 { 00059 // Check and store constructor arguments 00060 tolerance_ = tolerance < GEOMETRY_RESABS ? GEOMETRY_RESABS : tolerance; 00061 min_nodes_ = min_nodes_per_box < 0 ? DEFAULT_MIN_NODES_PER_BOX : min_nodes_per_box; 00062 double tol3 = 3 * tolerance_; 00063 min_box_size_ = min_box_dimension < tol3 ? tol3 : min_box_dimension; 00064 // Note: min_box_size must be greater than twice the tolerance 00065 // or the internal stack used for searching the tree will 00066 // overflow (more than 8 boxes may be within the tolerance 00067 // of a passed position). 00068 00069 // set up data for memory pool of oct-tree nodes 00070 mem_pages_.append( new OctTreeCell<X,E>[OCT_TREE_ALLOC_COUNT] ); 00071 curr_page_end_ = mem_pages_.get() + OCT_TREE_ALLOC_COUNT; 00072 00073 // construct root node 00074 node_memory_ = new OctTreeEntry<X,E>[nodes.size()]; 00075 nodes.reset(); 00076 OctTreeEntry<X,E>* ptr = node_memory_; 00077 for( int i = nodes.size(); i--; ) 00078 { 00079 ptr->node = nodes.get_and_step(); 00080 ptr->next = 0; 00081 ptr++; 00082 } 00083 root_ = new OctTreeCell<X,E>( node_memory_, nodes.size() ); 00084 00085 // get bounding box of all nodes 00086 CubitVector junk; 00087 root_->node_locations( min_, max_, junk ); 00088 00089 // create the oct-tree 00090 split_node(root_, min_, max_ ); 00091 } 00092 00093 //------------------------------------------------------------------------- 00094 // Purpose : Destructor 00095 // 00096 // Special Notes : 00097 // 00098 // Creator : Jason Kraftcheck 00099 // 00100 // Creation Date : 01/30/02 00101 //------------------------------------------------------------------------- 00102 template<class X, class E> 00103 OctTree<X,E>::~OctTree() 00104 { 00105 // Release all memory 00106 // Note that the oct-tree is not deleted except for the root node. 00107 // Only the root node was allocated directly from the heap. All 00108 // other nodes were allocated from our internal memory bool by 00109 // the allocate_8() method. We just release the whole pool. 00110 delete root_; 00111 delete [] node_memory_; 00112 while( mem_pages_.size() ) 00113 delete [] mem_pages_.pop(); 00114 00115 // Reinitialize to catch stale pointer to this object. 00116 00117 node_memory_ = 0; 00118 root_ = 0; 00119 } 00120 00121 //------------------------------------------------------------------------- 00122 // Purpose : Find all the nodes within tolerance of the passed position. 00123 // 00124 // Special Notes : 00125 // 00126 // Creator : Jason Kraftcheck 00127 // 00128 // Creation Date : 01/30/02 00129 //------------------------------------------------------------------------- 00130 template<class X, class E> 00131 void OctTree<X,E>::nodes_near( const CubitVector& position, 00132 DLIList<X*>& result_list ) 00133 { 00134 if( position.x() - min_.x() < -tolerance_ || 00135 position.y() - min_.y() < -tolerance_ || 00136 position.z() - min_.z() < -tolerance_ || 00137 position.x() - max_.x() > tolerance_ || 00138 position.y() - max_.y() > tolerance_ || 00139 position.z() - max_.z() > tolerance_ ) 00140 return; 00141 00142 // Initialize search stack 00143 if( root_->leaf() ) 00144 { 00145 // Done searching already -- that was easy 00146 root_->append_nodes( search_results ); 00147 box_count = 0; 00148 } 00149 else 00150 { 00151 search_results.clean_out(); 00152 00153 // Start with root node on stack of nodes to search 00154 box_count = 1; 00155 box_list[0] = root_; 00156 00157 box_min[0] = min_; 00158 box_max[0] = max_; 00159 } 00160 00161 // While there are still boxes on the search stack 00162 while( box_count ) 00163 { 00164 // Pop the 'current' box from the stack 00165 pop_to_current(); 00166 00167 // Push appropriate child box(es) to stack 00168 CubitVector diff = position - center; 00169 if( diff.x() <= tolerance_ ) 00170 { 00171 if( diff.y() <= tolerance_ ) 00172 { 00173 if( diff.z() <= tolerance_ ) 00174 { 00175 push_search_box( OctTreeCell<X,E>::X_MIN | 00176 OctTreeCell<X,E>::Y_MIN | 00177 OctTreeCell<X,E>::Z_MIN ); 00178 } 00179 if( diff.z() >= -tolerance_ ) 00180 { 00181 push_search_box( OctTreeCell<X,E>::X_MIN | 00182 OctTreeCell<X,E>::Y_MIN | 00183 OctTreeCell<X,E>::Z_MAX ); 00184 } 00185 } 00186 if( diff.y() >= -tolerance_ ) 00187 { 00188 if( diff.z() <= tolerance_ ) 00189 { 00190 push_search_box( OctTreeCell<X,E>::X_MIN | 00191 OctTreeCell<X,E>::Y_MAX | 00192 OctTreeCell<X,E>::Z_MIN ); 00193 } 00194 if( diff.z() >= -tolerance_ ) 00195 { 00196 push_search_box( OctTreeCell<X,E>::X_MIN | 00197 OctTreeCell<X,E>::Y_MAX | 00198 OctTreeCell<X,E>::Z_MAX ); 00199 } 00200 } 00201 } 00202 if( diff.x() >= -tolerance_ ) 00203 { 00204 if( diff.y() <= tolerance_ ) 00205 { 00206 if( diff.z() <= tolerance_ ) 00207 { 00208 push_search_box( OctTreeCell<X,E>::X_MAX | 00209 OctTreeCell<X,E>::Y_MIN | 00210 OctTreeCell<X,E>::Z_MIN ); 00211 } 00212 if( diff.z() >= -tolerance_ ) 00213 { 00214 push_search_box( OctTreeCell<X,E>::X_MAX | 00215 OctTreeCell<X,E>::Y_MIN | 00216 OctTreeCell<X,E>::Z_MAX ); 00217 } 00218 } 00219 if( diff.y() >= -tolerance_ ) 00220 { 00221 if( diff.z() <= tolerance_ ) 00222 { 00223 push_search_box( OctTreeCell<X,E>::X_MAX | 00224 OctTreeCell<X,E>::Y_MAX | 00225 OctTreeCell<X,E>::Z_MIN ); 00226 } 00227 if( diff.z() >= -tolerance_ ) 00228 { 00229 push_search_box( OctTreeCell<X,E>::X_MAX | 00230 OctTreeCell<X,E>::Y_MAX | 00231 OctTreeCell<X,E>::Z_MAX ); 00232 } 00233 } 00234 } 00235 } 00236 00237 // append to result list all nodes within tolerance 00238 // of passed position 00239 search_results.reset(); 00240 const double tol_sqr = tolerance_ * tolerance_; 00241 for( int i = search_results.size(); i--; ) 00242 { 00243 X* node = search_results.get_and_step(); 00244 CubitVector coords = E::coordinates(node); 00245 double x = position.x() - coords.x(); 00246 double y = position.y() - coords.y(); 00247 double z = position.z() - coords.z(); 00248 00249 double dist_sqr = x * x + y * y + z * z; 00250 if( dist_sqr <= tol_sqr ) 00251 result_list.append( node ); 00252 } 00253 } 00254 00255 00256 //------------------------------------------------------------------------- 00257 // Purpose : Push child boxes onto search stack (or leaf node list) 00258 // 00259 // Special Notes : 00260 // 00261 // Creator : Jason Kraftcheck 00262 // 00263 // Creation Date : 01/30/02 00264 //------------------------------------------------------------------------- 00265 template<class X, class E> 00266 void OctTree<X,E>::push_search_box( int quadrant_flags ) 00267 { 00268 OctTreeCell<X,E>* box = current_box->child( quadrant_flags ); 00269 if( box->leaf() ) 00270 { 00271 // If a leaf node, get its nodes 00272 box->append_nodes( search_results ); 00273 } 00274 else 00275 { 00276 assert( box_count < 64 ); 00277 00278 // Get appropriate min and max corners of child box 00279 00280 CubitVector& oldmin = current_min; 00281 CubitVector& oldmax = current_max; 00282 CubitVector& newmin = box_min[box_count]; 00283 CubitVector& newmax = box_max[box_count]; 00284 00285 if( quadrant_flags & OctTreeCell<X,E>::X_MAX ) 00286 { 00287 newmin.x( center.x() ); 00288 newmax.x( oldmax.x() ); 00289 } 00290 else 00291 { 00292 newmin.x( oldmin.x() ); 00293 newmax.x( center.x() ); 00294 } 00295 00296 if( quadrant_flags & OctTreeCell<X,E>::Y_MAX ) 00297 { 00298 newmin.y( center.y() ); 00299 newmax.y( oldmax.y() ); 00300 } 00301 else 00302 { 00303 newmin.y( oldmin.y() ); 00304 newmax.y( center.y() ); 00305 } 00306 00307 if( quadrant_flags & OctTreeCell<X,E>::Z_MAX ) 00308 { 00309 newmin.z( center.z() ); 00310 newmax.z( oldmax.z() ); 00311 } 00312 else 00313 { 00314 newmin.z( oldmin.z() ); 00315 newmax.z( center.z() ); 00316 } 00317 00318 // Put the box on the stack 00319 box_list[box_count++] = box; 00320 } 00321 } 00322 00323 //------------------------------------------------------------------------- 00324 // Purpose : Pop box/tree-node from stack and store in current_box 00325 // 00326 // Special Notes : 00327 // 00328 // Creator : Jason Kraftcheck 00329 // 00330 // Creation Date : 01/30/02 00331 //------------------------------------------------------------------------- 00332 template<class X, class E> 00333 void OctTree<X,E>::pop_to_current() 00334 { 00335 assert( box_count ); 00336 box_count--; 00337 current_min = box_min[box_count]; 00338 current_max = box_max[box_count]; 00339 current_box = box_list[box_count]; 00340 center = (current_min + current_max) * 0.5; 00341 } 00342 00343 //------------------------------------------------------------------------- 00344 // Purpose : Allocate block of 8 oct-tree nodes for use as children 00345 // of some (currently-leaf) node. 00346 // 00347 // Special Notes : 00348 // 00349 // Creator : Jason Kraftcheck 00350 // 00351 // Creation Date : 01/30/02 00352 //------------------------------------------------------------------------- 00353 template<class X, class E> 00354 OctTreeCell<X,E>* OctTree<X,E>::allocate_8() 00355 { 00356 // Want to pop from end of page 00357 curr_page_end_ -= 8; 00358 00359 // Any blocks available in current page? 00360 mem_pages_.last(); 00361 if( curr_page_end_ < mem_pages_.get() ) 00362 { 00363 // Allocate new page 00364 mem_pages_.append( new OctTreeCell<X,E>[OCT_TREE_ALLOC_COUNT] ); 00365 00366 // Pop last block from new page 00367 mem_pages_.last(); 00368 curr_page_end_ = mem_pages_.get() + OCT_TREE_ALLOC_COUNT - 8; 00369 } 00370 00371 return curr_page_end_; 00372 } 00373 00374 00375 //------------------------------------------------------------------------- 00376 // Purpose : Recursively subdivide oct-tree nodes until 00377 // one of three stop conditions are met: 00378 // - min_nodes_ or less nodes in the box 00379 // - size of child boxes will be smaller than 00380 // min_box_size_ in all three dimensions 00381 // - the nodes within this box are all within 00382 // 2*tolerance_ of each other. 00383 // 00384 // Special Notes : 00385 // 00386 // Creator : Jason Kraftcheck 00387 // 00388 // Creation Date : 01/30/02 00389 //------------------------------------------------------------------------- 00390 template<class X, class E> 00391 void OctTree<X,E>::split_node( OctTreeCell<X,E>* box, 00392 const CubitVector& min, 00393 const CubitVector& max ) 00394 { 00395 assert( box->leaf() ); 00396 CubitVector diag = max - min; 00397 diag *= 0.5; // diagonal size of child boxes 00398 if( (box->node_count() <= min_nodes_) || // must have more than min_nodes_ 00399 (diag.x() < min_box_size_ && // child boxes will be smaller 00400 diag.y() < min_box_size_ && // than min_box_size_ in all 00401 diag.z() < min_box_size_ ) ) // three dimensions. 00402 return; 00403 00404 // Check if all nodes are currently within 2*tolerance_ 00405 // of each other. 00406 double tol2 = 2 * tolerance_; 00407 CubitVector node_min, node_max, junk; 00408 box->node_locations( node_min, node_max, junk ); 00409 diag = node_max - node_min; 00410 if( diag.x() < tol2 && 00411 diag.y() < tol2 && 00412 diag.z() < tol2 ) 00413 return; 00414 00415 // Split the box 00416 CubitVector mid = (min + max) * 0.5; 00417 if( !box->split( mid, allocate_8() ) ) 00418 return; 00419 00420 // Recursively call split_node on all 8 new child boxes 00421 split_node( 00422 box->child(OctTreeCell<X,E>::X_MIN|OctTreeCell<X,E>::Y_MIN|OctTreeCell<X,E>::Z_MIN), 00423 CubitVector( min.x(), min.y(), min.z() ), 00424 CubitVector( mid.x(), mid.y(), mid.z() ) ); 00425 split_node( 00426 box->child(OctTreeCell<X,E>::X_MIN|OctTreeCell<X,E>::Y_MIN|OctTreeCell<X,E>::Z_MAX), 00427 CubitVector( min.x(), min.y(), mid.z() ), 00428 CubitVector( mid.x(), mid.y(), max.z() ) ); 00429 split_node( 00430 box->child(OctTreeCell<X,E>::X_MIN|OctTreeCell<X,E>::Y_MAX|OctTreeCell<X,E>::Z_MIN), 00431 CubitVector( min.x(), mid.y(), min.z() ), 00432 CubitVector( mid.x(), max.y(), mid.z() ) ); 00433 split_node( 00434 box->child(OctTreeCell<X,E>::X_MIN|OctTreeCell<X,E>::Y_MAX|OctTreeCell<X,E>::Z_MAX), 00435 CubitVector( min.x(), mid.y(), mid.z() ), 00436 CubitVector( mid.x(), max.y(), max.z() ) ); 00437 split_node( 00438 box->child(OctTreeCell<X,E>::X_MAX|OctTreeCell<X,E>::Y_MIN|OctTreeCell<X,E>::Z_MIN), 00439 CubitVector( mid.x(), min.y(), min.z() ), 00440 CubitVector( max.x(), mid.y(), mid.z() ) ); 00441 split_node( 00442 box->child(OctTreeCell<X,E>::X_MAX|OctTreeCell<X,E>::Y_MIN|OctTreeCell<X,E>::Z_MAX), 00443 CubitVector( mid.x(), min.y(), mid.z() ), 00444 CubitVector( max.x(), mid.y(), max.z() ) ); 00445 split_node( 00446 box->child(OctTreeCell<X,E>::X_MAX|OctTreeCell<X,E>::Y_MAX|OctTreeCell<X,E>::Z_MIN), 00447 CubitVector( mid.x(), mid.y(), min.z() ), 00448 CubitVector( max.x(), max.y(), mid.z() ) ); 00449 split_node( 00450 box->child(OctTreeCell<X,E>::X_MAX|OctTreeCell<X,E>::Y_MAX|OctTreeCell<X,E>::Z_MAX), 00451 CubitVector( mid.x(), mid.y(), mid.z() ), 00452 CubitVector( max.x(), max.y(), max.z() ) ); 00453 } 00454 00455 00456 template<class X, class E> 00457 void OctTree<X,E>::tree_size( DLIList<int>& boxes, DLIList<int>& leaves ) 00458 { 00459 boxes.clean_out(); 00460 boxes.reset(); 00461 00462 leaves.clean_out(); 00463 leaves.reset(); 00464 00465 tree_size( root_, boxes, leaves ); 00466 } 00467 00468 template<class X, class E> 00469 void OctTree<X,E>::tree_size( OctTreeCell<X,E>* box, DLIList<int>& boxes, DLIList<int>& leaves ) 00470 { 00471 if( boxes.is_at_end() ) 00472 { 00473 boxes.append(1); 00474 boxes.step(); 00475 } 00476 else 00477 { 00478 boxes.step(); 00479 boxes.change_to( boxes.get() + 1 ); 00480 } 00481 00482 if( box->leaf() ) 00483 { 00484 if( leaves.is_at_end() ) 00485 { 00486 leaves.append(1); 00487 leaves.step(); 00488 } 00489 else 00490 { 00491 leaves.step(); 00492 leaves.change_to( leaves.get() + 1 ); 00493 } 00494 } 00495 else 00496 { 00497 if( leaves.is_at_end() ) 00498 leaves.append(0); 00499 leaves.step(); 00500 00501 for( int i = 0; i < 8; i++ ) 00502 tree_size( box->child(i), boxes, leaves ); 00503 } 00504 00505 boxes.back(); 00506 leaves.back(); 00507 } 00508