cgma
OctTree.cpp
Go to the documentation of this file.
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 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines