mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-27 00:33:48 -05:00
1333 lines
48 KiB
C++
1333 lines
48 KiB
C++
//
|
|
// RTree.h
|
|
//
|
|
#ifndef RTREE_H
|
|
#define RTREE_H
|
|
|
|
#include <cassert>
|
|
#include <algorithm>
|
|
#include <functional>
|
|
#include <type_traits>
|
|
#include <cmath>
|
|
|
|
#define RTreeAssert assert // RTree uses RTreeAssert( condition )
|
|
|
|
|
|
#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
|
|
|
|
|
|
// 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 {
|
|
public:
|
|
RTFileStream() = default;
|
|
|
|
~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 = nullptr;
|
|
}
|
|
}
|
|
|
|
template< typename TYPE >
|
|
size_t Write(const TYPE& a_value) {
|
|
RTreeAssert(m_file);
|
|
return fwrite((void*)&a_value, sizeof(a_value), 1, m_file);
|
|
}
|
|
|
|
template< typename TYPE >
|
|
size_t WriteArray(const TYPE* a_array, int a_count) {
|
|
RTreeAssert(m_file);
|
|
return fwrite((void*)a_array, sizeof(TYPE) * a_count, 1, m_file);
|
|
}
|
|
|
|
template< typename TYPE >
|
|
size_t Read(TYPE& a_value) {
|
|
RTreeAssert(m_file);
|
|
return fread((void*)&a_value, sizeof(a_value), 1, m_file);
|
|
}
|
|
|
|
template< typename TYPE >
|
|
size_t ReadArray(TYPE* a_array, int a_count) {
|
|
RTreeAssert(m_file);
|
|
return fread((void*)a_array, sizeof(TYPE) * a_count, 1, m_file);
|
|
}
|
|
|
|
private:
|
|
FILE* m_file = nullptr;
|
|
};
|
|
|
|
|
|
/// \class RTree
|
|
/// Implementation of RTree, a multidimensional bounding rectangle tree.
|
|
/// Example usage: For a 3-dimensional tree use RTree<Object*, float, 3> 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<void*> and simple type
|
|
/// _ElementType Type of element such as int or float
|
|
/// _NumDimensions Number of dimensions such as 2 or 3
|
|
/// _ElementTypeReal 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 _DataType, class _ElementType, int _NumDimensions,
|
|
class _ElementTypeReal = _ElementType, int _MaxNodeCount = 8, int _MinNodeCount = _MaxNodeCount / 2>
|
|
class RTree
|
|
{
|
|
static_assert(1 < _NumDimensions, "_NumDimensions must be larger than 1");
|
|
static_assert(0 < _MinNodeCount, "_MinNodeCount must be larger than 0");
|
|
static_assert(_MinNodeCount < _MaxNodeCount, "_MaxNodeCount must be larger than _MinNodeCount");
|
|
static_assert(std::is_floating_point<_ElementTypeReal>::value, "_ElementTypeReal must be floating point type");
|
|
protected:
|
|
|
|
struct Node; // Fwd decl. Used by other internal structs and iterator
|
|
|
|
public:
|
|
using DataType = _DataType;
|
|
using ElementType = _ElementType;
|
|
using ElementTypeReal = _ElementTypeReal;
|
|
using Element = ElementType[_NumDimensions];
|
|
|
|
constexpr static const int kNumDimensions = _NumDimensions;
|
|
constexpr static const int kMaxNodeCount = _MaxNodeCount;
|
|
constexpr static const int kMinNodeCount = _MinNodeCount;
|
|
|
|
template<typename ValueType>
|
|
constexpr static ElementType CastElementType(const ValueType val) noexcept {
|
|
return static_cast<ElementType>(val);
|
|
}
|
|
template<typename ValueType>
|
|
constexpr static ElementTypeReal CastElementTypeReal(const ValueType val) noexcept {
|
|
return static_cast<ElementTypeReal>(val);
|
|
}
|
|
constexpr static const ElementTypeReal kElementTypeRealOne = CastElementTypeReal(1.0);
|
|
constexpr static const ElementTypeReal kElementTypeRealZero = CastElementTypeReal(0.0);
|
|
|
|
RTree() {
|
|
// Precomputed volumes of the unit spheres for the first few dimensions
|
|
constexpr const ElementTypeReal kUnitSphereVolumes[] = {
|
|
CastElementTypeReal(0.000000), CastElementTypeReal(2.000000), CastElementTypeReal(3.141593), // Dimension 0,1,2
|
|
CastElementTypeReal(4.188790), CastElementTypeReal(4.934802), CastElementTypeReal(5.263789), // Dimension 3,4,5
|
|
CastElementTypeReal(5.167713), CastElementTypeReal(4.724766), CastElementTypeReal(4.058712), // Dimension 6,7,8
|
|
CastElementTypeReal(3.298509), CastElementTypeReal(2.550164), CastElementTypeReal(1.884104), // Dimension 9,10,11
|
|
CastElementTypeReal(1.335263), CastElementTypeReal(0.910629), CastElementTypeReal(0.599265), // Dimension 12,13,14
|
|
CastElementTypeReal(0.381443), CastElementTypeReal(0.235331), CastElementTypeReal(0.140981), // Dimension 15,16,17
|
|
CastElementTypeReal(0.082146), CastElementTypeReal(0.046622), CastElementTypeReal(0.025807), // Dimension 18,19,20
|
|
};
|
|
|
|
this->m_root = this->AllocNode();
|
|
this->m_root->m_level = 0;
|
|
this->m_unitSphereVolume = kUnitSphereVolumes[kNumDimensions];
|
|
}
|
|
|
|
RTree(const RTree& other) : RTree() {
|
|
this->CopyRec(this->m_root, other.m_root);
|
|
}
|
|
|
|
~RTree() {
|
|
this->Reset();
|
|
}
|
|
|
|
/// 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 Element& a_min, const Element& a_max, const DataType& a_dataId) {
|
|
#ifdef _DEBUG
|
|
for (int index = 0; index < kNumDimensions; ++index) {
|
|
RTreeAssert(a_min[index] <= a_max[index]);
|
|
}
|
|
#endif //_DEBUG
|
|
|
|
Branch branch;
|
|
branch.m_data = a_dataId;
|
|
branch.m_child = nullptr;
|
|
|
|
for (int axis = 0; axis < kNumDimensions; ++axis) {
|
|
branch.m_rect.m_min[axis] = a_min[axis];
|
|
branch.m_rect.m_max[axis] = a_max[axis];
|
|
}
|
|
|
|
InsertRect(branch, &m_root, 0);
|
|
}
|
|
|
|
/// 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 Element& a_min, const Element& a_max, const DataType& a_dataId) {
|
|
#ifdef _DEBUG
|
|
for (int index = 0; index < kNumDimensions; ++index) {
|
|
RTreeAssert(a_min[index] <= a_max[index]);
|
|
}
|
|
#endif //_DEBUG
|
|
|
|
Rect rect;
|
|
|
|
for (int axis = 0; axis < kNumDimensions; ++axis) {
|
|
rect.m_min[axis] = a_min[axis];
|
|
rect.m_max[axis] = a_max[axis];
|
|
}
|
|
|
|
RemoveRect(&rect, a_dataId, &m_root);
|
|
}
|
|
|
|
/// 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 Element& a_min, const Element& a_max, std::function<bool(const DataType&)> callback) const {
|
|
#ifdef _DEBUG
|
|
for (int index = 0; index < kNumDimensions; ++index) {
|
|
RTreeAssert(a_min[index] <= a_max[index]);
|
|
}
|
|
#endif //_DEBUG
|
|
|
|
Rect rect;
|
|
|
|
for (int axis = 0; axis < kNumDimensions; ++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;
|
|
}
|
|
|
|
/// Remove all entries from tree
|
|
void RemoveAll() {
|
|
|
|
// Delete all existing nodes
|
|
Reset();
|
|
|
|
m_root = AllocNode();
|
|
m_root->m_level = 0;
|
|
}
|
|
|
|
/// Count the data elements in this container. This is slow as no internal counter is maintained.
|
|
int Count() const {
|
|
|
|
int count = 0;
|
|
CountRec(m_root, count);
|
|
|
|
return count;
|
|
}
|
|
|
|
/// Load tree contents from file
|
|
bool 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;
|
|
}
|
|
/// Load tree contents from stream
|
|
bool 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 = kNumDimensions;
|
|
int _dataElemSize = sizeof(ElementType);
|
|
int _dataElemRealSize = sizeof(ElementTypeReal);
|
|
int _dataMaxNodes = kMaxNodeCount;
|
|
int _dataMinNodes = kMinNodeCount;
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
/// Save tree contents to file
|
|
bool Save(const char* a_fileName) {
|
|
|
|
RTFileStream stream;
|
|
if (!stream.OpenWrite(a_fileName)) {
|
|
return false;
|
|
}
|
|
|
|
bool result = Save(stream);
|
|
|
|
stream.Close();
|
|
|
|
return result;
|
|
}
|
|
/// Save tree contents to stream
|
|
bool 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 = kNumDimensions;
|
|
int dataElemSize = sizeof(ElementType);
|
|
int dataElemRealSize = sizeof(ElementTypeReal);
|
|
int dataMaxNodes = kMaxNodeCount;
|
|
int dataMinNodes = kMinNodeCount;
|
|
|
|
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;
|
|
}
|
|
|
|
/// Iterator is not remove safe.
|
|
class Iterator
|
|
{
|
|
private:
|
|
|
|
constexpr static const int kMaxStackSize = 32; // Max stack size. Allows almost n^32 where n is number of branches in node
|
|
|
|
struct StackElement
|
|
{
|
|
Node* m_node = nullptr;
|
|
int m_branchIndex = 0;
|
|
};
|
|
|
|
public:
|
|
|
|
Iterator() { Init(); }
|
|
|
|
~Iterator() = default;
|
|
|
|
/// Is iterator invalid
|
|
bool IsNull() const noexcept { return (m_tos <= 0); }
|
|
|
|
/// Is iterator pointing to valid data
|
|
bool IsNotNull() const noexcept { return (m_tos > 0); }
|
|
|
|
/// Access the current data element. Caller must be sure iterator is not NULL first.
|
|
DataType& operator*()
|
|
{
|
|
RTreeAssert(IsNotNull());
|
|
auto& 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
|
|
{
|
|
RTreeAssert(IsNotNull());
|
|
auto& 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(Element& a_min, Element& a_max)
|
|
{
|
|
RTreeAssert(IsNotNull());
|
|
auto& curTos = m_stack[m_tos - 1];
|
|
auto& curBranch = curTos.m_node->m_branch[curTos.m_branchIndex];
|
|
|
|
for (int index = 0; index < kNumDimensions; ++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;
|
|
}
|
|
auto 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
|
|
auto 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;
|
|
RTreeAssert(m_tos <= kMaxStackSize);
|
|
}
|
|
|
|
/// Pop element off iteration stack (For internal use only)
|
|
StackElement& Pop()
|
|
{
|
|
RTreeAssert(m_tos > 0);
|
|
--m_tos;
|
|
return m_stack[m_tos];
|
|
}
|
|
|
|
StackElement m_stack[kMaxStackSize]; ///< Stack as we are doing iteration instead of recursion
|
|
int m_tos = 0; ///< 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) const
|
|
{
|
|
a_it.Init();
|
|
auto 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) const { ++a_it; }
|
|
|
|
/// Is iterator NULL, or at end?
|
|
bool IsNull(const Iterator& a_it) const { 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
|
|
{
|
|
ElementType m_min[kNumDimensions] = { 0, }; ///< Min dimensions of bounding box
|
|
ElementType m_max[kNumDimensions] = { 0, }; ///< 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 = nullptr; ///< Child node
|
|
DataType m_data; ///< Data Id
|
|
};
|
|
|
|
/// Node for each branch level
|
|
struct Node
|
|
{
|
|
bool IsInternalNode() const noexcept { return (m_level > 0); } // Not a leaf, but a internal node
|
|
bool IsLeaf() const noexcept { return (m_level == 0); } // A leaf, contains data
|
|
|
|
int m_count = 0; ///< Count
|
|
int m_level = 0; ///< Leaf is zero, others positive
|
|
Branch m_branch[_MaxNodeCount]; ///< Branch
|
|
};
|
|
|
|
/// A link list of nodes for reinsertion after a delete operation
|
|
struct ListNode
|
|
{
|
|
ListNode* m_next = nullptr; ///< Next in list
|
|
Node* m_node = nullptr; ///< Node
|
|
};
|
|
|
|
/// Variables for finding a split partition
|
|
struct PartitionVars
|
|
{
|
|
constexpr static const int kNotTaken = -1; // indicates that position
|
|
|
|
int m_partition[_MaxNodeCount + 1] = { 0, };
|
|
int m_total = 0;
|
|
int m_minFill = 0;
|
|
int m_count[2] = { 0, };
|
|
Rect m_cover[2];
|
|
ElementTypeReal m_area[2] = { kElementTypeRealZero, };
|
|
|
|
Branch m_branchBuf[_MaxNodeCount + 1];
|
|
int m_branchCount = 0;
|
|
Rect m_coverSplit;
|
|
ElementTypeReal m_coverSplitArea = kElementTypeRealZero;
|
|
};
|
|
|
|
Node* 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;
|
|
}
|
|
|
|
void FreeNode(Node* a_node) {
|
|
RTreeAssert(a_node);
|
|
|
|
#ifdef RTREE_DONT_USE_MEMPOOLS
|
|
delete a_node;
|
|
#else // RTREE_DONT_USE_MEMPOOLS
|
|
// EXAMPLE
|
|
#endif // RTREE_DONT_USE_MEMPOOLS
|
|
}
|
|
|
|
void InitNode(Node* a_node) {
|
|
a_node->m_count = 0;
|
|
a_node->m_level = -1;
|
|
}
|
|
|
|
void InitRect(Rect* a_rect) {
|
|
for (int index = 0; index < kNumDimensions; ++index) {
|
|
a_rect->m_min[index] = CastElementType(0);
|
|
a_rect->m_max[index] = CastElementType(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.
|
|
bool InsertRectRec(const Branch& a_branch, Node* a_node, Node** a_newNode, int a_level) {
|
|
RTreeAssert(a_node && a_newNode);
|
|
RTreeAssert(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
|
|
RTreeAssert(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.
|
|
bool InsertRect(const Branch& a_branch, Node** a_root, int a_level) {
|
|
|
|
RTreeAssert(a_root);
|
|
RTreeAssert(a_level >= 0 && a_level <= (*a_root)->m_level);
|
|
#ifdef _DEBUG
|
|
for (int index = 0; index < kNumDimensions; ++index) {
|
|
RTreeAssert(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, nullptr);
|
|
|
|
// add the split node as a child of the new root
|
|
branch.m_rect = NodeCover(newNode);
|
|
branch.m_child = newNode;
|
|
AddBranch(&branch, newRoot, nullptr);
|
|
|
|
// 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.
|
|
Rect NodeCover(Node* a_node) {
|
|
RTreeAssert(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.
|
|
bool AddBranch(const Branch* a_branch, Node* a_node, Node** a_newNode) {
|
|
|
|
RTreeAssert(a_branch);
|
|
RTreeAssert(a_node);
|
|
|
|
if (a_node->m_count < _MaxNodeCount) // Split won't be necessary
|
|
{
|
|
a_node->m_branch[a_node->m_count] = *a_branch;
|
|
++a_node->m_count;
|
|
|
|
return false;
|
|
} else {
|
|
RTreeAssert(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
|
|
void DisconnectBranch(Node* a_node, int a_index) {
|
|
RTreeAssert(a_node && (a_index >= 0) && (a_index < _MaxNodeCount));
|
|
RTreeAssert(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.
|
|
int PickBranch(const Rect* a_rect, Node* a_node) {
|
|
RTreeAssert(a_rect && a_node);
|
|
|
|
bool firstTime = true;
|
|
ElementTypeReal increase;
|
|
ElementTypeReal bestIncr = CastElementTypeReal(-1);
|
|
ElementTypeReal area;
|
|
ElementTypeReal bestArea = CastElementTypeReal(-1);
|
|
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
|
|
Rect CombineRect(const Rect* a_rectA, const Rect* a_rectB) {
|
|
RTreeAssert(a_rectA && a_rectB);
|
|
|
|
Rect newRect;
|
|
|
|
for (int index = 0; index < kNumDimensions; ++index) {
|
|
newRect.m_min[index] = (std::min)(a_rectA->m_min[index], a_rectB->m_min[index]);
|
|
newRect.m_max[index] = (std::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.
|
|
void SplitNode(Node* a_node, const Branch* a_branch, Node** a_newNode) {
|
|
RTreeAssert(a_node);
|
|
RTreeAssert(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, _MinNodeCount);
|
|
|
|
// 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);
|
|
|
|
RTreeAssert((a_node->m_count + (*a_newNode)->m_count) == parVars->m_total);
|
|
}
|
|
|
|
// The exact volume of the bounding sphere for the given Rect
|
|
ElementTypeReal RectSphericalVolume(Rect* a_rect) {
|
|
RTreeAssert(a_rect);
|
|
|
|
ElementTypeReal sumOfSquares = kElementTypeRealZero;
|
|
|
|
for (int index = 0; index < kNumDimensions; ++index) {
|
|
const auto halfExtent = ((ElementTypeReal)a_rect->m_max[index] - (ElementTypeReal)a_rect->m_min[index]) * 0.5f;
|
|
sumOfSquares += halfExtent * halfExtent;
|
|
}
|
|
|
|
const auto radius = CastElementTypeReal(sqrt(sumOfSquares));
|
|
|
|
// Pow maybe slow, so test for common dims like 2,3 and just use x*x, x*x*x.
|
|
if (kNumDimensions == 3) {
|
|
return (radius * radius * radius * m_unitSphereVolume);
|
|
} else if (kNumDimensions == 2) {
|
|
return (radius * radius * m_unitSphereVolume);
|
|
} else {
|
|
return CastElementTypeReal(pow(radius, kNumDimensions) * m_unitSphereVolume);
|
|
}
|
|
}
|
|
|
|
// Calculate the n-dimensional volume of a rectangle
|
|
ElementTypeReal RectVolume(Rect* a_rect) {
|
|
RTreeAssert(a_rect);
|
|
|
|
auto volume = kElementTypeRealOne;
|
|
|
|
for (int index = 0; index < kNumDimensions; ++index) {
|
|
volume *= a_rect->m_max[index] - a_rect->m_min[index];
|
|
}
|
|
|
|
RTreeAssert(volume >= kElementTypeRealZero);
|
|
|
|
return volume;
|
|
}
|
|
|
|
// Use one of the methods to calculate retangle volume
|
|
ElementTypeReal 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.
|
|
void GetBranches(Node* a_node, const Branch* a_branch, PartitionVars* a_parVars) {
|
|
RTreeAssert(a_node);
|
|
RTreeAssert(a_branch);
|
|
|
|
RTreeAssert(a_node->m_count == _MaxNodeCount);
|
|
|
|
// Load the branch buffer
|
|
for (int index = 0; index < _MaxNodeCount; ++index) {
|
|
a_parVars->m_branchBuf[index] = a_node->m_branch[index];
|
|
}
|
|
a_parVars->m_branchBuf[_MaxNodeCount] = *a_branch;
|
|
a_parVars->m_branchCount = _MaxNodeCount + 1;
|
|
|
|
// Calculate rect containing all in the set
|
|
a_parVars->m_coverSplit = a_parVars->m_branchBuf[0].m_rect;
|
|
for (int index = 1; index < _MaxNodeCount + 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.
|
|
void ChoosePartition(PartitionVars* a_parVars, int a_minFill) {
|
|
RTreeAssert(a_parVars);
|
|
|
|
ElementTypeReal 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 = CastElementTypeReal(-1);
|
|
for (int index = 0; index < a_parVars->m_total; ++index) {
|
|
if (PartitionVars::kNotTaken == 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]);
|
|
ElementTypeReal growth0 = CalcRectVolume(&rect0) - a_parVars->m_area[0];
|
|
ElementTypeReal growth1 = CalcRectVolume(&rect1) - a_parVars->m_area[1];
|
|
ElementTypeReal 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::kNotTaken == a_parVars->m_partition[index]) {
|
|
Classify(index, group, a_parVars);
|
|
}
|
|
}
|
|
}
|
|
|
|
RTreeAssert((a_parVars->m_count[0] + a_parVars->m_count[1]) == a_parVars->m_total);
|
|
RTreeAssert((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.
|
|
void LoadNodes(Node* a_nodeA, Node* a_nodeB, PartitionVars* a_parVars) {
|
|
RTreeAssert(a_nodeA);
|
|
RTreeAssert(a_nodeB);
|
|
RTreeAssert(a_parVars);
|
|
|
|
for (int index = 0; index < a_parVars->m_total; ++index) {
|
|
RTreeAssert(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], nullptr);
|
|
(void)nodeWasSplit;
|
|
RTreeAssert(!nodeWasSplit);
|
|
}
|
|
}
|
|
|
|
// Initialize a PartitionVars structure.
|
|
void InitParVars(PartitionVars* a_parVars, int a_maxRects, int a_minFill) {
|
|
RTreeAssert(a_parVars);
|
|
|
|
a_parVars->m_count[0] = a_parVars->m_count[1] = 0;
|
|
a_parVars->m_area[0] = a_parVars->m_area[1] = kElementTypeRealZero;
|
|
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::kNotTaken;
|
|
}
|
|
}
|
|
|
|
void PickSeeds(PartitionVars* a_parVars) {
|
|
int seed0 = 0, seed1 = 0;
|
|
_ElementTypeReal worst{ 0.0 }, waste{ 0.0 };
|
|
_ElementTypeReal area[_MaxNodeCount + 1] = { 0.0, };
|
|
|
|
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) {
|
|
auto 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.
|
|
void Classify(int a_index, int a_group, PartitionVars* a_parVars) {
|
|
RTreeAssert(a_parVars);
|
|
RTreeAssert(PartitionVars::kNotTaken == 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.
|
|
bool RemoveRect(Rect* a_rect, const DataType& a_id, Node** a_root) {
|
|
RTreeAssert(a_rect && a_root);
|
|
RTreeAssert(*a_root);
|
|
|
|
ListNode* reInsertList{ nullptr };
|
|
|
|
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()) {
|
|
auto tempNode = (*a_root)->m_branch[0].m_child;
|
|
|
|
RTreeAssert(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.
|
|
bool RemoveRectRec(Rect* a_rect, const DataType& a_id, Node* a_node, ListNode** a_listNode) {
|
|
RTreeAssert(a_rect && a_node && a_listNode);
|
|
RTreeAssert(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 >= _MinNodeCount) {
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
// Allocate space for a node in the list used in DeletRect to
|
|
// store Nodes that are too empty.
|
|
ListNode* AllocListNode() {
|
|
#ifdef RTREE_DONT_USE_MEMPOOLS
|
|
return new ListNode;
|
|
#else // RTREE_DONT_USE_MEMPOOLS
|
|
// EXAMPLE
|
|
#endif // RTREE_DONT_USE_MEMPOOLS
|
|
}
|
|
|
|
void FreeListNode(ListNode* a_listNode) {
|
|
#ifdef RTREE_DONT_USE_MEMPOOLS
|
|
delete a_listNode;
|
|
#else // RTREE_DONT_USE_MEMPOOLS
|
|
// EXAMPLE
|
|
#endif // RTREE_DONT_USE_MEMPOOLS
|
|
}
|
|
|
|
// Decide whether two rectangles overlap.
|
|
bool Overlap(Rect* a_rectA, Rect* a_rectB) const {
|
|
RTreeAssert(a_rectA && a_rectB);
|
|
|
|
for (int index = 0; index < kNumDimensions; ++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.
|
|
void 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.
|
|
bool Search(Node* a_node, Rect* a_rect, int& a_foundCount, std::function<bool(const DataType&)> callback) const {
|
|
RTreeAssert(a_node);
|
|
RTreeAssert(a_node->m_level >= 0);
|
|
RTreeAssert(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)) {
|
|
const auto& id = a_node->m_branch[index].m_data;
|
|
++a_foundCount;
|
|
|
|
if (callback && !callback(id)) {
|
|
return false; // Don't continue searching
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true; // Continue searching
|
|
}
|
|
|
|
void RemoveAllRec(Node* a_node) {
|
|
RTreeAssert(a_node);
|
|
RTreeAssert(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);
|
|
}
|
|
|
|
void 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
|
|
}
|
|
|
|
void CountRec(Node* a_node, int& a_count) const {
|
|
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;
|
|
}
|
|
}
|
|
|
|
bool 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) {
|
|
const auto& curBranch = a_node->m_branch[index];
|
|
|
|
a_stream.WriteArray(curBranch->m_rect.m_min, kNumDimensions);
|
|
a_stream.WriteArray(curBranch->m_rect.m_max, kNumDimensions);
|
|
|
|
SaveRec(curBranch->m_child, a_stream);
|
|
}
|
|
} else // A leaf node
|
|
{
|
|
for (int index = 0; index < a_node->m_count; ++index) {
|
|
const auto& curBranch = &a_node->m_branch[index];
|
|
|
|
a_stream.WriteArray(curBranch->m_rect.m_min, kNumDimensions);
|
|
a_stream.WriteArray(curBranch->m_rect.m_max, kNumDimensions);
|
|
|
|
a_stream.Write(curBranch->m_data);
|
|
}
|
|
}
|
|
|
|
return true; // Should do more error checking on I/O operations
|
|
}
|
|
|
|
bool 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, kNumDimensions);
|
|
a_stream.ReadArray(curBranch->m_rect.m_max, kNumDimensions);
|
|
|
|
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, kNumDimensions);
|
|
a_stream.ReadArray(curBranch->m_rect.m_max, kNumDimensions);
|
|
|
|
a_stream.Read(curBranch->m_data);
|
|
}
|
|
}
|
|
|
|
return true; // Should do more error checking on I/O operations
|
|
}
|
|
|
|
void 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) {
|
|
auto& currentBranch = current->m_branch[index];
|
|
const auto& otherBranch = other->m_branch[index];
|
|
|
|
std::copy(otherBranch.m_rect.m_min,
|
|
otherBranch.m_rect.m_min + kNumDimensions,
|
|
currentBranch.m_rect.m_min);
|
|
|
|
std::copy(otherBranch.m_rect.m_max,
|
|
otherBranch.m_rect.m_max + kNumDimensions,
|
|
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) {
|
|
auto& currentBranch = current->m_branch[index];
|
|
const auto& otherBranch = other->m_branch[index];
|
|
|
|
std::copy(otherBranch.m_rect.m_min,
|
|
otherBranch.m_rect.m_min + kNumDimensions,
|
|
currentBranch.m_rect.m_min);
|
|
|
|
std::copy(otherBranch.m_rect.m_max,
|
|
otherBranch.m_rect.m_max + kNumDimensions,
|
|
currentBranch.m_rect.m_max);
|
|
|
|
currentBranch.m_data = otherBranch.m_data;
|
|
}
|
|
}
|
|
}
|
|
|
|
Node* m_root = nullptr; ///< Root of tree
|
|
ElementTypeReal m_unitSphereVolume = kElementTypeRealZero; ///< Unit sphere constant for required number of dimensions
|
|
};
|
|
|
|
#endif //RTREE_H
|