#pragma once #include "RTree-fwd.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 //#define RTREE_STDIO #ifdef RTREE_STDIO #include #endif // // RTree.h // //#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 #ifdef RTREE_STDIO // Fwd decl class RTFileStream; // File I/O helper class, look below for implementation and notes. #endif #define RTREE_TEMPLATE template #define RTREE_QUAL RTree namespace floormat::detail { template struct rtree_pool final { rtree_pool() noexcept; rtree_pool(const rtree_pool&) = delete; rtree_pool& operator=(const rtree_pool&) = delete; ~rtree_pool(); T* construct(); void free(T* pool); union node_u { union { T data; }; node_u* next; }; union node_p { T* ptr; node_u* data_ptr; }; private: node_u* free_list = nullptr; }; } // namespace floormat::detail /// \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 final { public: struct Node; // Fwd decl. Used by other internal structs and iterator struct ListNode; // 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 }; RTree(); RTree(const RTree& other); ~RTree() noexcept; RTree& operator=(const 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 template int Search(const ELEMTYPE a_min[NUMDIMS], const ELEMTYPE a_max[NUMDIMS], F&& callback) const; /// Remove all entries from tree void RemoveAll(); /// Count the data elements in this container. This is slow as no internal counter is maintained. int Count() const; #ifdef RTREE_STDIO /// Load tree contents from file bool Load(const char* a_fileName); /// Load tree contents from stream bool Load(RTFileStream& a_stream); /// Save tree contents to file bool Save(const char* a_fileName); /// Save tree contents to stream bool Save(RTFileStream& a_stream); #endif /// Iterator is not remove safe. class Iterator { private: enum { MAX_STACK = 32 }; // Max stack size. Allows almost n^32 where n is number of branches in node struct StackElement { Node* m_node; int m_branchIndex; }; public: Iterator() { Init(); } ~Iterator() { } /// Is iterator invalid bool IsNull() { return (m_tos <= 0); } /// Is iterator pointing to valid data bool IsNotNull() const { return (m_tos > 0); } /// Access the current data element. Caller must be sure iterator is not NULL first. DATATYPE& operator*(); /// Access the current data element. Caller must be sure iterator is not NULL first. const DATATYPE& operator*() const; /// 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]); private: /// Reset iterator void Init() { m_tos = 0; } /// Find the next data element in the tree (For internal use only) bool FindNextData(); /// Push node and branch onto iteration stack (For internal use only) void Push(Node* a_node, int a_branchIndex); /// Pop element off iteration stack (For internal use only) StackElement& Pop(); 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); /// 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; } /// 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 Node* m_child; ///< Child node DATATYPE m_data; ///< Data Id }; /// 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 }; static floormat::detail::rtree_pool node_pool; static floormat::detail::rtree_pool list_node_pool; protected: /// Variables for finding a split partition struct PartitionVars { enum { NOT_TAKEN = -1 }; // indicates that position int m_partition[MAXNODES+1]; int m_total; int m_minFill; 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(const Branch& a_branch, Node* a_node, Node** a_newNode, int a_level); bool InsertRect(const Branch& a_branch, Node** a_root, int a_level); Rect NodeCover(Node* a_node); bool AddBranch(const Branch* a_branch, Node* a_node, Node** a_newNode); void DisconnectBranch(Node* a_node, int a_index); int PickBranch(const Rect* a_rect, Node* a_node); Rect CombineRect(const Rect* a_rectA, const Rect* a_rectB); void SplitNode(Node* a_node, const 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, const 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) const; void ReInsert(Node* a_node, ListNode** a_listNode); template bool Search(Node* a_node, Rect* a_rect, int& a_foundCount, F&& callback) const; void RemoveAllRec(Node* a_node); void Reset(); void CountRec(Node* a_node, int& a_count) const; #ifdef RTREE_STDIO bool SaveRec(Node* a_node, RTFileStream& a_stream); bool LoadRec(Node* a_node, RTFileStream& a_stream); #endif void CopyRec(Node* current, Node* other); Node* m_root; ///< Root of tree ELEMTYPEREAL m_unitSphereVolume; ///< Unit sphere constant for required number of dimensions template using Array = ::Corrade::Containers::Array; public: // return all the AABBs that form the RTree void ListTree(Array& vec, Array& temp) const; }; #ifndef RTREE_NO_EXTERN_TEMPLATE extern template struct floormat::detail::rtree_pool::Node>; extern template struct floormat::detail::rtree_pool::ListNode>; extern template class RTree; #endif //#undef RTREE_TEMPLATE //#undef RTREE_QUAL