// ____ ______ __ // / __ \ / ____// / // / /_/ // / / / // / ____// /___ / /___ PixInsight Class Library // /_/ \____//_____/ PCL 2.4.23 // ---------------------------------------------------------------------------- // pcl/QuadTree.h - Released 2022-03-12T18:59:29Z // ---------------------------------------------------------------------------- // This file is part of the PixInsight Class Library (PCL). // PCL is a multiplatform C++ framework for development of PixInsight modules. // // Copyright (c) 2003-2022 Pleiades Astrophoto S.L. All Rights Reserved. // // Redistribution and use in both source and binary forms, with or without // modification, is permitted provided that the following conditions are met: // // 1. All redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // 2. All redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // 3. Neither the names "PixInsight" and "Pleiades Astrophoto", nor the names // of their contributors, may be used to endorse or promote products derived // from this software without specific prior written permission. For written // permission, please contact info@pixinsight.com. // // 4. All products derived from this software, in any form whatsoever, must // reproduce the following acknowledgment in the end-user documentation // and/or other materials provided with the product: // // "This product is based on software from the PixInsight project, developed // by Pleiades Astrophoto and its contributors (https://pixinsight.com/)." // // Alternatively, if that is where third-party acknowledgments normally // appear, this acknowledgment must be reproduced in the product itself. // // THIS SOFTWARE IS PROVIDED BY PLEIADES ASTROPHOTO AND ITS CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED // TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PLEIADES ASTROPHOTO OR ITS // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, BUSINESS // INTERRUPTION; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; AND LOSS OF USE, // DATA OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. // ---------------------------------------------------------------------------- #ifndef __PCL_QuadTree_h #define __PCL_QuadTree_h /// \file pcl/QuadTree.h #include #include #include #include namespace pcl { // ---------------------------------------------------------------------------- /* * Default bucket capacity for point region quadtree generation. */ #define __PCL_QUADTREE_DEFAULT_BUCKET_CAPACITY 40 /* * Default minimum allowed dimension of a quadtree node region. */ #define __PCL_QUADTREE_DEFAULT_EPSILON 1.0e-08 // ---------------------------------------------------------------------------- /*! * \class QuadTree * \brief Bucket PR quadtree for two-dimensional point data. * * A quadtree is a specialized binary search tree for partitioning of a set of * points in two dimensions. Quadtrees have important applications in * computational geometry problems requiring efficient rectangular range * searching and nearest neighbor queries. * * This class implements a bucket point region quadtree structure * (see Reference 2). * * The template type argument T represents the type of a \e point object stored * in a %QuadTree structure. The type T must have the following properties: * * \li The standard default and copy constructors are required:\n * \n * T::T() \n * T::T( const T& ) * * \li The \c T::component subtype must be defined. It represents a component * of an object of type T. For example, if T is a vector type, T::component * must be the type of a vector component. * * \li The array subscript operator must be defined as follows:\n * \n * component T::operator []( int i ) const \n * \n * This operator must return the value of the first or second component of an * object being stored in the quadtree. The subindex i will be either 0 or 1 * for the first or second point component, respectively. * * \b References * * 1. Mark de Berg et al, Computational Geometry: Algorithms and * Applications Third Edition, Springer, 2010, Chapter 14. * * 2. Hanan Samet, Foundations of Multidimensional and Metric Data * Structures, Morgan Kaufmann, 2006, Section 1.4. * * \sa KDTree */ template class QuadTree { public: /*! * Represents a two-dimensional point stored in this quadtree. */ typedef T point; /*! * Represents a point component. */ typedef typename point::component component; /*! * A list of points. Used for tree build and search operations. */ typedef Array point_list; /*! * A rectangular region. Used for rectangular range search operations. */ typedef DRect rectangle; /*! * The type of rectangular region coordinates. */ typedef rectangle::component coordinate; // ------------------------------------------------------------------------- /*! * \class pcl::QuadTree::Node * \brief Quadtree node structure */ struct Node { rectangle rect = 0.0; //!< The rectangular region represented by this node. Node* nw = nullptr; //!< North-West child node, representing the top-left subregion. Node* ne = nullptr; //!< North-East child node, representing the top-right subregion. Node* sw = nullptr; //!< South-West child node, representing the bottom-left subregion. Node* se = nullptr; //!< South-East child node, representing the bottom-right subregion. #ifdef __PCL_QUADTREE_STRUCTURAL_NODE_HAS_DATA void* data = nullptr; #endif /*! * Default constructor. Constructs an uninitialized quadtree node. */ Node() = default; /*! * Constructs a quadtree node for the specified rectangular region \a r. */ Node( const rectangle& r ) : rect( r ) { } /*! * Returns true iff this is a leaf quadtree node. A leaf node does not * contain child nodes, that is, there is no further subdivision of the * domain space beyond a leaf quadtree node. * * In a healthy quadtree (as any QuadTree structure should be under * normal working conditions), you can expect any leaf node to be an * instance of QuadTree::LeafNode containing a nonempty list of points. */ bool IsLeaf() const { return nw == nullptr && ne == nullptr && sw == nullptr && se == nullptr; } /*! * Returns true iff the rectangular region represented by this node * intersects the specified rectangle \a r. */ bool Intersects( const rectangle& r ) const { return rect.x1 >= r.x0 && rect.x0 <= r.x1 && rect.y1 >= r.y0 && rect.y0 <= r.y1; } /*! * Returns true iff the rectangular region represented by this node * includes a point in the plane specified by its coordinates \a x, \a y. */ bool Includes( coordinate x, coordinate y ) const { return x >= rect.x0 && x <= rect.x1 && y >= rect.y0 && y <= rect.y1; } /*! * Returns true iff the rectangular region represented by this node * includes the specified point \a p in the plane. */ bool Includes( const rectangle::point& p ) const { return Includes( p.x, p.y ); } /*! * Returns the Northwest (top left) splitting rectangle for this node. */ rectangle NWRect() const { return rectangle( rect.TopLeft(), rect.Center() ); } /*! * Returns the Northeast (top right) splitting rectangle for this node. */ rectangle NERect() const { return rectangle( rect.CenterTop(), rect.CenterRight() ); } /*! * Returns the Southwest (bottom left) splitting rectangle for this node. */ rectangle SWRect() const { return rectangle( rect.CenterLeft(), rect.CenterBottom() ); } /*! * Returns the Southeast (bottom right) splitting rectangle for this * node. */ rectangle SERect() const { return rectangle( rect.Center(), rect.BottomRight() ); } }; // ------------------------------------------------------------------------- /*! * \class pcl::QuadTree::LeafNode * \brief Quadtree leaf node structure */ struct LeafNode : public Node { /*! * The list of points contained by this leaf node. * * In a healthy quadtree (as any QuadTree structure should be under * normal working conditions), every existing leaf node should contain a * nonempty point list. */ point_list points; #ifndef __PCL_QUADTREE_STRUCTURAL_NODE_HAS_DATA /*! * Pointer to an arbitrary data structure that can be associated with * this leaf node. Its default value is \c nullptr. * * The quadtree structure does not access this pointer in any way. * Destruction of the object pointed to by this member, if required, is * the sole responsibility of the external code that has defined it. */ void* data = nullptr; #endif /*! * Constructs a new leaf node representing the specified rectangular * region \a r and storing a nonempty point list \a p. */ LeafNode( const rectangle& r, const point_list& p ) : Node( r ) , points( p ) { PCL_CHECK( !points.IsEmpty() ) } /*! * Constructs a new leaf node representing the specified rectangular * region \a r and storing the specified point \a p. */ LeafNode( const rectangle& r, const point& p ) : Node( r ) { points << p; } /*! * Returns the number of points contained by this leaf node. Under normal * conditions, the returned value must be > 0. */ int Length() const { return int( points.Length() ); } }; // ------------------------------------------------------------------------- /*! * Constructs an empty quadtree. */ QuadTree() = default; /*! * Constructs a quadtree and builds it for the specified list of \a points. * * \param points A list of points that will be stored in this * quadtree. * * \param bucketCapacity The maximum number of points in a leaf tree node. * Must be ≥ 1. The default value is 40. * * \param epsilon The minimum allowed dimension of a quadtree node * region. Must be larger than or equal to twice the * machine epsilon for the \c coordinate type. The * default value is 1e-8. * * If the specified list of \a points is empty, this constructor yields an * empty quadtree. */ QuadTree( const point_list& points, int bucketCapacity = __PCL_QUADTREE_DEFAULT_BUCKET_CAPACITY, double epsilon = __PCL_QUADTREE_DEFAULT_EPSILON ) { Build( points, bucketCapacity, epsilon ); } /*! * Constructs a quadtree and builds it for the specified list of \a points * and a prescribed rectangular search region. * * \param rect The rectangular search region. * * \param points A list of points that will be stored in this * quadtree. * * \param bucketCapacity The maximum number of points in a leaf tree node. * Must be ≥ 1. The default value is 40. * * \param epsilon The minimum allowed dimension of a quadtree node * region. Must be larger than or equal to twice the * machine epsilon for the \c coordinate type. The * default value is 1e-8. * * If the specified list of \a points is empty, or if no points lie within * the \a rect region, this constructor yields an empty quadtree. */ QuadTree( const rectangle& rect, const point_list& points, int bucketCapacity = __PCL_QUADTREE_DEFAULT_BUCKET_CAPACITY, double epsilon = __PCL_QUADTREE_DEFAULT_EPSILON ) { Build( rect, points, bucketCapacity, epsilon ); } /*! * Copy constructor. Copy construction is disabled because this class uses * internal data structures that cannot be copy-constructed. However, * %QuadTree implements move construction and move assignment. */ QuadTree( const QuadTree& ) = delete; /*! * Copy assignment operator. Copy assignment is disabled because this class * uses internal data structures that cannot be copy-assigned. However, * %QuadTree implements move assignment and move construction. */ QuadTree& operator =( const QuadTree& ) = delete; /*! * Move constructor. */ QuadTree( QuadTree&& x ) : m_root( x.m_root ) , m_bucketCapacity( x.m_bucketCapacity ) , m_epsilon( x.m_epsilon ) , m_length( x.m_length ) { x.m_root = nullptr; x.m_length = 0; } /*! * Move assignment operator. Returns a reference to this object. */ QuadTree& operator =( QuadTree&& x ) { if ( &x != this ) { DestroyTree( m_root ); m_root = x.m_root; m_bucketCapacity = x.m_bucketCapacity; m_epsilon = x.m_epsilon; m_length = x.m_length; x.m_root = nullptr; x.m_length = 0; } return *this; } /*! * Destroys a quadtree. All the stored point objects are deleted. */ ~QuadTree() { Clear(); } /*! * Removes all the stored point objects, yielding an empty quadtree. */ void Clear() { DestroyTree( m_root ); m_root = nullptr; m_length = 0; } /*! * Builds a new quadtree for the specified list of \a points. * * \param points A list of points that will be stored in this * quadtree. * * \param bucketCapacity The maximum number of points in a leaf tree node. * Must be ≥ 1. The default value is 40. * * \param epsilon The minimum allowed dimension of a quadtree node * region. Must be larger than or equal to twice the * machine epsilon for the \c coordinate type. The * default value is 1e-8. * * If the tree stores point objects before calling this function, they are * destroyed and removed before building a new tree. * * If the specified list of \a points is empty, this member function yields * an empty quadtree. */ void Build( const point_list& points, int bucketCapacity = __PCL_QUADTREE_DEFAULT_BUCKET_CAPACITY, double epsilon = __PCL_QUADTREE_DEFAULT_EPSILON ) { Clear(); m_bucketCapacity = Max( 1, bucketCapacity ); m_epsilon = Max( 2*std::numeric_limits::epsilon(), epsilon ); if ( !points.IsEmpty() ) { component x = points[0][0]; component y = points[0][1]; rectangle rect( x, y, x, y ); for ( const point& p : points ) { x = p[0]; y = p[1]; if ( x < rect.x0 ) rect.x0 = x; if ( y < rect.y0 ) rect.y0 = y; if ( x > rect.x1 ) rect.x1 = x; if ( y > rect.y1 ) rect.y1 = y; } m_root = BuildTree( rect, points ); } } /*! * Builds a new quadtree with the specified list of \a points and a * prescribed rectangular search region. * * \param rect The rectangular search region. * * \param points A list of points to be stored in this quadtree. * Only points included in the specified \a rect * search region will be inserted in the tree. * All points outside \a rect will be ignored. * * \param bucketCapacity The maximum number of points in a leaf tree node. * Must be ≥ 1. The default value is 40. * * \param epsilon The minimum allowed dimension of a quadtree node * region. Must be larger than or equal to twice the * machine epsilon for the \c coordinate type. The * default value is 1e-8. * * If the tree stores point objects before calling this function, they are * destroyed and removed before building a new tree. * * If the specified list of \a points is empty, or if no points lie within * the \a rect region, this member function yields an empty quadtree. */ void Build( const rectangle& rect, const point_list& points, int bucketCapacity = __PCL_QUADTREE_DEFAULT_BUCKET_CAPACITY, double epsilon = __PCL_QUADTREE_DEFAULT_EPSILON ) { Clear(); m_bucketCapacity = Max( 1, bucketCapacity ); m_epsilon = Max( 2*std::numeric_limits::epsilon(), epsilon ); if ( !points.IsEmpty() ) m_root = BuildTree( rect.Ordered(), points ); } /*! * Performs a rectangular range search in this quadtree. * * \param rect The rectangular search region. * * Returns a (possibly empty) list with all the points found in this tree * within the specified search range. */ point_list Search( const rectangle& rect ) const { point_list found; SearchTree( found, rect.Ordered(), m_root ); return found; } /*! * Performs a rectangular range search in this quadtree. * * \param rect The rectangular search region. * * \param callback Callback functional. * * \param data Callback data. * * The callback function prototype should be: * * \code void callback( const point& pt, void* data ) \endcode * * The callback function will be called once for each point found in the * tree within the specified search range. */ template void Search( const rectangle& rect, F callback, void* data ) const { SearchTree( rect.Ordered(), callback, data, m_root ); } /*! * Inserts a point in this quadtree. */ void Insert( const point& pt ) { if ( m_root != nullptr ) InsertTree( pt, m_root ); else { component x = pt[0]; component y = pt[1]; m_root = new LeafNode( rectangle( x, y, x, y ), pt ); } } /*! * Deletes all points in this quadtree equal to the specified point. */ void Delete( const point& pt ) { DeleteTree( pt, m_root ); } /*! * Deletes all points in this quadtree included in the specified rectangular * region \a rect. */ void Delete( const rectangle& rect ) { DeleteTree( rect.Ordered(), m_root ); } /*! * Returns the bucket capacity of this quadtree, or the maximum number of * points that can be stored in a leaf tree node. This parameter is * specified when a new tree is built. */ int BucketCapacity() const { return m_bucketCapacity; } /*! * Returns the total number of points stored in this quadtree. */ size_type Length() const { return m_length; } /*! * Returns true iff this quadtree is empty. */ bool IsEmpty() const { return m_root == nullptr; } /*! * Returns a pointer to the root node of this quadtree, or nullptr if this * quadtree is empty. * * The returned pointer is const qualified to forbid uncontrolled * alterations that might invalidate the quadtree structure. */ const Node* Root() const { return m_root; } /*! * Returns a pointer to the (mutable) root node of this quadtree, or nullptr * if this quadtree is empty. */ Node* Root() { return m_root; } /*! * Returns a pointer to the (immutable) leaf node of this quadtree that * includes the specified point \a p in the plane, or nullptr if no such * leaf node exists in this quadtree. */ const LeafNode* LeafNodeAt( rectangle::point p ) const { return SearchLeafNode( p, m_root ); } /*! * Returns a pointer to the leaf node of this quadtree that includes the * specified point \a p in the plane, or nullptr if no such leaf node exists * in this quadtree. */ LeafNode* LeafNodeAt( rectangle::point p ) { return SearchLeafNode( p, m_root ); } /*! * Returns a pointer to the (immutable) node of this quadtree that includes * the specified point \a p in the plane, or nullptr if no such node exists * in this quadtree. * * The returned node can be a leaf node or a structural node. This function * should only return nullptr if the specified point \a p is exterior to the * root rectangular region of this quadtree, or if this quadtree is empty. */ const Node* NodeAt( rectangle::point p ) const { return SearchNode( p, m_root ); } /*! * Returns a pointer to the node of this quadtree that includes the * specified point \a p in the plane, or nullptr if no such node exists in * this quadtree. * * The returned node can be a leaf node or a structural node. This function * should only return nullptr if the specified point \a p is exterior to the * root rectangular region of this quadtree, or if this quadtree is empty. */ Node* NodeAt( rectangle::point p ) { return SearchNode( p, m_root ); } /*! * Forces a quadtree subdivision of the leaf node that includes the * specified point \a p in the plane. * * Returns the newly created structural node. This function should only * return nullptr if the specified point \a p is exterior to the root * rectangular region of this quadtree, or if this quadtree is empty. It * could also return nullptr in degenerate cases where no further * subdivision of the plane would be possible because of numerical limits. */ Node* SplitAt( rectangle::point p ) { Node* node = SearchDeepestStructuralNodeAt( p, m_root ); if ( node != nullptr ) { Node** leaf = nullptr; if ( node->nw != nullptr && node->nw->Includes( p ) ) leaf = &node->nw; else if ( node->ne != nullptr && node->ne->Includes( p ) ) leaf = &node->ne; else if ( node->sw != nullptr && node->sw->Includes( p ) ) leaf = &node->sw; else if ( node->se != nullptr && node->se->Includes( p ) ) leaf = &node->se; if ( leaf != nullptr ) // cannot be false! { Node* newNode = SplitLeafNode( *leaf ); if ( newNode != nullptr ) // should be true { delete static_cast( *leaf ); *leaf = newNode; } return newNode; } } return nullptr; } /*! * Performs a recursive left-to-right, depth-first traversal of the subtree * rooted at the specified \a node, invoking the specified function \a f * successively for each leaf node. * * The function \a f must be compatible with the form: * * \code void f( const rectangle& r, const point_list& p, void*& d ) \endcode * * where \a r is the plane region covered by the current leaf node, \a p is * the list of points in the current leaf node, and \a d is the optional * pointer to arbitrary data associated with the current leaf node. * * The sequence of calls for the subtrees in each non-leaf node is: NW, NE, * SW, SE. Only non-empty leaf nodes are included in the traversal, hence * the function \a f will be invoked exclusively for non-empty point lists. * * If the specified \a node is nullptr, this function takes no action. */ template static void Traverse( const Node* node, F f ) { if ( node != nullptr ) if ( node->IsLeaf() ) f( node->rect, static_cast( node )->points, static_cast( node )->data ); else { Traverse( node->nw, f ); Traverse( node->ne, f ); Traverse( node->sw, f ); Traverse( node->se, f ); } } /*! * Performs a recursive left-to-right, depth-first traversal of the subtree * rooted at the specified (mutable) \a node, invoking the specified * function \a f successively for each leaf node. * * The function \a f must be compatible with the form: * * \code void f( const rectangle& r, point_list& p, void*& d ) \endcode * * where \a r is the plane region covered by the current leaf node, \a p is * the list of points in the current leaf node, and \a d is the optional * pointer to arbitrary data associated with the current leaf node. * * The sequence of calls for the subtrees in each non-leaf node is: NW, NE, * SW, SE. Only non-empty leaf nodes are included in the traversal, hence * the function \a f will be invoked exclusively for non-empty point lists. * * If the specified \a node is nullptr, this function takes no action. */ template static void Traverse( Node* node, F f ) { if ( node != nullptr ) if ( node->IsLeaf() ) f( node->rect, static_cast( node )->points, static_cast( node )->data ); else { Traverse( node->nw, f ); Traverse( node->ne, f ); Traverse( node->sw, f ); Traverse( node->se, f ); } } /*! * Performs a recursive left-to-right, depth-first traversal of the entire * quadtree, invoking the specified function \a f successively for each leaf * node. Calling this function is equivalent to: * * \code Traverse( Root(), f ) \endcode * * If this quadtree is empty, this function takes no action. */ template void Traverse( F f ) const { Traverse( m_root, f ); } /*! * Performs a recursive left-to-right, depth-first traversal of the entire * (mutable) quadtree, invoking the specified function \a f successively for * each leaf node. Calling this function is equivalent to: * * \code Traverse( Root(), f ) \endcode * * If this quadtree is empty, this function takes no action. */ template void Traverse( F f ) { Traverse( m_root, f ); } /*! * Returns the total number of existing nodes in the subtree rooted at the * specified \a node, including structural and leaf nodes. */ static size_type NumberOfNodes( const Node* node ) { size_type N = 0; GetNumberOfNodes( N, node ); return N; } /*! * Returns the total number of existing nodes in this quadtree, including * structural and leaf nodes. */ size_type NumberOfNodes() const { return NumberOfNodes( m_root ); } /*! * Returns the total number of existing leaf nodes in the subtree rooted at * the specified \a node. */ static size_type NumberOfLeafNodes( const Node* node ) { size_type N = 0; GetNumberOfLeafNodes( N, node ); return N; } /*! * Returns the total number of existing leaf nodes in this quadtree. */ size_type NumberOfLeafNodes() const { return NumberOfLeafNodes( m_root ); } /*! * Returns the height of the subtree rooted at the specified \a node. */ static int Height( const Node* node ) { return TreeHeight( node, 0 ); } /*! * Returns the height of this quadtree. */ int Height() const { return Height( m_root ); } /*! * Exchanges two %QuadTree objects \a x1 and \a x2. */ friend void Swap( QuadTree& x1, QuadTree& x2 ) { pcl::Swap( x1.m_root, x2.m_root ); pcl::Swap( x1.m_bucketCapacity, x2.m_bucketCapacity ); pcl::Swap( x1.m_epsilon, x2.m_epsilon ); pcl::Swap( x1.m_length, x2.m_length ); } private: Node* m_root = nullptr; int m_bucketCapacity = 0; double m_epsilon = 0; size_type m_length = 0; Node* NewLeafNode( const rectangle& rect, const point_list& points ) { m_length += points.Length(); return new LeafNode( rect, points ); } LeafNode* SearchLeafNode( const rectangle::point& p, const Node* node ) const { if ( node != nullptr ) if ( node->Includes( p ) ) { if ( node->IsLeaf() ) return const_cast( static_cast( node ) ); LeafNode* child = SearchLeafNode( p, node->nw ); if ( child == nullptr ) { child = SearchLeafNode( p, node->ne ); if ( child == nullptr ) { child = SearchLeafNode( p, node->sw ); if ( child == nullptr ) child = SearchLeafNode( p, node->se ); } } return child; } return nullptr; } Node* SearchNode( const rectangle::point& p, const Node* node ) const { if ( node != nullptr ) if ( node->Includes( p ) ) { if ( node->IsLeaf() ) return const_cast( node ); Node* child = SearchNode( p, node->nw ); if ( child == nullptr ) { child = SearchNode( p, node->ne ); if ( child == nullptr ) { child = SearchNode( p, node->sw ); if ( child == nullptr ) { child = SearchNode( p, node->se ); if ( child == nullptr ) return const_cast( node ); } } } return child; } return nullptr; } Node* SearchDeepestStructuralNodeAt( const rectangle::point& p, const Node* node ) const { if ( node != nullptr ) if ( !node->IsLeaf() ) if ( node->Includes( p ) ) { Node* child = SearchDeepestStructuralNodeAt( p, node->nw ); if ( child == nullptr ) { child = SearchDeepestStructuralNodeAt( p, node->ne ); if ( child == nullptr ) { child = SearchDeepestStructuralNodeAt( p, node->sw ); if ( child == nullptr ) { child = SearchDeepestStructuralNodeAt( p, node->se ); if ( child == nullptr ) return const_cast( node ); } } } return child; } return nullptr; } Node* BuildTree( const rectangle& rect, const point_list& points ) { if ( points.IsEmpty() ) return nullptr; if ( points.Length() <= size_type( m_bucketCapacity ) ) return NewLeafNode( rect, points ); double x2 = (rect.x0 + rect.x1)/2; double y2 = (rect.y0 + rect.y1)/2; // Prevent geometrically degenerate subtrees. For safety, we enforce // minimum region dimensions larger than twice the machine epsilon for // the rectangle coordinate type. if ( x2 - rect.x0 < m_epsilon || rect.x1 - x2 < m_epsilon || y2 - rect.y0 < m_epsilon || rect.y1 - y2 < m_epsilon ) { return NewLeafNode( rect, points ); } point_list nw, ne, sw, se; for ( const point& p : points ) { component x = p[0]; component y = p[1]; if ( x <= x2 ) { if ( y <= y2 ) nw << p; else sw << p; } else { if ( y <= y2 ) ne << p; else se << p; } } Node* node = new Node( rect ); node->nw = BuildTree( rectangle( rect.x0, rect.y0, x2, y2 ), nw ); node->ne = BuildTree( rectangle( x2, rect.y0, rect.x1, y2 ), ne ); node->sw = BuildTree( rectangle( rect.x0, y2, x2, rect.y1 ), sw ); node->se = BuildTree( rectangle( x2, y2, rect.x1, rect.y1 ), se ); // Further degeneracies may result, e.g. if the point class is not // behaving as expected. Do not allow them. if ( node->IsLeaf() ) { delete node; return NewLeafNode( rect, points ); } return node; } Node* SplitLeafNode( const Node* node ) { if ( node == nullptr || !node->IsLeaf() ) return nullptr; double x2 = (node->rect.x0 + node->rect.x1)/2; double y2 = (node->rect.y0 + node->rect.y1)/2; // Prevent geometrically degenerate subtrees. For safety, we enforce // minimum region dimensions larger than twice the machine epsilon for // the rectangle coordinate type. if ( x2 - node->rect.x0 < m_epsilon || node->rect.x1 - x2 < m_epsilon || y2 - node->rect.y0 < m_epsilon || node->rect.y1 - y2 < m_epsilon ) { return nullptr; } const LeafNode* leaf = static_cast( node ); point_list nw, ne, sw, se; for ( const point& p : leaf->points ) { component x = p[0]; component y = p[1]; if ( x <= x2 ) { if ( y <= y2 ) nw << p; else sw << p; } else { if ( y <= y2 ) ne << p; else se << p; } } size_type savedLength = m_length; Node* newNode = new Node( node->rect ); newNode->nw = BuildTree( rectangle( node->rect.x0, node->rect.y0, x2, y2 ), nw ); newNode->ne = BuildTree( rectangle( x2, node->rect.y0, node->rect.x1, y2 ), ne ); newNode->sw = BuildTree( rectangle( node->rect.x0, y2, x2, node->rect.y1 ), sw ); newNode->se = BuildTree( rectangle( x2, y2, node->rect.x1, node->rect.y1 ), se ); m_length = savedLength; // Further degeneracies may result, e.g. if the point class is not // behaving as expected. Do not allow them. if ( newNode->IsLeaf() ) { delete newNode; return nullptr; } return newNode; } void SearchTree( point_list& found, const rectangle& rect, const Node* node ) const { if ( node != nullptr ) if ( node->Intersects( rect ) ) if ( node->IsLeaf() ) { const LeafNode* leaf = static_cast( node ); for ( const point& p : leaf->points ) { component x = p[0]; if ( x >= rect.x0 ) if ( x <= rect.x1 ) { component y = p[1]; if ( y >= rect.y0 ) if ( y <= rect.y1 ) found << p; } } } else { SearchTree( found, rect, node->nw ); SearchTree( found, rect, node->ne ); SearchTree( found, rect, node->sw ); SearchTree( found, rect, node->se ); } } template void SearchTree( const rectangle& rect, F callback, void* data, const Node* node ) const { if ( node != nullptr ) if ( node->Intersects( rect ) ) if ( node->IsLeaf() ) { const LeafNode* leaf = static_cast( node ); for ( const point& p : leaf->points ) { component x = p[0]; if ( x >= rect.x0 ) if ( x <= rect.x1 ) { component y = p[1]; if ( y >= rect.y0 ) if ( y <= rect.y1 ) callback( p, data ); } } } else { SearchTree( rect, callback, data, node->nw ); SearchTree( rect, callback, data, node->ne ); SearchTree( rect, callback, data, node->sw ); SearchTree( rect, callback, data, node->se ); } } void InsertTree( const point& pt, Node*& node ) { PCL_CHECK( node != nullptr ) component x = pt[0]; component y = pt[1]; if ( x < node->rect.x0 ) node->rect.x0 = x; else if ( x > node->rect.x1 ) node->rect.x1 = x; if ( y < node->rect.y0 ) node->rect.y0 = y; else if ( y > node->rect.y1 ) node->rect.y1 = y; if ( node->IsLeaf() ) { LeafNode* leaf = static_cast( node ); if ( leaf->Length() < m_bucketCapacity ) leaf->points << pt; else { rectangle rect = leaf->rect; double x2 = (rect.x0 + rect.x1)/2; double y2 = (rect.y0 + rect.y1)/2; // Prevent geometrically degenerate subtrees. For safety, we // enforce minimum region dimensions larger than twice the // machine epsilon for the rectangle coordinate type. if ( x2 - rect.x0 < m_epsilon || rect.x1 - x2 < m_epsilon || y2 - rect.y0 < m_epsilon || rect.y1 - y2 < m_epsilon ) { leaf->points << pt; } else { point_list nw, ne, sw, se; for ( const point& p : leaf->points ) { component x = p[0]; component y = p[1]; if ( x <= x2 ) { if ( y <= y2 ) nw << p; else sw << p; } else { if ( y <= y2 ) ne << p; else se << p; } } if ( x <= x2 ) { if ( y <= y2 ) nw << pt; else sw << pt; } else { if ( y <= y2 ) ne << pt; else se << pt; } delete leaf; node = new Node( rect ); if ( !nw.IsEmpty() ) node->nw = new LeafNode( rectangle( rect.x0, rect.y0, x2, y2 ), nw ); if ( !ne.IsEmpty() ) node->ne = new LeafNode( rectangle( x2, rect.y0, rect.x1, y2 ), ne ); if ( !sw.IsEmpty() ) node->sw = new LeafNode( rectangle( rect.x0, y2, x2, rect.y1 ), sw ); if ( !se.IsEmpty() ) node->se = new LeafNode( rectangle( x2, y2, rect.x1, rect.y1 ), se ); } } ++m_length; } else { rectangle rect = node->rect; double x2 = (rect.x0 + rect.x1)/2; double y2 = (rect.y0 + rect.y1)/2; if ( pt[0] <= x2 ) { if ( pt[1] <= y2 ) { if ( node->nw == nullptr ) node->nw = new LeafNode( rectangle( rect.x0, rect.y0, x2, y2 ), pt ); else InsertTree( pt, node->nw ); } else { if ( node->sw == nullptr ) node->sw = new LeafNode( rectangle( rect.x0, y2, x2, rect.y1 ), pt ); else InsertTree( pt, node->sw ); } } else { if ( pt[1] <= y2 ) { if ( node->ne == nullptr ) node->ne = new LeafNode( rectangle( x2, rect.y0, rect.x1, y2 ), pt ); else InsertTree( pt, node->ne ); } else { if ( node->se == nullptr ) node->se = new LeafNode( rectangle( x2, y2, rect.x1, rect.y1 ), pt ); else InsertTree( pt, node->se ); } } } } void DeleteTree( const rectangle& rect, Node*& node ) { if ( node != nullptr ) if ( node->Intersects( rect ) ) { if ( node->IsLeaf() ) { LeafNode* leaf = static_cast( node ); point_list points; for ( const point& p : leaf->points ) { component x = p[0]; if ( x >= rect.x0 ) if ( x <= rect.x1 ) { component y = p[1]; if ( y >= rect.y0 ) if ( y <= rect.y1 ) { --m_length; continue; } } points << p; } if ( points.IsEmpty() ) { delete leaf; node = nullptr; } else leaf->points = points; } else { DeleteTree( rect, node->nw ); DeleteTree( rect, node->ne ); DeleteTree( rect, node->sw ); DeleteTree( rect, node->se ); if ( node->IsLeaf() ) { delete node; node = nullptr; } } } } void DeleteTree( const point& pt, Node*& node ) { if ( node != nullptr ) { component x = pt[0]; component y = pt[1]; if ( node->Includes( x, y ) ) if ( node->IsLeaf() ) { LeafNode* leaf = static_cast( node ); point_list points; for ( const point& p : leaf->points ) { if ( p[0] == x ) if ( p[1] == y ) { --m_length; continue; } points << p; } if ( points.IsEmpty() ) { delete leaf; node = nullptr; } else leaf->points = points; } else { DeleteTree( pt, node->nw ); DeleteTree( pt, node->ne ); DeleteTree( pt, node->sw ); DeleteTree( pt, node->se ); if ( node->IsLeaf() ) { delete node; node = nullptr; } } } } void DestroyTree( Node* node ) { if ( node != nullptr ) if ( node->IsLeaf() ) delete static_cast( node ); else { DestroyTree( node->nw ); DestroyTree( node->ne ); DestroyTree( node->sw ); DestroyTree( node->se ); delete node; } } static void GetNumberOfNodes( size_type& N, const Node* node ) { if ( node != nullptr ) { ++N; GetNumberOfNodes( N, node->nw ); GetNumberOfNodes( N, node->ne ); GetNumberOfNodes( N, node->sw ); GetNumberOfNodes( N, node->se ); } } static void GetNumberOfLeafNodes( size_type& N, const Node* node ) { if ( node != nullptr ) { if ( node->IsLeaf() ) ++N; GetNumberOfLeafNodes( N, node->nw ); GetNumberOfLeafNodes( N, node->ne ); GetNumberOfLeafNodes( N, node->sw ); GetNumberOfLeafNodes( N, node->se ); } } static int TreeHeight( const Node* node, int h ) { if ( node == nullptr ) return h; return h + 1 + Max( Max( Max( TreeHeight( node->nw, h ), TreeHeight( node->ne, h ) ), TreeHeight( node->sw, h ) ), TreeHeight( node->se, h ) ); } }; // ---------------------------------------------------------------------------- } // pcl #endif // __PCL_QuadTree_h // ---------------------------------------------------------------------------- // EOF pcl/QuadTree.h - Released 2022-03-12T18:59:29Z