Clear And Efficient 3D Range Tree Implementation

后端 未结 1 642
孤街浪徒
孤街浪徒 2021-02-06 13:42

I\'m working on this project where I have to search for objects in 3d space, and efficiency is a huge concern, I think Range Tree is perfect for what I\'m tryin

1条回答
  •  孤街浪徒
    2021-02-06 14:09

    I just read the wikipedia article. Lets see if I can write an n dimensional range tree. Because anything worth doing in 3 dimensions is worth doing in n.

    So the basic part of an n-dimensional range tree is that it can be recursively defined in terms of lower dimensional range trees.

    Some property classes to work with relatively generic value types. Specialize element_properties to set the scalar type of whatever your n-dimensional value is, and specialize get(T const&) to get the ith dimension of your n-dimensional value.

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    void Assert(bool test) {
      if (!test)
      {
        std::cout << "Assert failed" << std::endl;
        exit(-1);
      }
    }
    template
    struct Print {
      static void Do(Args... args) {}
    };
    template
    struct Print {
      static void Do(Arg arg, Tail... args) {
          std::cout << arg;
          Print::Do(args...);
      }
    };
    template
    void Debug(Args... args) {
        std::cout << "DEBUG:[";
        Print::Do(args...);
        std::cout << "]\n";
    }
    
    template
    struct element_properties {
      typedef typename T::value_type value_type;
    };
    template<>
    struct element_properties {
      typedef int value_type;
    };
    template
    typename element_properties::value_type get( T const & t );
    
    template
    typename element_properties::value_type get( int i ) { return i; }
    
    template
    typename element_properties>::value_type get( std::vector const& v) {
      return v[d];
    }
    
    template::value_type> >
    struct range_tree {
      typedef typename element_properties::value_type value_type;
      struct sorter {
        bool operator()( T const& left, T const& right ) const {
          return Order()( get(left), get(right) );
        }
      };
      struct printer {
        std::string operator()( T const& t ) const {
          std::string retval = "[ ";
          retval += print_elements( t );
          retval += "]";
          return retval;
        }
        std::string print_elements( T const& t ) const {
          std::stringstream ss;
          typedef typename range_tree::printer next_printer;
          ss << next_printer().print_elements(t);
          ss << get(t) << " ";
          return ss.str();
        }
      };
      template
      range_tree( Iterator begin, Iterator end ) {
        std::sort( begin, end, sorter() );
        root.reset( new tree_node( begin, end ) );
      }
    
      template
      void walk(Func f) const {
          if (root) root->walk(f);
      }
      template
      void walk(Func f) {
          if (root) root->walk(f);
      }
      struct tree_node {
        std::unique_ptr< range_tree > subtree;
        T value;
        template
        void walk(Func f) const {
          if (n==dim && !left && !right)
            f(value);
          if (left)
            left->walk(f);
          if (right)
            right->walk(f);
          if (subtree)
            subtree->walk(f);
        }
        template
        void walk(Func f) {
          if (n==dim && !left && !right)
            f(value);
          if (left)
            left->walk(f);
          if (right)
            right->walk(f);
          if (subtree)
            subtree->walk(f);
        }
        void find_path( T const& t, std::vector< tree_node const* >& vec ) {
          vec.push_back(this);
          if ( sorter()(t, value) ) {
            if (left)
              left->find_path(t, vec);
          } else if (sorter()(value, t)) {
            if (right)
              right->find_path(t, vec);
          } else {
            // found it!
            return;
          }
        }
        std::vector< tree_node const* > range_search( T const& left, T const& right )
        {
          std::vector left_path;
          std::vector right_path;
          find_path( left, left_path );
          find_path( right, right_path );
          // erase common path:
          {
            auto it1 = left_path.begin();
            auto it2 = right_path.begin();
            for( ; it1 != left_path.end() && it2 != right_path.end(); ++it1, ++it2) {
              if (*it1 != *it2)
              {
                Debug( "Different: ", printer()( (*it1)->value ), ", ", printer()( (*it2)->value ) );
                break;
              }
    
              Debug( "Identical: ", printer()( (*it1)->value ), ", ", printer()( (*it2)->value ) );
            }
            // remove identical prefixes:
            if (it2 == right_path.end() && it2 != right_path.begin())
                --it2;
            if (it1 == left_path.end() && it1 != left_path.begin())
                --it1;
            right_path.erase( right_path.begin(), it2 );
            left_path.erase( left_path.begin(), it1 );
          }
          for (auto it = left_path.begin(); it != left_path.end(); ++it) {
            if (*it && (*it)->right) {
              Debug( "Has right child: ", printer()( (*it)->value ) );
              *it = (*it)->right.get();
              Debug( "It is: ", printer()( (*it)->value ) );
            } else {
              Debug( "Has no right child: ", printer()( (*it)->value ) );
              if ( sorter()( (*it)->value, left) || sorter()( right, (*it)->value) ) {
                Debug( printer()( (*it)->value ), "<", printer()( left ), " so erased" );
                *it = 0;
              }
            }
          }
          for (auto it = right_path.begin(); it != right_path.end(); ++it) {
            if (*it && (*it)->left) {
              Debug( "Has left child: ", printer()( (*it)->value ) );
              *it = (*it)->left.get();
              Debug( "It is: ", printer()( (*it)->value ) );
            } else {
              Debug( "Has no left child: ", printer()( (*it)->value ) );
              if ( sorter()( (*it)->value, left) || sorter()( right, (*it)->value) ) {
                Debug( printer()( right ), "<", printer()( (*it)->value ), " so erased" );
                *it = 0;
              }
            }
          }
          left_path.insert( left_path.end(), right_path.begin(), right_path.end() );
          // remove duds and duplicates:
          auto highwater = std::remove_if( left_path.begin(), left_path.end(), []( tree_node const* n) { return n==0; } );
          std::sort( left_path.begin(), highwater );
          left_path.erase( std::unique( left_path.begin(), highwater ), left_path.end() );
          return left_path;
        }
    
        std::unique_ptr left;
        std::unique_ptr right;
        // rounds down:
        template
        static Iterator middle( Iterator begin, Iterator end ) {
          return (end-begin-1)/2 + begin ;
        }
        template
        tree_node( Iterator begin, Iterator end ):value(*middle(begin,end)) {
          Debug( "Inserted ", get(value), " at level ", dim );
          Iterator mid = middle(begin,end);
          Assert( begin != end );
          if (begin +1 != end) { // not a leaf
            Debug( "Not a leaf at level ", dim );
            ++mid; // so *mid was the last element in the left sub tree 
            Assert(mid!=begin);
            Assert(mid!=end);
            left.reset( new tree_node( begin, mid ) );
            right.reset( new tree_node( mid, end ) );
          } else {
            Debug( "Leaf at level ", dim );
          }
          if (dim > 0) {
            subtree.reset( new range_tree( begin, end ) );
          }
        }
      };
      std::unique_ptr root;
    };
    // makes the code above a tad easier:
    template
    struct range_tree< T, 0, Order > {
      typedef typename element_properties::value_type value_type;
      struct printer { templatestd::string print_elements(Unused const&) {return std::string();} };
      range_tree(...) {};
      struct tree_node {}; // maybe some stub functions in here
      template
      void walk(Func f) {}
    };
    
    int main() {
      typedef std::vector vector_type;
      std::vector test;
      test.push_back( vector_type{5,2} );
      test.push_back( vector_type{2,3} );
      range_tree< vector_type, 2 > tree( test.begin(), test.end() );
      std::cout << "Walking dim 2:";
      auto print_node = [](vector_type const& v){ std::cout << "(" << v[0] << "," << v[1] << ")"; };
      tree.walk<2>( print_node );
      std::cout << "\nWalking dim 1:";
      tree.walk<1>( print_node );
      std::cout << "\n";
    
      std::cout << "Range search from {3,3} to {10,10}\n";
      auto nodes = tree.root->range_search( vector_type{3,3}, vector_type{10,10} );
      for (auto it = nodes.begin(); it != nodes.end(); ++it)
      {
        (*it)->walk<2>( print_node );
      }
    }
    

    which is pretty damn close to an n-dimensional range tree. The 0 dimension tree naturally contains nothing.

    Basic facilities to search (in one dimension at a time) have been added now. You can manually do the recursions into lower dimensions, or it up so that the range_search always returns level 1 tree_node*s.

    0 讨论(0)
提交回复
热议问题