diff --git a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/WritePoleFigure.cpp b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/WritePoleFigure.cpp index ecc913bcdb..72e67555e2 100644 --- a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/WritePoleFigure.cpp +++ b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/Filters/Algorithms/WritePoleFigure.cpp @@ -5,7 +5,6 @@ #include "OrientationAnalysis/utilities/IntersectionUtilities.hpp" #include "OrientationAnalysis/utilities/LatoBold.hpp" #include "OrientationAnalysis/utilities/LatoRegular.hpp" -#include "OrientationAnalysis/utilities/RTree.hpp" #include "OrientationAnalysis/utilities/TiffWriter.hpp" #include "OrientationAnalysis/utilities/delaunator.h" @@ -18,6 +17,7 @@ #include "simplnx/Utilities/DataArrayUtilities.hpp" #include "simplnx/Utilities/ParallelTaskAlgorithm.hpp" #include "simplnx/Utilities/Parsing/DREAM3D/Dream3dIO.hpp" +#include "simplnx/Utilities/RTree.hpp" #include "simplnx/Utilities/StringUtilities.hpp" #include "EbsdLib/Core/EbsdLibConstants.h" diff --git a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/utilities/RTree.hpp b/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/utilities/RTree.hpp deleted file mode 100644 index 2f6b953b68..0000000000 --- a/src/Plugins/OrientationAnalysis/src/OrientationAnalysis/utilities/RTree.hpp +++ /dev/null @@ -1,1652 +0,0 @@ -#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 - -#include -#include -#include - -#define ASSERT assert // RTree uses ASSERT( condition ) -#ifndef RTREE_MIN -#define RTREE_MIN std::min -#endif // RTREE_MIN -#ifndef RTREE_MAX -#define RTREE_MAX std::max -#endif // RTREE_MAX - -// -// RTree.h -// - -#define RTREE_TEMPLATE template -#define RTREE_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 -{ - static_assert(std::numeric_limits::is_iec559, "'ELEMTYPEREAL' accepts floating-point types only"); - -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, ///< RTREE_MAX elements in node - MINNODES = TMINNODES, ///< RTREE_MIN elements in node - }; - -public: - RTree(); - RTree(const RTree& other); - virtual ~RTree(); - - /// Insert entry - /// \param a_min RTREE_MIN of bounding rect - /// \param a_max RTREE_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 RTREE_MIN of bounding rect - /// \param a_max RTREE_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 RTREE_MIN of search bounding rect - /// \param a_max RTREE_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], std::function 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(); - - /// 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); - - /// Iterator is not remove safe. - class Iterator - { - private: - enum - { - MAX_STACK = 32 - }; // RTREE_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() - { - return (m_tos > 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]; ///< RTREE_MIN dimensions of bounding box - ELEMTYPE m_max[NUMDIMS]; ///< RTREE_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 - }; - - /// 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); - bool Search(Node* a_node, Rect* a_rect, int& a_foundCount, std::function callback) const; - 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); - void CopyRec(Node* current, Node* other); - - Node* m_root; ///< Root of tree - ELEMTYPEREAL m_unitSphereVolume; ///< Unit sphere constant for required number of dimensions - -public: - // return all the AABBs that form the RTree - std::vector ListTree() const; -}; - -// 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 Open(const char* a_fileName, const char* mode) - { -#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__) - return fopen_s(&m_file, a_fileName, mode) == 0; -#else - m_file = fopen(a_fileName, mode); - return m_file != nullptr; -#endif - } - - bool OpenRead(const char* a_fileName) - { - return this->Open(a_fileName, "rb"); - } - - bool OpenWrite(const char* a_fileName) - { - return this->Open(a_fileName, "wb"); - } - - 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); - - // 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(const RTree& other) -: RTree() -{ - CopyRec(m_root, other.m_root); -} - -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; index < NUMDIMS; ++index) - { - ASSERT(a_min[index] <= a_max[index]); - } -#endif //_DEBUG - - Branch branch; - branch.m_data = a_dataId; - branch.m_child = NULL; - - for(int axis = 0; axis < NUMDIMS; ++axis) - { - branch.m_rect.m_min[axis] = a_min[axis]; - branch.m_rect.m_max[axis] = a_max[axis]; - } - - InsertRect(branch, &m_root, 0); -} - -RTREE_TEMPLATE -void RTREE_QUAL::Remove(const ELEMTYPE a_min[NUMDIMS], const ELEMTYPE a_max[NUMDIMS], const DATATYPE& a_dataId) -{ -#ifdef _DEBUG - for(int index = 0; index < NUMDIMS; ++index) - { - ASSERT(a_min[index] <= a_max[index]); - } -#endif //_DEBUG - - Rect rect; - - for(int axis = 0; axis < NUMDIMS; ++axis) - { - rect.m_min[axis] = a_min[axis]; - rect.m_max[axis] = a_max[axis]; - } - - RemoveRect(&rect, a_dataId, &m_root); -} - -RTREE_TEMPLATE -int RTREE_QUAL::Search(const ELEMTYPE a_min[NUMDIMS], const ELEMTYPE a_max[NUMDIMS], std::function callback) const -{ -#ifdef _DEBUG - for(int index = 0; index < NUMDIMS; ++index) - { - ASSERT(a_min[index] <= a_max[index]); - } -#endif //_DEBUG - - Rect rect; - - for(int axis = 0; axis < NUMDIMS; ++axis) - { - rect.m_min[axis] = a_min[axis]; - rect.m_max[axis] = a_max[axis]; - } - - // NOTE: May want to return search result another way, perhaps returning the number of found elements here. - - int foundCount = 0; - Search(m_root, &rect, foundCount, callback); - - return foundCount; -} - -RTREE_TEMPLATE -int RTREE_QUAL::Count() -{ - int count = 0; - CountRec(m_root, count); - - return count; -} - -RTREE_TEMPLATE -void RTREE_QUAL::CountRec(Node* a_node, int& a_count) -{ - if(a_node->IsInternalNode()) // 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 -void RTREE_QUAL::CopyRec(Node* current, Node* other) -{ - current->m_level = other->m_level; - current->m_count = other->m_count; - - if(current->IsInternalNode()) // not a leaf node - { - for(int index = 0; index < current->m_count; ++index) - { - Branch* currentBranch = ¤t->m_branch[index]; - Branch* otherBranch = &other->m_branch[index]; - - std::copy(otherBranch->m_rect.m_min, otherBranch->m_rect.m_min + NUMDIMS, currentBranch->m_rect.m_min); - - std::copy(otherBranch->m_rect.m_max, otherBranch->m_rect.m_max + NUMDIMS, currentBranch->m_rect.m_max); - - currentBranch->m_child = AllocNode(); - CopyRec(currentBranch->m_child, otherBranch->m_child); - } - } - else // A leaf node - { - for(int index = 0; index < current->m_count; ++index) - { - Branch* currentBranch = ¤t->m_branch[index]; - Branch* otherBranch = &other->m_branch[index]; - - std::copy(otherBranch->m_rect.m_min, otherBranch->m_rect.m_min + NUMDIMS, currentBranch->m_rect.m_min); - - std::copy(otherBranch->m_rect.m_max, otherBranch->m_rect.m_max + NUMDIMS, currentBranch->m_rect.m_max); - - currentBranch->m_data = otherBranch->m_data; - } - } -} - -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 simplnx 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(const Branch& a_branch, Node* a_node, Node** a_newNode, int a_level) -{ - ASSERT(a_node && a_newNode); - ASSERT(a_level >= 0 && a_level <= a_node->m_level); - - // recurse until we reach the correct level for the new record. data records - // will always be called with a_level == 0 (leaf) - if(a_node->m_level > a_level) - { - // Still above level for insertion, go down tree recursively - Node* otherNode; - - // find the optimal branch for this record - int index = PickBranch(&a_branch.m_rect, a_node); - - // recursively insert this record into the picked branch - bool childWasSplit = InsertRectRec(a_branch, a_node->m_branch[index].m_child, &otherNode, a_level); - - if(!childWasSplit) - { - // Child was not split. Merge the bounding box of the new record with the - // existing bounding box - a_node->m_branch[index].m_rect = CombineRect(&a_branch.m_rect, &(a_node->m_branch[index].m_rect)); - return false; - } - else - { - // Child was split. The old branches are now re-partitioned to two nodes - // so we have to re-calculate the bounding boxes of each node - a_node->m_branch[index].m_rect = NodeCover(a_node->m_branch[index].m_child); - Branch branch; - branch.m_child = otherNode; - branch.m_rect = NodeCover(otherNode); - - // The old node is already a child of a_node. Now add the newly-created - // node to a_node as well. a_node might be split because of that. - return AddBranch(&branch, a_node, a_newNode); - } - } - else if(a_node->m_level == a_level) - { - // We have reached level for insertion. Add rect, split if necessary - return AddBranch(&a_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(const Branch& a_branch, Node** a_root, int a_level) -{ - ASSERT(a_root); - ASSERT(a_level >= 0 && a_level <= (*a_root)->m_level); -#ifdef _DEBUG - for(int index = 0; index < NUMDIMS; ++index) - { - ASSERT(a_branch.m_rect.m_min[index] <= a_branch.m_rect.m_max[index]); - } -#endif //_DEBUG - - Node* newNode; - - if(InsertRectRec(a_branch, *a_root, &newNode, a_level)) // Root split - { - // Grow tree taller and new root - Node* newRoot = AllocNode(); - newRoot->m_level = (*a_root)->m_level + 1; - - Branch branch; - - // add old root node as a child of the new root - branch.m_rect = NodeCover(*a_root); - branch.m_child = *a_root; - AddBranch(&branch, newRoot, NULL); - - // add the split node as a child of the new root - branch.m_rect = NodeCover(newNode); - branch.m_child = newNode; - AddBranch(&branch, newRoot, NULL); - - // set the new root as the root node - *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); - - Rect rect = a_node->m_branch[0].m_rect; - for(int index = 1; index < a_node->m_count; ++index) - { - 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(const 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(const 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 = 0; - 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(const Rect* a_rectA, const Rect* a_rectB) -{ - ASSERT(a_rectA && a_rectB); - - Rect newRect; - - for(int index = 0; index < NUMDIMS; ++index) - { - newRect.m_min[index] = RTREE_MIN(a_rectA->m_min[index], a_rectB->m_min[index]); - newRect.m_max[index] = RTREE_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, const 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; - - // Load all the branches into a buffer, initialize old node - GetBranches(a_node, a_branch, parVars); - - // Find partition - ChoosePartition(parVars, MINNODES); - - // Create a new node to hold (about) half of the branches - *a_newNode = AllocNode(); - (*a_newNode)->m_level = a_node->m_level; - - // Put branches from buffer into 2 nodes according to the chosen partition - a_node->m_count = 0; - 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; index < NUMDIMS; ++index) - { - volume *= a_rect->m_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]) * (ELEMTYPEREAL)0.5; - 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, const 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); -} - -// 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 = 0, betterGroup = 0; - - 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; index < a_parVars->m_total; ++index) - { - if(PartitionVars::NOT_TAKEN == a_parVars->m_partition[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; index < a_parVars->m_total; ++index) - { - if(PartitionVars::NOT_TAKEN == a_parVars->m_partition[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); - - int targetNodeIndex = a_parVars->m_partition[index]; - Node* targetNodes[] = {a_nodeA, a_nodeB}; - - // It is assured that AddBranch here will not cause a node split. - bool nodeWasSplit = AddBranch(&a_parVars->m_branchBuf[index], targetNodes[targetNodeIndex], NULL); - ASSERT(!nodeWasSplit); - } -} - -// 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_partition[index] = PartitionVars::NOT_TAKEN; - } -} - -RTREE_TEMPLATE -void RTREE_QUAL::PickSeeds(PartitionVars* a_parVars) -{ - int seed0 = 0, seed1 = 0; - ELEMTYPEREAL worst, waste; - ELEMTYPEREAL area[MAXNODES + 1]; - - for(int index = 0; index < a_parVars->m_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(PartitionVars::NOT_TAKEN == a_parVars->m_partition[a_index]); - - a_parVars->m_partition[a_index] = a_group; - - // Calculate combined rect - 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]); - } - - // Calculate volume of combined rect - 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); - - 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) - { - Node* tempNode = reInsertList->m_node; - - for(int index = 0; index < tempNode->m_count; ++index) - { - // TODO go over this code. should I use (tempNode->m_level - 1)? - InsertRect(tempNode->m_branch[index], 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 TODO replace - // if with while? In case there is a whole branch of redundant roots... - if((*a_root)->m_count == 1 && (*a_root)->IsInternalNode()) - { - Node* 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_data == 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) const -{ - 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, std::function callback) const -{ - 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, callback)) - { - // The callback indicated to stop searching - return false; - } - } - } - } - 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_foundCount; - - if(callback && !callback(id)) - { - return false; // Don't continue searching - } - } - } - } - - return true; // Continue searching -} - -RTREE_TEMPLATE -std::vector RTREE_QUAL::ListTree() const -{ - ASSERT(m_root); - ASSERT(m_root->m_level >= 0); - - std::vector treeList; - - std::vector toVisit; - toVisit.push_back(m_root); - - while(!toVisit.empty()) - { - Node* a_node = toVisit.back(); - toVisit.pop_back(); - if(a_node->IsInternalNode()) - { - // This is an internal node in the tree - for(int index = 0; index < a_node->m_count; ++index) - { - treeList.push_back(a_node->m_branch[index].m_rect); - toVisit.push_back(a_node->m_branch[index].m_child); - } - } - else - { - // This is a leaf node - for(int index = 0; index < a_node->m_count; ++index) - { - treeList.push_back(a_node->m_branch[index].m_rect); - } - } - } - - return treeList; -} - -#undef RTREE_TEMPLATE -#undef RTREE_QUAL - -#endif // RTREE_H diff --git a/src/simplnx/Utilities/RTree.hpp b/src/simplnx/Utilities/RTree.hpp index 85d9e93678..1678b9cc4f 100644 --- a/src/simplnx/Utilities/RTree.hpp +++ b/src/simplnx/Utilities/RTree.hpp @@ -1,6 +1,11 @@ #ifndef RTREE_H #define RTREE_H +/* **************************************************************************** + * This file comes from the https://github.com/nushoin/RTree repository. + * The file is under the MIT license as indicated within that repository + **************************************************************************** */ + // 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