Nyall Dawson da5cd52162 [pal] Bring back refined upstream version of superliminal rtree index
Turns out this index is MUCH (magnitudes) faster for use in pal. So
grab an updated version of the upstream library and place in external libs,
and use this for indices in pal.

(we should probably investigate whether this is faster for snapping and
other index use too!)
2019-12-15 10:18:47 +10:00

1332 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;
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);
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