|
Sierra Toolkit Version of the Day
|
00001 /*------------------------------------------------------------------------*/ 00002 /* Copyright 2010 Sandia Corporation. */ 00003 /* Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive */ 00004 /* license for use of this work by or on behalf of the U.S. Government. */ 00005 /* Export of this program may require a license from the */ 00006 /* United States Government. */ 00007 /*------------------------------------------------------------------------*/ 00008 00009 #ifndef stk_search_BihTree_hpp 00010 #define stk_search_BihTree_hpp 00011 00012 #include <float.h> 00013 00014 #include <algorithm> 00015 #include <list> 00016 #include <vector> 00017 #include <set> 00018 00019 #include <stk_search/BoundingBox.hpp> 00020 00021 namespace stk { 00022 namespace search { 00023 namespace bih { 00024 00025 class UnitTestBihTree; 00026 00029 template <class RangeBoundingVolume, int COMPARE_MODE = stk::search::box::compare::MIDDLE> 00030 class BihTree 00031 { 00032 public: 00033 typedef typename RangeBoundingVolume::Key RangeKey; 00034 00035 00036 template <class InputIterator> 00037 BihTree( 00038 const InputIterator & first, 00039 const InputIterator & last, 00040 unsigned max_depth = 32, 00041 unsigned items_per_leaf = 1 ) : 00042 MAX_DEPTH(max_depth), ITEMS_PER_LEAF(items_per_leaf), m_range(), m_root(), m_depth(0), m_num_nodes(0) 00043 { 00044 m_range.insert(m_range.begin(),first,last); 00045 build_tree(m_root, m_range.begin(), m_range.end(), 0); 00046 } 00047 00054 template<class DomainBoundingVolume, class RelationshipContainer> 00055 void intersect( 00056 const DomainBoundingVolume & domain, 00057 RelationshipContainer & relations) const 00058 { 00059 intersect_helper(m_root,domain,relations); 00060 } 00061 00068 template<class DomainIterator, class RelationshipContainer> 00069 void intersect( 00070 const DomainIterator & first, 00071 const DomainIterator & last, 00072 RelationshipContainer & relations) const 00073 { 00074 for (DomainIterator i = first; i != last; ++i) 00075 intersect_helper(m_root,*i,relations); 00076 } 00077 00084 template<class RelationshipContainer> 00085 void self_intersect (RelationshipContainer & relations) const { 00086 intersect(m_range.begin(),m_range.end(),relations); 00087 } 00088 00089 unsigned depth() const { return m_depth; } 00090 unsigned num_nodes() const { return m_num_nodes; } 00091 00092 const unsigned MAX_DEPTH; 00093 const unsigned ITEMS_PER_LEAF; 00094 00095 private: 00096 00099 class Node 00100 { 00101 private: // functions to use inside of BihTree 00102 static const uint64_t LEAF_NODE = 3; 00103 static const uint64_t BIT_MASK = 3; 00104 00105 Node() 00106 : m_children_ptr(LEAF_NODE) 00107 { 00108 m_data.volumes = 0; 00109 } 00110 00111 ~Node() { 00112 if (!is_leaf()) // remove volumes 00113 delete [] children(); 00114 } 00115 00117 inline int axis() const { return static_cast<int>(m_children_ptr & BIT_MASK); } 00118 00120 inline bool is_leaf() const { return (m_children_ptr & BIT_MASK) == LEAF_NODE; } 00121 00124 inline int size() const { return is_leaf() ? static_cast<int>(m_children_ptr >> 2) : -1; } 00125 00128 inline const RangeBoundingVolume * begin() const { return m_data.volumes; } 00129 00132 inline const RangeBoundingVolume * end() const { return m_data.volumes + size(); } 00133 00135 inline Node * children() const { return is_leaf() ? 0 : reinterpret_cast<Node *>(m_children_ptr & ~BIT_MASK); } 00136 00138 inline Node & left() const { return children()[0]; } 00139 00141 inline Node & right() const { return children()[1]; } 00142 00145 inline float left_max() const { return m_data.planes[0]; } 00146 00149 inline float right_min() const { return m_data.planes[1]; } 00150 00152 inline void make_interior_node(uint64_t axis, const float clipplanes[]) { 00153 Node * child = new Node[2]; 00154 m_children_ptr = reinterpret_cast<uint64_t>(child) | axis; 00155 m_data.planes[0] = clipplanes[0]; 00156 m_data.planes[1] = clipplanes[1]; 00157 } 00158 00160 void make_leaf_node(const RangeBoundingVolume * volumes, size_t num) { 00161 // the size is stored in the upper bytes of the children_ptr 00162 m_children_ptr = (num << 2) | LEAF_NODE; 00163 m_data.volumes = volumes; 00164 } 00165 00166 00169 uint64_t m_children_ptr; 00170 00171 union { 00172 const RangeBoundingVolume * volumes ; 00173 float planes[2] ; 00174 }m_data; 00175 00176 //disallow copy constructor and assignment operator 00177 Node(const Node &); 00178 void operator = (const Node &); 00179 00180 friend class BihTree<RangeBoundingVolume,COMPARE_MODE>; 00181 friend class ::stk::search::bih::UnitTestBihTree; 00182 }; //end class Node 00183 00184 00185 private: 00186 00188 void build_tree( 00189 Node & node, 00190 typename std::vector<RangeBoundingVolume>::iterator begin, 00191 typename std::vector<RangeBoundingVolume>::iterator end, 00192 unsigned current_depth); 00193 00194 template<class DomainBoundingVolume> 00195 void intersect_helper( 00196 const Node & node, 00197 const DomainBoundingVolume & domain, 00198 std::vector<std::pair<typename DomainBoundingVolume::Key, RangeKey> > & relations) const; 00199 00200 template<class DomainBoundingVolume> 00201 void intersect_helper( 00202 const Node & node, 00203 const DomainBoundingVolume & domain, 00204 std::set<std::pair<typename DomainBoundingVolume::Key, RangeKey> > & relations) const; 00205 00206 template<class DomainBoundingVolume> 00207 void intersect_helper( 00208 const Node & node, 00209 const DomainBoundingVolume & domain, 00210 std::vector<RangeBoundingVolume> & range) const; 00211 private: 00212 00213 00215 float find_max( 00216 typename std::vector<RangeBoundingVolume>::iterator begin, 00217 typename std::vector<RangeBoundingVolume>::iterator end, 00218 int axis) const 00219 { 00220 float value = begin->upper(axis); 00221 00222 for (typename std::vector<RangeBoundingVolume>::const_iterator i = begin+1; i!=end; ++i) { 00223 if ( static_cast<float>(i->upper(axis)) > value) { 00224 value = static_cast<float>(i->upper(axis)); 00225 } 00226 } 00227 return value; 00228 } 00229 00231 float find_ratio( 00232 typename std::vector<RangeBoundingVolume>::iterator begin, 00233 typename std::vector<RangeBoundingVolume>::iterator end, 00234 int axis, 00235 float split_plane) const 00236 { 00237 int count = 0; 00238 for (typename std::vector<RangeBoundingVolume>::const_iterator i = begin; i!=end; ++i) { 00239 if ( static_cast<float>(i->center(axis)) < split_plane) { 00240 ++count; 00241 } 00242 } 00243 float value = static_cast<float>(count)/(end-begin) - 0.5f; 00244 return value >=0 ? value : -1 * value; 00245 } 00246 00248 float find_min( 00249 typename std::vector<RangeBoundingVolume>::iterator begin, 00250 typename std::vector<RangeBoundingVolume>::iterator end, 00251 int axis) const 00252 { 00253 float value = begin->lower(axis); 00254 00255 for (typename std::vector<RangeBoundingVolume>::const_iterator i = begin+1; i!=end; ++i) { 00256 if ( static_cast<float>(i->lower(axis)) < value) { 00257 value = static_cast<float>(i->lower(axis)); 00258 } 00259 } 00260 return value; 00261 } 00262 00263 00264 bool find_split_plane( 00265 const typename std::vector<RangeBoundingVolume>::iterator begin, 00266 const typename std::vector<RangeBoundingVolume>::iterator end, 00267 int & axis, 00268 typename RangeBoundingVolume::Data & split_plane ) 00269 { 00270 bool split_by_space = true; 00271 axis = 0; 00272 00273 int size = end - begin; 00274 00275 typename RangeBoundingVolume::Data length = 0, mean_length = 0, max_length = 0; 00276 00277 00278 for (int j = 0; j < RangeBoundingVolume::DIMENSION; ++j) { 00279 typename RangeBoundingVolume::Data min = FLT_MAX, max = -FLT_MAX, temp_mean_length = 0, temp_max_length = 0; 00280 for (typename std::vector<RangeBoundingVolume>::const_iterator i = begin; i!=end; ++i) { 00281 temp_mean_length += i->length(j); 00282 if (i->length(j) > temp_max_length) temp_max_length = i->length(j); 00283 if (i->lower(j) < min) min = i->lower(j); 00284 if (i->upper(j) > max) max = i->upper(j); 00285 } 00286 temp_mean_length /= size; 00287 if (max-min > length) { 00288 length = max-min; 00289 axis = j; 00290 split_plane = min + length/2; 00291 mean_length = temp_mean_length; 00292 max_length = temp_max_length; 00293 } 00294 } 00295 00296 if (mean_length*8 < max_length) { 00297 split_by_space = false; 00298 split_plane = mean_length*5; 00299 } 00300 00301 return split_by_space; 00302 } 00303 00304 void make_leaf( 00305 Node & node, 00306 typename std::vector<RangeBoundingVolume>::iterator begin, 00307 typename std::vector<RangeBoundingVolume>::iterator end ) 00308 { 00309 int num = end - begin; 00310 if (num > 0) { 00311 node.make_leaf_node(&(*begin),num); 00312 } 00313 else { 00314 node.make_leaf_node(0,0); 00315 } 00316 } 00317 00318 private: 00319 00320 //member variables 00321 std::vector<RangeBoundingVolume> m_range; 00322 Node m_root; 00323 unsigned m_depth; 00324 unsigned m_num_nodes; 00325 00326 00327 //disable copy constructor and assignment operator 00328 explicit BihTree( const BihTree &); 00329 void operator = ( const BihTree &); 00330 00331 friend class ::stk::search::bih::UnitTestBihTree; 00332 }; 00333 00334 00335 template <class RangeBoundingVolume, int COMPARE_MODE> 00336 void BihTree<RangeBoundingVolume, COMPARE_MODE>::build_tree( 00337 Node & node, 00338 typename std::vector<RangeBoundingVolume>::iterator begin, 00339 typename std::vector<RangeBoundingVolume>::iterator end, 00340 unsigned current_depth) 00341 { 00342 //update tree depth 00343 m_depth = current_depth > m_depth ? current_depth : m_depth; 00344 00345 // make leaf node 00346 if (current_depth >= MAX_DEPTH || end-begin <= static_cast<int>(ITEMS_PER_LEAF)) { 00347 make_leaf(node,begin,end); 00348 return; 00349 } 00350 00351 typename std::vector<RangeBoundingVolume>::iterator k = end; 00352 typename RangeBoundingVolume::Data split_plane = 0; 00353 int axis = 0; 00354 00355 bool split_by_space = find_split_plane(begin,end, axis, split_plane); 00356 00357 if (split_by_space) { // split by space 00358 k = std::partition(begin,end,stk::search::box::compare::Partition<RangeBoundingVolume,COMPARE_MODE>(axis,split_plane)); 00359 00360 float ratio = static_cast<float>(k-begin) / (end-begin); 00361 ratio -= 0.5f; 00362 ratio = ratio >= 0 ? ratio : -1.0f * ratio; 00363 00364 if (ratio > 0.35f) { 00365 k = begin + (end - begin)/2; 00366 std::nth_element(begin,k,end,stk::search::box::compare::Compare<RangeBoundingVolume,COMPARE_MODE>(axis)); 00367 } 00368 } 00369 else { // split by length 00370 k = std::partition(begin,end,stk::search::box::compare::Partition<RangeBoundingVolume,stk::search::box::compare::LENGTH>(axis,split_plane)); 00371 } 00372 00373 float clip_planes[2] = {-FLT_MAX,FLT_MAX}; 00374 00375 if (k - begin >= 1) {// left child non-empty 00376 clip_planes[0] = find_max(begin,k,axis); 00377 } 00378 00379 if (end - k >= 1) { // right child non-empty 00380 clip_planes[1] = find_min(k,end,axis); 00381 } 00382 00383 //make the node an interior node 00384 node.make_interior_node(axis,clip_planes); 00385 00386 m_num_nodes += 2; 00387 00388 build_tree( 00389 node.left(), 00390 begin, 00391 k, 00392 current_depth+1 00393 ); 00394 00395 build_tree( 00396 node.right(), 00397 k, 00398 end, 00399 current_depth+1 00400 ); 00401 } 00402 00403 template <class RangeBoundingVolume, int COMPARE_MODE> 00404 template<class DomainBoundingVolume> 00405 void BihTree<RangeBoundingVolume, COMPARE_MODE>::intersect_helper( 00406 const Node & node, 00407 const DomainBoundingVolume & domain, 00408 std::vector<std::pair<typename DomainBoundingVolume::Key, RangeKey> > & relations) const 00409 { 00410 //if leaf add items to vector 00411 if (node.is_leaf()) { 00412 if (node.size() > 0 ) { 00413 for( const RangeBoundingVolume * i = node.begin(); i != node.end(); ++i) { 00414 relations.push_back( std::pair<typename DomainBoundingVolume::Key, RangeKey>(domain.key,i->key) ); 00415 } 00416 } 00417 return; 00418 } 00419 00420 int axis = node.axis(); 00421 00422 if (domain.lower(axis) <= node.left_max()) { //go left 00423 intersect_helper( node.left(), domain, relations ); 00424 } 00425 00426 if (domain.upper(axis) >= node.right_min()) { //go right 00427 intersect_helper( node.right(), domain, relations ); 00428 } 00429 00430 } 00431 00432 template <class RangeBoundingVolume, int COMPARE_MODE> 00433 template<class DomainBoundingVolume> 00434 void BihTree<RangeBoundingVolume, COMPARE_MODE>::intersect_helper( 00435 const Node & node, 00436 const DomainBoundingVolume & domain, 00437 std::set<std::pair<typename DomainBoundingVolume::Key, RangeKey> > & relations) const 00438 { 00439 //if leaf add items to vector 00440 if (node.is_leaf()) { 00441 if (node.size() > 0 ) { 00442 for( const RangeBoundingVolume * i = node.begin(); i != node.end(); ++i) { 00443 relations.insert( std::pair<typename DomainBoundingVolume::Key, RangeKey>(domain.key,i->key) ); 00444 } 00445 } 00446 return; 00447 } 00448 00449 int axis = node.axis(); 00450 00451 if (domain.lower(axis) <= node.left_max()) { //go left 00452 intersect_helper( node.left(), domain, relations ); 00453 } 00454 00455 if (domain.upper(axis) >= node.right_min()) { //go right 00456 intersect_helper( node.right(), domain, relations ); 00457 } 00458 00459 } 00460 00461 00462 template <class RangeBoundingVolume, int COMPARE_MODE> 00463 template<class DomainBoundingVolume> 00464 void BihTree<RangeBoundingVolume, COMPARE_MODE>::intersect_helper( 00465 const Node & node, 00466 const DomainBoundingVolume & domain, 00467 std::vector<RangeBoundingVolume> & range) const 00468 { 00469 //if leaf add items to vector 00470 if (node.is_leaf()) { 00471 if (node.size() > 0 ) { 00472 for( const RangeBoundingVolume * i = node.begin(); i != node.end(); ++i) { 00473 range.push_back(*i); 00474 } 00475 } 00476 return; 00477 } 00478 00479 int axis = node.axis(); 00480 00481 if (domain.lower(axis) <= node.left_max()) { //go left 00482 intersect_helper( node.left(), domain, range ); 00483 } 00484 00485 if (domain.upper(axis) >= node.right_min()) { //go right 00486 intersect_helper( node.right(), domain, range ); 00487 } 00488 00489 } 00490 00491 } // namespace bih 00492 } // namespace search 00493 } // namespace stk 00494 00495 #endif // stk_search_BihTree_hpp