From ef2c94069ffda809d83a9bc791b2aeeb99ca55d8 Mon Sep 17 00:00:00 2001 From: Maciej Suminski Date: Thu, 28 Mar 2013 17:48:29 +0100 Subject: [PATCH] Added template RTREE and class VIEW_RTREE (R-tree for fast spatial indexing of VIEW items; for future usage in GAL) --- include/rtree.h | 1758 +++++++++++++++++++++++++++++++++++++ include/view/view_rtree.h | 92 ++ 2 files changed, 1850 insertions(+) create mode 100644 include/rtree.h create mode 100644 include/view/view_rtree.h diff --git a/include/rtree.h b/include/rtree.h new file mode 100644 index 0000000000..d507f84e11 --- /dev/null +++ b/include/rtree.h @@ -0,0 +1,1758 @@ +/* + * R-Tree index + * + * from http://www.superliminal.com/ + * + * LICENSE : Entirely free for all uses. Enjoy! + * + * AUTORS + * - 1983 Original algorithm and test code by Antonin Guttman and Michael Stonebraker, UC Berkely + * - 1994 ANCI C ported from original test code by Melinda Green - melinda@superliminal.com + * - 1995 Sphere volume fix for degeneracy problem submitted by Paul Brook + * - 2004 Templated C++ port by Greg Douglas + * - 2008 Portability issues fixed by Maxence Laurent + */ + +#ifndef RTREE_H +#define RTREE_H + +// NOTE This file compiles under MSVC 6 SP5 and MSVC .Net 2003 it may not work on other compilers without modification. + +// NOTE These next few lines may be win32 specific, you may need to modify them to compile on other platform +#include +#include +#include +#include + +#define ASSERT assert // RTree uses ASSERT( condition ) +#ifndef Min + #define Min std::min +#endif // Min +#ifndef Max + #define Max std::max +#endif // Max + +// +// RTree.h +// + +#define RTREE_TEMPLATE template +#define RTREE_SEARCH_TEMPLATE template +#define RTREE_QUAL RTree +#define RTREE_SEARCH_QUAL RTree + +#define RTREE_DONT_USE_MEMPOOLS // This version does not contain a fixed memory allocator, fill in lines with EXAMPLE to implement one. +#define RTREE_USE_SPHERICAL_VOLUME // Better split classification, may be slower on some systems + +// Fwd decl +class RTFileStream; // File I/O helper class, look below for implementation and notes. + + +/// \class RTree +/// Implementation of RTree, a multidimensional bounding rectangle tree. +/// Example usage: For a 3-dimensional tree use RTree myTree; +/// +/// This modified, templated C++ version by Greg Douglas at Auran (http://www.auran.com) +/// +/// DATATYPE Referenced data, should be int, void*, obj* etc. no larger than sizeof and simple type +/// ELEMTYPE Type of element such as int or float +/// NUMDIMS Number of dimensions such as 2 or 3 +/// ELEMTYPEREAL Type of element that allows fractional and large values such as float or double, for use in volume calcs +/// +/// NOTES: Inserting and removing data requires the knowledge of its constant Minimal Bounding Rectangle. +/// This version uses new/delete for nodes, I recommend using a fixed size allocator for efficiency. +/// Instead of using a callback function for returned results, I recommend and efficient pre-sized, grow-only memory +/// array similar to MFC CArray or STL Vector for returning search query result. +/// +template +class RTree +{ +protected: + + struct Node; // Fwd decl. Used by other internal structs and iterator +public: + + // These constant must be declared after Branch and before Node struct + // Stuck up here for MSVC 6 compiler. NSVC .NET 2003 is much happier. + enum { + MAXNODES = TMAXNODES, ///< Max elements in node + MINNODES = TMINNODES, ///< Min elements in node + }; +public: + + RTree(); + virtual ~RTree(); + + /// Insert entry + /// \param a_min Min of bounding rect + /// \param a_max Max of bounding rect + /// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed. + void Insert( const ELEMTYPE a_min[NUMDIMS], + const ELEMTYPE a_max[NUMDIMS], + const DATATYPE& a_dataId ); + + /// Remove entry + /// \param a_min Min of bounding rect + /// \param a_max Max of bounding rect + /// \param a_dataId Positive Id of data. Maybe zero, but negative numbers not allowed. + void Remove( const ELEMTYPE a_min[NUMDIMS], + const ELEMTYPE a_max[NUMDIMS], + const DATATYPE& a_dataId ); + + /// Find all within search rectangle + /// \param a_min Min of search bounding rect + /// \param a_max Max of search bounding rect + /// \param a_searchResult Search result array. Caller should set grow size. Function will reset, not append to array. + /// \param a_resultCallback Callback function to return result. Callback should return 'true' to continue searching + /// \param a_context User context to pass as parameter to a_resultCallback + /// \return Returns the number of entries found + int Search( const ELEMTYPE a_min[NUMDIMS], + const ELEMTYPE a_max[NUMDIMS], + bool a_resultCallback( DATATYPE a_data, void* a_context ), + void* a_context ); + + template + int Search( const ELEMTYPE a_min[NUMDIMS], const ELEMTYPE a_max[NUMDIMS], VISITOR& a_visitor ) + { + #ifdef _DEBUG + + for( int index = 0; index 0; } + + /// Access the current data element. Caller must be sure iterator is not NULL first. + DATATYPE& operator*() + { + ASSERT( IsNotNull() ); + StackElement& curTos = m_stack[m_tos - 1]; + return curTos.m_node->m_branch[curTos.m_branchIndex].m_data; + } + + /// Access the current data element. Caller must be sure iterator is not NULL first. + const DATATYPE& operator*() const + { + ASSERT( IsNotNull() ); + StackElement& curTos = m_stack[m_tos - 1]; + return curTos.m_node->m_branch[curTos.m_branchIndex].m_data; + } + + /// Find the next data element + bool operator++() { return FindNextData(); } + + /// Get the bounds for this node + void GetBounds( ELEMTYPE a_min[NUMDIMS], ELEMTYPE a_max[NUMDIMS] ) + { + ASSERT( IsNotNull() ); + StackElement& curTos = m_stack[m_tos - 1]; + Branch& curBranch = curTos.m_node->m_branch[curTos.m_branchIndex]; + + for( int index = 0; index < NUMDIMS; ++index ) + { + a_min[index] = curBranch.m_rect.m_min[index]; + a_max[index] = curBranch.m_rect.m_max[index]; + } + } + +private: + + /// Reset iterator + void Init() { m_tos = 0; } + + /// Find the next data element in the tree (For internal use only) + bool FindNextData() + { + for( ; ; ) + { + if( m_tos <= 0 ) + { + return false; + } + + StackElement curTos = Pop(); // Copy stack top cause it may change as we use it + + if( curTos.m_node->IsLeaf() ) + { + // Keep walking through data while we can + if( curTos.m_branchIndex + 1 < curTos.m_node->m_count ) + { + // There is more data, just point to the next one + Push( curTos.m_node, curTos.m_branchIndex + 1 ); + return true; + } + + // No more data, so it will fall back to previous level + } + else + { + if( curTos.m_branchIndex + 1 < curTos.m_node->m_count ) + { + // Push sibling on for future tree walk + // This is the 'fall back' node when we finish with the current level + Push( curTos.m_node, curTos.m_branchIndex + 1 ); + } + + // Since cur node is not a leaf, push first of next level to get deeper into the tree + Node* nextLevelnode = curTos.m_node->m_branch[curTos.m_branchIndex].m_child; + Push( nextLevelnode, 0 ); + + // If we pushed on a new leaf, exit as the data is ready at TOS + if( nextLevelnode->IsLeaf() ) + { + return true; + } + } + } + } + + /// Push node and branch onto iteration stack (For internal use only) + void Push( Node* a_node, int a_branchIndex ) + { + m_stack[m_tos].m_node = a_node; + m_stack[m_tos].m_branchIndex = a_branchIndex; + ++m_tos; + ASSERT( m_tos <= MAX_STACK ); + } + + /// Pop element off iteration stack (For internal use only) + StackElement& Pop() + { + ASSERT( m_tos > 0 ); + --m_tos; + return m_stack[m_tos]; + } + + StackElement m_stack[MAX_STACK]; ///< Stack as we are doing iteration instead of recursion + int m_tos; ///< Top Of Stack index + + friend class RTree; // Allow hiding of non-public functions while allowing manipulation by logical owner + }; + + + /// Get 'first' for iteration + void GetFirst( Iterator& a_it ) + { + a_it.Init(); + Node* first = m_root; + + while( first ) + { + if( first->IsInternalNode() && first->m_count > 1 ) + { + a_it.Push( first, 1 ); // Descend sibling branch later + } + else if( first->IsLeaf() ) + { + if( first->m_count ) + { + a_it.Push( first, 0 ); + } + + break; + } + + first = first->m_branch[0].m_child; + } + } + + /// Get Next for iteration + void GetNext( Iterator& a_it ) { ++a_it; } + + /// Is iterator NULL, or at end? + bool IsNull( Iterator& a_it ) { return a_it.IsNull(); } + + /// Get object at iterator position + DATATYPE& GetAt( Iterator& a_it ) { return *a_it; } +protected: + + /// Minimal bounding rectangle (n-dimensional) + struct Rect + { + ELEMTYPE m_min[NUMDIMS]; ///< Min dimensions of bounding box + ELEMTYPE m_max[NUMDIMS]; ///< Max dimensions of bounding box + }; + + /// May be data or may be another subtree + /// The parents level determines this. + /// If the parents level is 0, then this is data + struct Branch + { + Rect m_rect; ///< Bounds + union + { + Node* m_child; ///< Child node + DATATYPE m_data; ///< Data Id or Ptr + }; + }; + + /// Node for each branch level + struct Node + { + bool IsInternalNode() { return m_level > 0; } // Not a leaf, but a internal node + bool IsLeaf() { return m_level == 0; } // A leaf, contains data + + int m_count; ///< Count + int m_level; ///< Leaf is zero, others positive + Branch m_branch[MAXNODES]; ///< Branch + }; + + /// A link list of nodes for reinsertion after a delete operation + struct ListNode + { + ListNode* m_next; ///< Next in list + Node* m_node; ///< Node + }; + + /// Variables for finding a split partition + struct PartitionVars + { + int m_partition[MAXNODES + 1]; + int m_total; + int m_minFill; + int m_taken[MAXNODES + 1]; + int m_count[2]; + Rect m_cover[2]; + ELEMTYPEREAL m_area[2]; + + Branch m_branchBuf[MAXNODES + 1]; + int m_branchCount; + Rect m_coverSplit; + ELEMTYPEREAL m_coverSplitArea; + }; + + Node* AllocNode(); + void FreeNode( Node* a_node ); + void InitNode( Node* a_node ); + void InitRect( Rect* a_rect ); + bool InsertRectRec( Rect* a_rect, + const DATATYPE& a_id, + Node* a_node, + Node** a_newNode, + int a_level ); + bool InsertRect( Rect* a_rect, const DATATYPE& a_id, Node** a_root, int a_level ); + Rect NodeCover( Node* a_node ); + bool AddBranch( Branch* a_branch, Node* a_node, Node** a_newNode ); + void DisconnectBranch( Node* a_node, int a_index ); + int PickBranch( Rect* a_rect, Node* a_node ); + Rect CombineRect( Rect* a_rectA, Rect* a_rectB ); + void SplitNode( Node* a_node, Branch* a_branch, Node** a_newNode ); + ELEMTYPEREAL RectSphericalVolume( Rect* a_rect ); + ELEMTYPEREAL RectVolume( Rect* a_rect ); + ELEMTYPEREAL CalcRectVolume( Rect* a_rect ); + void GetBranches( Node* a_node, Branch* a_branch, PartitionVars* a_parVars ); + void ChoosePartition( PartitionVars* a_parVars, int a_minFill ); + void LoadNodes( Node* a_nodeA, Node* a_nodeB, PartitionVars* a_parVars ); + void InitParVars( PartitionVars* a_parVars, int a_maxRects, int a_minFill ); + void PickSeeds( PartitionVars* a_parVars ); + void Classify( int a_index, int a_group, PartitionVars* a_parVars ); + bool RemoveRect( Rect* a_rect, const DATATYPE& a_id, Node** a_root ); + bool RemoveRectRec( Rect* a_rect, + const DATATYPE& a_id, + Node* a_node, + ListNode** a_listNode ); + ListNode* AllocListNode(); + void FreeListNode( ListNode* a_listNode ); + bool Overlap( Rect* a_rectA, Rect* a_rectB ); + void ReInsert( Node* a_node, ListNode** a_listNode ); + + bool Search( Node * a_node, Rect * a_rect, int& a_foundCount, bool a_resultCallback( + DATATYPE a_data, + void* a_context ), void* a_context ); + + template + bool Search( Node* a_node, Rect* a_rect, VISITOR& a_visitor ) + { + ASSERT( a_node ); + ASSERT( a_node->m_level >= 0 ); + ASSERT( a_rect ); + + if( a_node->IsInternalNode() ) // This is an internal node in the tree + { + for( int index = 0; index < a_node->m_count; ++index ) + { + if( Overlap( a_rect, &a_node->m_branch[index].m_rect ) ) + { + if( !Search( a_node->m_branch[index].m_child, a_rect, a_visitor ) ) + { + return false; // Don't continue searching + } + } + } + } + else // This is a leaf node + { + for( int index = 0; index < a_node->m_count; ++index ) + { + if( Overlap( a_rect, &a_node->m_branch[index].m_rect ) ) + { + DATATYPE& id = a_node->m_branch[index].m_data; + + a_visitor( id ); + } + } + } + + return true; // Continue searching + } + + void RemoveAllRec( Node* a_node ); + void Reset(); + void CountRec( Node* a_node, int& a_count ); + + bool SaveRec( Node* a_node, RTFileStream& a_stream ); + bool LoadRec( Node* a_node, RTFileStream& a_stream ); + + Node* m_root; ///< Root of tree + ELEMTYPEREAL m_unitSphereVolume; ///< Unit sphere constant for required number of dimensions +}; + + +// Because there is not stream support, this is a quick and dirty file I/O helper. +// Users will likely replace its usage with a Stream implementation from their favorite API. +class RTFileStream +{ + FILE* m_file; +public: + + + RTFileStream() + { + m_file = NULL; + } + + ~RTFileStream() + { + Close(); + } + + bool OpenRead( const char* a_fileName ) + { + m_file = fopen( a_fileName, "rb" ); + + if( !m_file ) + { + return false; + } + + return true; + } + + bool OpenWrite( const char* a_fileName ) + { + m_file = fopen( a_fileName, "wb" ); + + if( !m_file ) + { + return false; + } + + return true; + } + + void Close() + { + if( m_file ) + { + fclose( m_file ); + m_file = NULL; + } + } + + template + size_t Write( const TYPE& a_value ) + { + ASSERT( m_file ); + return fwrite( (void*) &a_value, sizeof(a_value), 1, m_file ); + } + + template + size_t WriteArray( const TYPE* a_array, int a_count ) + { + ASSERT( m_file ); + return fwrite( (void*) a_array, sizeof(TYPE) * a_count, 1, m_file ); + } + + template + size_t Read( TYPE& a_value ) + { + ASSERT( m_file ); + return fread( (void*) &a_value, sizeof(a_value), 1, m_file ); + } + + template + size_t ReadArray( TYPE* a_array, int a_count ) + { + ASSERT( m_file ); + return fread( (void*) a_array, sizeof(TYPE) * a_count, 1, m_file ); + } +}; + + +RTREE_TEMPLATE RTREE_QUAL::RTree() +{ + ASSERT( MAXNODES > MINNODES ); + ASSERT( MINNODES > 0 ); + + + // We only support machine word size simple data type eg. integer index or object pointer. + // Since we are storing as union with non data branch + ASSERT( sizeof(DATATYPE) == sizeof(void*) || sizeof(DATATYPE) == sizeof(int) ); + + // Precomputed volumes of the unit spheres for the first few dimensions + const float UNIT_SPHERE_VOLUMES[] = + { + 0.000000f, 2.000000f, 3.141593f, // Dimension 0,1,2 + 4.188790f, 4.934802f, 5.263789f, // Dimension 3,4,5 + 5.167713f, 4.724766f, 4.058712f, // Dimension 6,7,8 + 3.298509f, 2.550164f, 1.884104f, // Dimension 9,10,11 + 1.335263f, 0.910629f, 0.599265f, // Dimension 12,13,14 + 0.381443f, 0.235331f, 0.140981f, // Dimension 15,16,17 + 0.082146f, 0.046622f, 0.025807f, // Dimension 18,19,20 + }; + + m_root = AllocNode(); + m_root->m_level = 0; + m_unitSphereVolume = (ELEMTYPEREAL) UNIT_SPHERE_VOLUMES[NUMDIMS]; +} + + +RTREE_TEMPLATE +RTREE_QUAL::~RTree() { + Reset(); // Free, or reset node memory +} + + +RTREE_TEMPLATE +void RTREE_QUAL::Insert( const ELEMTYPE a_min[NUMDIMS], + const ELEMTYPE a_max[NUMDIMS], + const DATATYPE& a_dataId ) +{ +#ifdef _DEBUG + + for( int index = 0; indexIsInternalNode() ) // not a leaf node + { + for( int index = 0; index < a_node->m_count; ++index ) + { + CountRec( a_node->m_branch[index].m_child, a_count ); + } + } + else // A leaf node + { + a_count += a_node->m_count; + } +} + + +RTREE_TEMPLATE +bool RTREE_QUAL::Load( const char* a_fileName ) +{ + RemoveAll(); // Clear existing tree + + RTFileStream stream; + + if( !stream.OpenRead( a_fileName ) ) + { + return false; + } + + bool result = Load( stream ); + + stream.Close(); + + return result; +}; + + +RTREE_TEMPLATE +bool RTREE_QUAL::Load( RTFileStream& a_stream ) +{ + // Write some kind of header + int _dataFileId = ('R' << 0) | ('T' << 8) | ('R' << 16) | ('E' << 24); + int _dataSize = sizeof(DATATYPE); + int _dataNumDims = NUMDIMS; + int _dataElemSize = sizeof(ELEMTYPE); + int _dataElemRealSize = sizeof(ELEMTYPEREAL); + int _dataMaxNodes = TMAXNODES; + int _dataMinNodes = TMINNODES; + + int dataFileId = 0; + int dataSize = 0; + int dataNumDims = 0; + int dataElemSize = 0; + int dataElemRealSize = 0; + int dataMaxNodes = 0; + int dataMinNodes = 0; + + a_stream.Read( dataFileId ); + a_stream.Read( dataSize ); + a_stream.Read( dataNumDims ); + a_stream.Read( dataElemSize ); + a_stream.Read( dataElemRealSize ); + a_stream.Read( dataMaxNodes ); + a_stream.Read( dataMinNodes ); + + bool result = false; + + // Test if header was valid and compatible + if( (dataFileId == _dataFileId) + && (dataSize == _dataSize) + && (dataNumDims == _dataNumDims) + && (dataElemSize == _dataElemSize) + && (dataElemRealSize == _dataElemRealSize) + && (dataMaxNodes == _dataMaxNodes) + && (dataMinNodes == _dataMinNodes) + ) + { + // Recursively load tree + result = LoadRec( m_root, a_stream ); + } + + return result; +} + + +RTREE_TEMPLATE +bool RTREE_QUAL::LoadRec( Node* a_node, RTFileStream& a_stream ) +{ + a_stream.Read( a_node->m_level ); + a_stream.Read( a_node->m_count ); + + if( a_node->IsInternalNode() ) // not a leaf node + { + for( int index = 0; index < a_node->m_count; ++index ) + { + Branch* curBranch = &a_node->m_branch[index]; + + a_stream.ReadArray( curBranch->m_rect.m_min, NUMDIMS ); + a_stream.ReadArray( curBranch->m_rect.m_max, NUMDIMS ); + + curBranch->m_child = AllocNode(); + LoadRec( curBranch->m_child, a_stream ); + } + } + else // A leaf node + { + for( int index = 0; index < a_node->m_count; ++index ) + { + Branch* curBranch = &a_node->m_branch[index]; + + a_stream.ReadArray( curBranch->m_rect.m_min, NUMDIMS ); + a_stream.ReadArray( curBranch->m_rect.m_max, NUMDIMS ); + + a_stream.Read( curBranch->m_data ); + } + } + + return true; // Should do more error checking on I/O operations +} + + +RTREE_TEMPLATE +bool RTREE_QUAL::Save( const char* a_fileName ) +{ + RTFileStream stream; + + if( !stream.OpenWrite( a_fileName ) ) + { + return false; + } + + bool result = Save( stream ); + + stream.Close(); + + return result; +} + + +RTREE_TEMPLATE +bool RTREE_QUAL::Save( RTFileStream& a_stream ) +{ + // Write some kind of header + int dataFileId = ('R' << 0) | ('T' << 8) | ('R' << 16) | ('E' << 24); + int dataSize = sizeof(DATATYPE); + int dataNumDims = NUMDIMS; + int dataElemSize = sizeof(ELEMTYPE); + int dataElemRealSize = sizeof(ELEMTYPEREAL); + int dataMaxNodes = TMAXNODES; + int dataMinNodes = TMINNODES; + + a_stream.Write( dataFileId ); + a_stream.Write( dataSize ); + a_stream.Write( dataNumDims ); + a_stream.Write( dataElemSize ); + a_stream.Write( dataElemRealSize ); + a_stream.Write( dataMaxNodes ); + a_stream.Write( dataMinNodes ); + + // Recursively save tree + bool result = SaveRec( m_root, a_stream ); + + return result; +} + + +RTREE_TEMPLATE +bool RTREE_QUAL::SaveRec( Node* a_node, RTFileStream& a_stream ) +{ + a_stream.Write( a_node->m_level ); + a_stream.Write( a_node->m_count ); + + if( a_node->IsInternalNode() ) // not a leaf node + { + for( int index = 0; index < a_node->m_count; ++index ) + { + Branch* curBranch = &a_node->m_branch[index]; + + a_stream.WriteArray( curBranch->m_rect.m_min, NUMDIMS ); + a_stream.WriteArray( curBranch->m_rect.m_max, NUMDIMS ); + + SaveRec( curBranch->m_child, a_stream ); + } + } + else // A leaf node + { + for( int index = 0; index < a_node->m_count; ++index ) + { + Branch* curBranch = &a_node->m_branch[index]; + + a_stream.WriteArray( curBranch->m_rect.m_min, NUMDIMS ); + a_stream.WriteArray( curBranch->m_rect.m_max, NUMDIMS ); + + a_stream.Write( curBranch->m_data ); + } + } + + return true; // Should do more error checking on I/O operations +} + + +RTREE_TEMPLATE +void RTREE_QUAL::RemoveAll() +{ + // Delete all existing nodes + Reset(); + + m_root = AllocNode(); + m_root->m_level = 0; +} + + +RTREE_TEMPLATE +void RTREE_QUAL::Reset() +{ +#ifdef RTREE_DONT_USE_MEMPOOLS + // Delete all existing nodes + RemoveAllRec( m_root ); +#else // RTREE_DONT_USE_MEMPOOLS + // Just reset memory pools. We are not using complex types + // EXAMPLE +#endif // RTREE_DONT_USE_MEMPOOLS +} + + +RTREE_TEMPLATE +void RTREE_QUAL::RemoveAllRec( Node* a_node ) +{ + ASSERT( a_node ); + ASSERT( a_node->m_level >= 0 ); + + if( a_node->IsInternalNode() ) // This is an internal node in the tree + { + for( int index = 0; index < a_node->m_count; ++index ) + { + RemoveAllRec( a_node->m_branch[index].m_child ); + } + } + + FreeNode( a_node ); +} + + +RTREE_TEMPLATE +typename RTREE_QUAL::Node* RTREE_QUAL::AllocNode() +{ + Node* newNode; + +#ifdef RTREE_DONT_USE_MEMPOOLS + newNode = new Node; +#else // RTREE_DONT_USE_MEMPOOLS + // EXAMPLE +#endif // RTREE_DONT_USE_MEMPOOLS + InitNode( newNode ); + return newNode; +} + + +RTREE_TEMPLATE +void RTREE_QUAL::FreeNode( Node* a_node ) +{ + ASSERT( a_node ); + +#ifdef RTREE_DONT_USE_MEMPOOLS + delete a_node; +#else // RTREE_DONT_USE_MEMPOOLS + // EXAMPLE +#endif // RTREE_DONT_USE_MEMPOOLS +} + + +// Allocate space for a node in the list used in DeletRect to +// store Nodes that are too empty. +RTREE_TEMPLATE +typename RTREE_QUAL::ListNode* RTREE_QUAL::AllocListNode() +{ +#ifdef RTREE_DONT_USE_MEMPOOLS + return new ListNode; +#else // RTREE_DONT_USE_MEMPOOLS + // EXAMPLE +#endif // RTREE_DONT_USE_MEMPOOLS +} + + +RTREE_TEMPLATE +void RTREE_QUAL::FreeListNode( ListNode* a_listNode ) +{ +#ifdef RTREE_DONT_USE_MEMPOOLS + delete a_listNode; +#else // RTREE_DONT_USE_MEMPOOLS + // EXAMPLE +#endif // RTREE_DONT_USE_MEMPOOLS +} + + +RTREE_TEMPLATE +void RTREE_QUAL::InitNode( Node* a_node ) +{ + a_node->m_count = 0; + a_node->m_level = -1; +} + + +RTREE_TEMPLATE +void RTREE_QUAL::InitRect( Rect* a_rect ) +{ + for( int index = 0; index < NUMDIMS; ++index ) + { + a_rect->m_min[index] = (ELEMTYPE) 0; + a_rect->m_max[index] = (ELEMTYPE) 0; + } +} + + +// Inserts a new data rectangle into the index structure. +// Recursively descends tree, propagates splits back up. +// Returns 0 if node was not split. Old node updated. +// If node was split, returns 1 and sets the pointer pointed to by +// new_node to point to the new node. Old node updated to become one of two. +// The level argument specifies the number of steps up from the leaf +// level to insert; e.g. a data rectangle goes in at level = 0. +RTREE_TEMPLATE +bool RTREE_QUAL::InsertRectRec( Rect* a_rect, + const DATATYPE& a_id, + Node* a_node, + Node** a_newNode, + int a_level ) +{ + ASSERT( a_rect && a_node && a_newNode ); + ASSERT( a_level >= 0 && a_level <= a_node->m_level ); + + int index; + Branch branch; + Node* otherNode; + + // Still above level for insertion, go down tree recursively + if( a_node->m_level > a_level ) + { + index = PickBranch( a_rect, a_node ); + + if( !InsertRectRec( a_rect, a_id, a_node->m_branch[index].m_child, &otherNode, a_level ) ) + { + // Child was not split + a_node->m_branch[index].m_rect = + CombineRect( a_rect, &(a_node->m_branch[index].m_rect) ); + return false; + } + else // Child was split + { + a_node->m_branch[index].m_rect = NodeCover( a_node->m_branch[index].m_child ); + branch.m_child = otherNode; + branch.m_rect = NodeCover( otherNode ); + return AddBranch( &branch, a_node, a_newNode ); + } + } + else if( a_node->m_level == a_level ) // Have reached level for insertion. Add rect, split if necessary + { + branch.m_rect = *a_rect; + branch.m_child = (Node*) a_id; + // Child field of leaves contains id of data record + return AddBranch( &branch, a_node, a_newNode ); + } + else + { + // Should never occur + ASSERT( 0 ); + return false; + } +} + + +// Insert a data rectangle into an index structure. +// InsertRect provides for splitting the root; +// returns 1 if root was split, 0 if it was not. +// The level argument specifies the number of steps up from the leaf +// level to insert; e.g. a data rectangle goes in at level = 0. +// InsertRect2 does the recursion. +// +RTREE_TEMPLATE +bool RTREE_QUAL::InsertRect( Rect* a_rect, const DATATYPE& a_id, Node** a_root, int a_level ) +{ + ASSERT( a_rect && a_root ); + ASSERT( a_level >= 0 && a_level <= (*a_root)->m_level ); +#ifdef _DEBUG + + for( int index = 0; index < NUMDIMS; ++index ) + { + ASSERT( a_rect->m_min[index] <= a_rect->m_max[index] ); + } + +#endif // _DEBUG + + Node* newRoot; + Node* newNode; + Branch branch; + + if( InsertRectRec( a_rect, a_id, *a_root, &newNode, a_level ) ) // Root split + { + newRoot = AllocNode(); // Grow tree taller and new root + newRoot->m_level = (*a_root)->m_level + 1; + branch.m_rect = NodeCover( *a_root ); + branch.m_child = *a_root; + AddBranch( &branch, newRoot, NULL ); + branch.m_rect = NodeCover( newNode ); + branch.m_child = newNode; + AddBranch( &branch, newRoot, NULL ); + *a_root = newRoot; + return true; + } + + return false; +} + + +// Find the smallest rectangle that includes all rectangles in branches of a node. +RTREE_TEMPLATE +typename RTREE_QUAL::Rect RTREE_QUAL::NodeCover( Node* a_node ) +{ + ASSERT( a_node ); + + int firstTime = true; + Rect rect; + InitRect( &rect ); + + for( int index = 0; index < a_node->m_count; ++index ) + { + if( firstTime ) + { + rect = a_node->m_branch[index].m_rect; + firstTime = false; + } + else + { + rect = CombineRect( &rect, &(a_node->m_branch[index].m_rect) ); + } + } + + return rect; +} + + +// Add a branch to a node. Split the node if necessary. +// Returns 0 if node not split. Old node updated. +// Returns 1 if node split, sets *new_node to address of new node. +// Old node updated, becomes one of two. +RTREE_TEMPLATE +bool RTREE_QUAL::AddBranch( Branch* a_branch, Node* a_node, Node** a_newNode ) +{ + ASSERT( a_branch ); + ASSERT( a_node ); + + if( a_node->m_count < MAXNODES ) // Split won't be necessary + { + a_node->m_branch[a_node->m_count] = *a_branch; + ++a_node->m_count; + + return false; + } + else + { + ASSERT( a_newNode ); + + SplitNode( a_node, a_branch, a_newNode ); + return true; + } +} + + +// Disconnect a dependent node. +// Caller must return (or stop using iteration index) after this as count has changed +RTREE_TEMPLATE +void RTREE_QUAL::DisconnectBranch( Node* a_node, int a_index ) +{ + ASSERT( a_node && (a_index >= 0) && (a_index < MAXNODES) ); + ASSERT( a_node->m_count > 0 ); + + // Remove element by swapping with the last element to prevent gaps in array + a_node->m_branch[a_index] = a_node->m_branch[a_node->m_count - 1]; + + --a_node->m_count; +} + + +// Pick a branch. Pick the one that will need the smallest increase +// in area to accomodate the new rectangle. This will result in the +// least total area for the covering rectangles in the current node. +// In case of a tie, pick the one which was smaller before, to get +// the best resolution when searching. +RTREE_TEMPLATE +int RTREE_QUAL::PickBranch( Rect* a_rect, Node* a_node ) +{ + ASSERT( a_rect && a_node ); + + bool firstTime = true; + ELEMTYPEREAL increase; + ELEMTYPEREAL bestIncr = (ELEMTYPEREAL) -1; + ELEMTYPEREAL area; + ELEMTYPEREAL bestArea; + int best; + Rect tempRect; + + for( int index = 0; index < a_node->m_count; ++index ) + { + Rect* curRect = &a_node->m_branch[index].m_rect; + area = CalcRectVolume( curRect ); + tempRect = CombineRect( a_rect, curRect ); + increase = CalcRectVolume( &tempRect ) - area; + + if( (increase < bestIncr) || firstTime ) + { + best = index; + bestArea = area; + bestIncr = increase; + firstTime = false; + } + else if( (increase == bestIncr) && (area < bestArea) ) + { + best = index; + bestArea = area; + bestIncr = increase; + } + } + + return best; +} + + +// Combine two rectangles into larger one containing both +RTREE_TEMPLATE +typename RTREE_QUAL::Rect RTREE_QUAL::CombineRect( Rect* a_rectA, Rect* a_rectB ) +{ + ASSERT( a_rectA && a_rectB ); + + Rect newRect; + + for( int index = 0; index < NUMDIMS; ++index ) + { + newRect.m_min[index] = Min( a_rectA->m_min[index], a_rectB->m_min[index] ); + newRect.m_max[index] = Max( a_rectA->m_max[index], a_rectB->m_max[index] ); + } + + return newRect; +} + + +// Split a node. +// Divides the nodes branches and the extra one between two nodes. +// Old node is one of the new ones, and one really new one is created. +// Tries more than one method for choosing a partition, uses best result. +RTREE_TEMPLATE +void RTREE_QUAL::SplitNode( Node* a_node, Branch* a_branch, Node** a_newNode ) +{ + ASSERT( a_node ); + ASSERT( a_branch ); + + // Could just use local here, but member or external is faster since it is reused + PartitionVars localVars; + PartitionVars* parVars = &localVars; + int level; + + // Load all the branches into a buffer, initialize old node + level = a_node->m_level; + GetBranches( a_node, a_branch, parVars ); + + // Find partition + ChoosePartition( parVars, MINNODES ); + + // Put branches from buffer into 2 nodes according to chosen partition + *a_newNode = AllocNode(); + (*a_newNode)->m_level = a_node->m_level = level; + LoadNodes( a_node, *a_newNode, parVars ); + + ASSERT( (a_node->m_count + (*a_newNode)->m_count) == parVars->m_total ); +} + + +// Calculate the n-dimensional volume of a rectangle +RTREE_TEMPLATE +ELEMTYPEREAL RTREE_QUAL::RectVolume( Rect* a_rect ) +{ + ASSERT( a_rect ); + + ELEMTYPEREAL volume = (ELEMTYPEREAL) 1; + + for( int index = 0; indexm_max[index] - a_rect->m_min[index]; + } + + ASSERT( volume >= (ELEMTYPEREAL) 0 ); + + return volume; +} + + +// The exact volume of the bounding sphere for the given Rect +RTREE_TEMPLATE +ELEMTYPEREAL RTREE_QUAL::RectSphericalVolume( Rect* a_rect ) +{ + ASSERT( a_rect ); + + ELEMTYPEREAL sumOfSquares = (ELEMTYPEREAL) 0; + ELEMTYPEREAL radius; + + for( int index = 0; index < NUMDIMS; ++index ) + { + ELEMTYPEREAL halfExtent = + ( (ELEMTYPEREAL) a_rect->m_max[index] - (ELEMTYPEREAL) a_rect->m_min[index] ) * 0.5f; + sumOfSquares += halfExtent * halfExtent; + } + + radius = (ELEMTYPEREAL) sqrt( sumOfSquares ); + + // Pow maybe slow, so test for common dims like 2,3 and just use x*x, x*x*x. + if( NUMDIMS == 3 ) + { + return radius * radius * radius * m_unitSphereVolume; + } + else if( NUMDIMS == 2 ) + { + return radius * radius * m_unitSphereVolume; + } + else + { + return (ELEMTYPEREAL) (pow( radius, NUMDIMS ) * m_unitSphereVolume); + } +} + + +// Use one of the methods to calculate retangle volume +RTREE_TEMPLATE +ELEMTYPEREAL RTREE_QUAL::CalcRectVolume( Rect* a_rect ) +{ +#ifdef RTREE_USE_SPHERICAL_VOLUME + return RectSphericalVolume( a_rect ); // Slower but helps certain merge cases +#else // RTREE_USE_SPHERICAL_VOLUME + return RectVolume( a_rect ); // Faster but can cause poor merges +#endif // RTREE_USE_SPHERICAL_VOLUME +} + + +// Load branch buffer with branches from full node plus the extra branch. +RTREE_TEMPLATE +void RTREE_QUAL::GetBranches( Node* a_node, Branch* a_branch, PartitionVars* a_parVars ) +{ + ASSERT( a_node ); + ASSERT( a_branch ); + + ASSERT( a_node->m_count == MAXNODES ); + + // Load the branch buffer + for( int index = 0; index < MAXNODES; ++index ) + { + a_parVars->m_branchBuf[index] = a_node->m_branch[index]; + } + + a_parVars->m_branchBuf[MAXNODES] = *a_branch; + a_parVars->m_branchCount = MAXNODES + 1; + + // Calculate rect containing all in the set + a_parVars->m_coverSplit = a_parVars->m_branchBuf[0].m_rect; + + for( int index = 1; index < MAXNODES + 1; ++index ) + { + a_parVars->m_coverSplit = + CombineRect( &a_parVars->m_coverSplit, &a_parVars->m_branchBuf[index].m_rect ); + } + + a_parVars->m_coverSplitArea = CalcRectVolume( &a_parVars->m_coverSplit ); + + InitNode( a_node ); +} + + +// Method #0 for choosing a partition: +// As the seeds for the two groups, pick the two rects that would waste the +// most area if covered by a single rectangle, i.e. evidently the worst pair +// to have in the same group. +// Of the remaining, one at a time is chosen to be put in one of the two groups. +// The one chosen is the one with the greatest difference in area expansion +// depending on which group - the rect most strongly attracted to one group +// and repelled from the other. +// If one group gets too full (more would force other group to violate min +// fill requirement) then other group gets the rest. +// These last are the ones that can go in either group most easily. +RTREE_TEMPLATE +void RTREE_QUAL::ChoosePartition( PartitionVars* a_parVars, int a_minFill ) +{ + ASSERT( a_parVars ); + + ELEMTYPEREAL biggestDiff; + int group, chosen, betterGroup; + + InitParVars( a_parVars, a_parVars->m_branchCount, a_minFill ); + PickSeeds( a_parVars ); + + while( ( (a_parVars->m_count[0] + a_parVars->m_count[1]) < a_parVars->m_total ) + && ( a_parVars->m_count[0] < (a_parVars->m_total - a_parVars->m_minFill) ) + && ( a_parVars->m_count[1] < (a_parVars->m_total - a_parVars->m_minFill) ) ) + { + biggestDiff = (ELEMTYPEREAL) -1; + + for( int index = 0; indexm_total; ++index ) + { + if( !a_parVars->m_taken[index] ) + { + Rect* curRect = &a_parVars->m_branchBuf[index].m_rect; + Rect rect0 = CombineRect( curRect, &a_parVars->m_cover[0] ); + Rect rect1 = CombineRect( curRect, &a_parVars->m_cover[1] ); + ELEMTYPEREAL growth0 = CalcRectVolume( &rect0 ) - a_parVars->m_area[0]; + ELEMTYPEREAL growth1 = CalcRectVolume( &rect1 ) - a_parVars->m_area[1]; + ELEMTYPEREAL diff = growth1 - growth0; + + if( diff >= 0 ) + { + group = 0; + } + else + { + group = 1; + diff = -diff; + } + + if( diff > biggestDiff ) + { + biggestDiff = diff; + chosen = index; + betterGroup = group; + } + else if( (diff == biggestDiff) + && (a_parVars->m_count[group] < a_parVars->m_count[betterGroup]) ) + { + chosen = index; + betterGroup = group; + } + } + } + + Classify( chosen, betterGroup, a_parVars ); + } + + // If one group too full, put remaining rects in the other + if( (a_parVars->m_count[0] + a_parVars->m_count[1]) < a_parVars->m_total ) + { + if( a_parVars->m_count[0] >= a_parVars->m_total - a_parVars->m_minFill ) + { + group = 1; + } + else + { + group = 0; + } + + for( int index = 0; indexm_total; ++index ) + { + if( !a_parVars->m_taken[index] ) + { + Classify( index, group, a_parVars ); + } + } + } + + ASSERT( (a_parVars->m_count[0] + a_parVars->m_count[1]) == a_parVars->m_total ); + ASSERT( (a_parVars->m_count[0] >= a_parVars->m_minFill) + && (a_parVars->m_count[1] >= a_parVars->m_minFill) ); +} + + +// Copy branches from the buffer into two nodes according to the partition. +RTREE_TEMPLATE +void RTREE_QUAL::LoadNodes( Node* a_nodeA, Node* a_nodeB, PartitionVars* a_parVars ) +{ + ASSERT( a_nodeA ); + ASSERT( a_nodeB ); + ASSERT( a_parVars ); + + for( int index = 0; index < a_parVars->m_total; ++index ) + { + ASSERT( a_parVars->m_partition[index] == 0 || a_parVars->m_partition[index] == 1 ); + + if( a_parVars->m_partition[index] == 0 ) + { + AddBranch( &a_parVars->m_branchBuf[index], a_nodeA, NULL ); + } + else if( a_parVars->m_partition[index] == 1 ) + { + AddBranch( &a_parVars->m_branchBuf[index], a_nodeB, NULL ); + } + } +} + + +// Initialize a PartitionVars structure. +RTREE_TEMPLATE +void RTREE_QUAL::InitParVars( PartitionVars* a_parVars, int a_maxRects, int a_minFill ) +{ + ASSERT( a_parVars ); + + a_parVars->m_count[0] = a_parVars->m_count[1] = 0; + a_parVars->m_area[0] = a_parVars->m_area[1] = (ELEMTYPEREAL) 0; + a_parVars->m_total = a_maxRects; + a_parVars->m_minFill = a_minFill; + + for( int index = 0; index < a_maxRects; ++index ) + { + a_parVars->m_taken[index] = false; + a_parVars->m_partition[index] = -1; + } +} + + +RTREE_TEMPLATE +void RTREE_QUAL::PickSeeds( PartitionVars* a_parVars ) +{ + int seed0, seed1; + ELEMTYPEREAL worst, waste; + ELEMTYPEREAL area[MAXNODES + 1]; + + for( int index = 0; indexm_total; ++index ) + { + area[index] = CalcRectVolume( &a_parVars->m_branchBuf[index].m_rect ); + } + + worst = -a_parVars->m_coverSplitArea - 1; + + for( int indexA = 0; indexA < a_parVars->m_total - 1; ++indexA ) + { + for( int indexB = indexA + 1; indexB < a_parVars->m_total; ++indexB ) + { + Rect oneRect = CombineRect( &a_parVars->m_branchBuf[indexA].m_rect, + &a_parVars->m_branchBuf[indexB].m_rect ); + waste = CalcRectVolume( &oneRect ) - area[indexA] - area[indexB]; + + if( waste > worst ) + { + worst = waste; + seed0 = indexA; + seed1 = indexB; + } + } + } + + Classify( seed0, 0, a_parVars ); + Classify( seed1, 1, a_parVars ); +} + + +// Put a branch in one of the groups. +RTREE_TEMPLATE +void RTREE_QUAL::Classify( int a_index, int a_group, PartitionVars* a_parVars ) +{ + ASSERT( a_parVars ); + ASSERT( !a_parVars->m_taken[a_index] ); + + a_parVars->m_partition[a_index] = a_group; + a_parVars->m_taken[a_index] = true; + + if( a_parVars->m_count[a_group] == 0 ) + { + a_parVars->m_cover[a_group] = a_parVars->m_branchBuf[a_index].m_rect; + } + else + { + a_parVars->m_cover[a_group] = CombineRect( &a_parVars->m_branchBuf[a_index].m_rect, + &a_parVars->m_cover[a_group] ); + } + + a_parVars->m_area[a_group] = CalcRectVolume( &a_parVars->m_cover[a_group] ); + ++a_parVars->m_count[a_group]; +} + + +// Delete a data rectangle from an index structure. +// Pass in a pointer to a Rect, the tid of the record, ptr to ptr to root node. +// Returns 1 if record not found, 0 if success. +// RemoveRect provides for eliminating the root. +RTREE_TEMPLATE +bool RTREE_QUAL::RemoveRect( Rect* a_rect, const DATATYPE& a_id, Node** a_root ) +{ + ASSERT( a_rect && a_root ); + ASSERT( *a_root ); + + Node* tempNode; + ListNode* reInsertList = NULL; + + if( !RemoveRectRec( a_rect, a_id, *a_root, &reInsertList ) ) + { + // Found and deleted a data item + // Reinsert any branches from eliminated nodes + while( reInsertList ) + { + tempNode = reInsertList->m_node; + + for( int index = 0; index < tempNode->m_count; ++index ) + { + InsertRect( &(tempNode->m_branch[index].m_rect), + tempNode->m_branch[index].m_data, + a_root, + tempNode->m_level ); + } + + ListNode* remLNode = reInsertList; + reInsertList = reInsertList->m_next; + + FreeNode( remLNode->m_node ); + FreeListNode( remLNode ); + } + + // Check for redundant root (not leaf, 1 child) and eliminate + if( (*a_root)->m_count == 1 && (*a_root)->IsInternalNode() ) + { + tempNode = (*a_root)->m_branch[0].m_child; + + ASSERT( tempNode ); + FreeNode( *a_root ); + *a_root = tempNode; + } + + return false; + } + else + { + return true; + } +} + + +// Delete a rectangle from non-root part of an index structure. +// Called by RemoveRect. Descends tree recursively, +// merges branches on the way back up. +// Returns 1 if record not found, 0 if success. +RTREE_TEMPLATE +bool RTREE_QUAL::RemoveRectRec( Rect* a_rect, + const DATATYPE& a_id, + Node* a_node, + ListNode** a_listNode ) +{ + ASSERT( a_rect && a_node && a_listNode ); + ASSERT( a_node->m_level >= 0 ); + + if( a_node->IsInternalNode() ) // not a leaf node + { + for( int index = 0; index < a_node->m_count; ++index ) + { + if( Overlap( a_rect, &(a_node->m_branch[index].m_rect) ) ) + { + if( !RemoveRectRec( a_rect, a_id, a_node->m_branch[index].m_child, a_listNode ) ) + { + if( a_node->m_branch[index].m_child->m_count >= MINNODES ) + { + // child removed, just resize parent rect + a_node->m_branch[index].m_rect = + NodeCover( a_node->m_branch[index].m_child ); + } + else + { + // child removed, not enough entries in node, eliminate node + ReInsert( a_node->m_branch[index].m_child, a_listNode ); + DisconnectBranch( a_node, index ); // Must return after this call as count has changed + } + + return false; + } + } + } + + return true; + } + else // A leaf node + { + for( int index = 0; index < a_node->m_count; ++index ) + { + if( a_node->m_branch[index].m_child == (Node*) a_id ) + { + DisconnectBranch( a_node, index ); // Must return after this call as count has changed + return false; + } + } + + return true; + } +} + + +// Decide whether two rectangles overlap. +RTREE_TEMPLATE +bool RTREE_QUAL::Overlap( Rect* a_rectA, Rect* a_rectB ) +{ + ASSERT( a_rectA && a_rectB ); + + for( int index = 0; index < NUMDIMS; ++index ) + { + if( a_rectA->m_min[index] > a_rectB->m_max[index] + || a_rectB->m_min[index] > a_rectA->m_max[index] ) + { + return false; + } + } + + return true; +} + + +// Add a node to the reinsertion list. All its branches will later +// be reinserted into the index structure. +RTREE_TEMPLATE +void RTREE_QUAL::ReInsert( Node* a_node, ListNode** a_listNode ) +{ + ListNode* newListNode; + + newListNode = AllocListNode(); + newListNode->m_node = a_node; + newListNode->m_next = *a_listNode; + *a_listNode = newListNode; +} + + +// Search in an index tree or subtree for all data retangles that overlap the argument rectangle. +RTREE_TEMPLATE +bool RTREE_QUAL::Search( Node* a_node, Rect* a_rect, int& a_foundCount, bool a_resultCallback( + DATATYPE a_data, + void* a_context ), void* a_context ) +{ + ASSERT( a_node ); + ASSERT( a_node->m_level >= 0 ); + ASSERT( a_rect ); + + if( a_node->IsInternalNode() ) // This is an internal node in the tree + { + for( int index = 0; index < a_node->m_count; ++index ) + { + if( Overlap( a_rect, &a_node->m_branch[index].m_rect ) ) + { + if( !Search( a_node->m_branch[index].m_child, a_rect, a_foundCount, + a_resultCallback, a_context ) ) + { + return false; // Don't continue searching + } + } + } + } + else // This is a leaf node + { + for( int index = 0; index < a_node->m_count; ++index ) + { + if( Overlap( a_rect, &a_node->m_branch[index].m_rect ) ) + { + DATATYPE& id = a_node->m_branch[index].m_data; + + // NOTE: There are different ways to return results. Here's where to modify + if( &a_resultCallback ) + { + ++a_foundCount; + + if( !a_resultCallback( id, a_context ) ) + { + return false; // Don't continue searching + } + } + } + } + } + + return true; // Continue searching +} + + +#undef RTREE_TEMPLATE +#undef RTREE_QUAL +#undef RTREE_SEARCH_TEMPLATE +#undef RTREE_SEARCH_QUAL + +#endif // RTREE_H diff --git a/include/view/view_rtree.h b/include/view/view_rtree.h new file mode 100644 index 0000000000..4762dfc691 --- /dev/null +++ b/include/view/view_rtree.h @@ -0,0 +1,92 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2013 CERN + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __VIEW_RTREE_H +#define __VIEW_RTREE_H + +#include + +#include + +namespace KiGfx +{ +typedef RTree VIEW_RTREE_BASE; + +/** + * Class VIEW_RTREE - + * Implements an R-tree for fast spatial indexing of VIEW items. + * Non-owning. + */ +class VIEW_RTREE : public VIEW_RTREE_BASE +{ +public: + + /** + * Function Insert() + * Inserts an item into the tree. Item's bounding box is taken via its ViewBBox() method. + */ + void Insert( VIEW_ITEM* aItem ) + { + const BOX2I& bbox = aItem->ViewBBox(); + const int mmin[2] = { bbox.GetX(), bbox.GetY() }; + const int mmax[2] = { bbox.GetRight(), bbox.GetBottom() }; + + VIEW_RTREE_BASE::Insert( mmin, mmax, aItem ); + } + + /** + * Function Remove() + * Removes an item from the tree. Removal is done by comparing pointers, attepmting to remove a copy + * of the item will fail. + */ + void Remove( VIEW_ITEM* aItem ) + { + // const BOX2I& bbox = aItem->ViewBBox(); + + // FIXME: use cached bbox or ptr_map to speed up pointer <-> node lookups. + const int mmin[2] = { INT_MIN, INT_MIN }; + const int mmax[2] = { INT_MAX, INT_MAX }; + + VIEW_RTREE_BASE::Remove( mmin, mmax, aItem ); + } + + /** + * Function Query() + * Executes a function object aVisitor for each item whose bounding box intersects + * with aBounds. + */ + + template + void Query( const BOX2I& aBounds, Visitor& aVisitor ) // const + { + const int mmin[2] = { aBounds.GetX(), aBounds.GetY() }; + const int mmax[2] = { aBounds.GetRight(), aBounds.GetBottom() }; + + VIEW_RTREE_BASE::Search( mmin, mmax, aVisitor ); + } + +private: +}; +} // namespace KiGfx + +#endif