Sierra Toolkit Version of the Day
BihTree.hpp
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
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends