Loading files
4f95ecaa3This file is pinned to the selected release. Download the original to open it in your design tool.
// Created on: 2002-07-30
// Created by: Michael SAZONOV
// Copyright (c) 2002-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#ifndef NCollection_UBTree_HeaderFile
#define NCollection_UBTree_HeaderFile
#include <NCollection_BaseAllocator.hxx>
#include <NCollection_DefineAlloc.hxx>
#include <NCollection_LocalArray.hxx>
/**
* The algorithm of unbalanced binary tree of overlapped bounding boxes.
*
* Once the tree of boxes of geometric objects is constructed, the algorithm
* is capable of fast geometric selection of objects. The tree can be easily
* updated by adding to it a new object with bounding box.
*
* The time of adding to the tree of one object is O(log(N)), where N is the
* total number of objects, so the time of building a tree of N objects is
* O(N(log(N)). The search time of one object is O(log(N)).
*
* Defining various classes inheriting NCollection_UBTree::Selector we can
* perform various kinds of selection over the same b-tree object
*
* The object may be of any type allowing copying. Among the best suitable
* solutions there can be a pointer to an object, handled object or integer
* index of object inside some collection. The bounding object may have any
* dimension and geometry. The minimal interface of TheBndType (besides
* public empty and copy constructor and operator =) used in UBTree algorithm
* is as the following:
* @code
* class MyBndType
* {
* public:
* inline void Add (const MyBndType& other);
* // Updates me with other bounding
*
* inline bool IsOut (const MyBndType& other) const;
* // Classifies other bounding relatively me
*
* inline double SquareExtent() const;
* // Computes the squared maximal linear extent of me.
* // (For box it is the squared diagonal of box)
* };
* @endcode
* To select objects you need to define a class derived from UBTree::Selector
* that should redefine the necessary virtual methods to maintain the
* selection condition. The object of this class is also used to retrieve
* selected objects after search.
*/
template <class TheObjType, class TheBndType>
class NCollection_UBTree
{
public:
//! Memory allocation
DEFINE_STANDARD_ALLOC
DEFINE_NCOLLECTION_ALLOC
public:
// ---------- PUBLIC TYPES ----------
/**
* Class defining the minimal interface of selector.
*/
class Selector
{
public:
/**
* Constructor
*/
Selector()
: myStop(false)
{
}
/**
* Rejection base on the bounding type.
* @return
* True if the bounding box does not conform to some selection conditions
*/
virtual bool Reject(const TheBndType&) const = 0;
/**
* Confirm the object while making necessary tests on it. This method is
* called when the bounding box of the object conforms to the conditions
* (see Reject()). It is also supposed to keep record of accepted objects.
* @return
* True if the object is accepted
*/
virtual bool Accept(const TheObjType&) = 0;
/**
* This condition is checked after each call to Accept().
* @return
* True signals that the selection process is stopped
*/
bool Stop() const noexcept { return myStop; }
/**
* Destructor
*/
virtual ~Selector() = default;
protected:
/**
* The method Accept() should set this flag if the selection process
* is to be stopped
*/
bool myStop;
};
/**
* Class describing the node of the tree.
* Initially the tree consists of one leaf. A node can grow to a branch
* holding two childs:
* - one correspondent to initial node
* - the new one with a new object and bounding box
*/
class TreeNode
{
public:
DEFINE_STANDARD_ALLOC
DEFINE_NCOLLECTION_ALLOC
public:
TreeNode(const TheObjType& theObj, const TheBndType& theBnd)
: myBnd(theBnd),
myObject(theObj),
myChildren(nullptr),
myParent(nullptr)
{
}
bool IsLeaf() const noexcept { return !myChildren; }
bool IsRoot() const noexcept { return !myParent; }
const TheBndType& Bnd() const noexcept { return myBnd; }
TheBndType& ChangeBnd() noexcept { return myBnd; }
const TheObjType& Object() const noexcept { return myObject; }
const TreeNode& Child(const int i) const noexcept { return myChildren[i]; }
TreeNode& ChangeChild(const int i) noexcept { return myChildren[i]; }
const TreeNode& Parent() const noexcept { return *myParent; }
TreeNode& ChangeParent() noexcept { return *myParent; }
/**
* Forces *this node being gemmated such a way that it becomes
* a branch holding the previous content of *this node at the
* first child and theObj at the second child.
* @param theNewBnd
* new bounding box comprizing both child nodes.
* @param theObj
* added object.
* @param theBnd
* bounding box of theObj.
* @param theAlloc
* allocator providing memory to the new child nodes, provided by the
* calling Tree instance.
*/
void Gemmate(const TheBndType& theNewBnd,
const TheObjType& theObj,
const TheBndType& theBnd,
const occ::handle<NCollection_BaseAllocator>& theAlloc)
{
TreeNode* children = (TreeNode*)theAlloc->Allocate(2 * sizeof(TreeNode));
new (&children[0]) TreeNode;
new (&children[1]) TreeNode;
children[0] = *this;
children[1].myObject = theObj;
children[1].myBnd = theBnd;
children[0].myParent = children[1].myParent = this;
if (!IsLeaf())
{
myChildren[0].myParent = children;
myChildren[1].myParent = children;
}
myChildren = children;
myBnd = theNewBnd;
myObject = TheObjType(); // nullify myObject
}
/**
* Kills the i-th child, and *this accepts the content of another child
*/
void Kill(const int i, const occ::handle<NCollection_BaseAllocator>& theAlloc)
{
if (!IsLeaf())
{
TreeNode* oldChildren = myChildren;
const int iopp = 1 - i;
myBnd = oldChildren[iopp].myBnd;
myObject = oldChildren[iopp].myObject;
myChildren = oldChildren[iopp].myChildren;
if (!IsLeaf())
{
myChildren[0].myParent = this;
myChildren[1].myParent = this;
}
oldChildren[iopp].~TreeNode();
delNode(&oldChildren[i], theAlloc); // remove the whole branch
theAlloc->Free(oldChildren);
}
}
~TreeNode() { myChildren = nullptr; }
//! Deleter of tree node. The whole hierarchy of its children is also deleted.
//! This method should be used instead of operator delete.
//! Uses iterative traversal to avoid stack overflow on deeply unbalanced trees.
static void delNode(TreeNode* theNode, const occ::handle<NCollection_BaseAllocator>& theAlloc)
{
if (!theNode)
return;
// Collect children arrays during pre-order traversal, free them after.
constexpr int THE_INIT_STACK_SIZE = 64;
NCollection_LocalArray<TreeNode*, THE_INIT_STACK_SIZE> aChildArrays(THE_INIT_STACK_SIZE);
NCollection_LocalArray<TreeNode*, THE_INIT_STACK_SIZE> aStack(THE_INIT_STACK_SIZE);
int aNumArrays = 0;
int aTop = 0;
aStack[aTop++] = theNode;
while (aTop > 0)
{
TreeNode* aNode = aStack[--aTop];
if (aNode->myChildren)
{
// Record children array for later freeing
if (aNumArrays >= static_cast<int>(aChildArrays.Size()))
aChildArrays.Reallocate(aChildArrays.Size() * 2, true);
aChildArrays[aNumArrays++] = aNode->myChildren;
// Push both children for traversal
if (aTop + 2 > static_cast<int>(aStack.Size()))
aStack.Reallocate(aStack.Size() * 2, true);
aStack[aTop++] = &aNode->myChildren[1];
aStack[aTop++] = &aNode->myChildren[0];
}
aNode->~TreeNode();
}
// Free all collected children arrays
for (int i = 0; i < aNumArrays; ++i)
theAlloc->Free(aChildArrays[i]);
}
private:
TreeNode()
: myChildren(nullptr),
myParent(nullptr)
{
}
TheBndType myBnd; ///< bounding geometry
TheObjType myObject; ///< the object
TreeNode* myChildren; ///< 2 children forming a b-tree
TreeNode* myParent; ///< the pointer to a parent node
};
// ---------- PUBLIC METHODS ----------
/**
* Empty constructor.
*/
NCollection_UBTree()
: myRoot(nullptr),
myLastNode(nullptr),
myAlloc(NCollection_BaseAllocator::CommonBaseAllocator())
{
}
/**
* Constructor.
*/
explicit NCollection_UBTree(const occ::handle<NCollection_BaseAllocator>& theAllocator)
: myRoot(nullptr),
myLastNode(nullptr),
myAlloc(!theAllocator.IsNull() ? theAllocator
: NCollection_BaseAllocator::CommonBaseAllocator())
{
}
NCollection_UBTree(NCollection_UBTree&& theOther) noexcept
: myRoot(theOther.myRoot),
myLastNode(theOther.myLastNode),
myAlloc(std::move(theOther.myAlloc))
{
theOther.myRoot = nullptr;
theOther.myLastNode = nullptr;
}
NCollection_UBTree& operator=(NCollection_UBTree&& theOther) noexcept
{
if (this != &theOther)
{
Clear();
myRoot = theOther.myRoot;
myLastNode = theOther.myLastNode;
myAlloc = std::move(theOther.myAlloc);
theOther.myRoot = nullptr;
theOther.myLastNode = nullptr;
}
return *this;
}
/**
* Update the tree with a new object and its bounding box.
* @param theObj
* added object
* @param theBnd
* bounding box of the object.
* @return
* always True
*/
virtual bool Add(const TheObjType& theObj, const TheBndType& theBnd);
/**
* Searches in the tree all objects conforming to the given selector.
* @return
* Number of objects accepted
*/
virtual int Select(Selector& theSelector) const
{
return (IsEmpty() ? 0 : Select(Root(), theSelector));
}
/**
* Clears the contents of the tree.
* @param aNewAlloc
* Optional: a new allocator that will be used when the tree is rebuilt
* anew. This makes sense if the memory allocator needs re-initialisation
* (like NCollection_IncAllocator). By default the previous allocator is
* kept.
*/
virtual void Clear(const occ::handle<NCollection_BaseAllocator>& aNewAlloc = nullptr)
{
if (myRoot)
{
TreeNode::delNode(myRoot, this->myAlloc);
this->myAlloc->Free(myRoot);
myRoot = nullptr;
}
if (!aNewAlloc.IsNull())
myAlloc = aNewAlloc;
}
bool IsEmpty() const noexcept { return !myRoot; }
/**
* @return
* the root node of the tree
*/
const TreeNode& Root() const noexcept { return *myRoot; }
/**
* Destructor.
*/
virtual ~NCollection_UBTree() { Clear(); }
/**
* Recommended to be used only in sub-classes.
* @return
* Allocator object used in this instance of UBTree.
*/
const occ::handle<NCollection_BaseAllocator>& Allocator() const noexcept { return myAlloc; }
protected:
// ---------- PROTECTED METHODS ----------
/**
* @return
* the last added node
*/
TreeNode& ChangeLastNode() noexcept { return *myLastNode; }
/**
* Searches in the branch all objects conforming to the given selector.
* @return
* the number of objects accepted
*/
int Select(const TreeNode& theBranch, Selector& theSelector) const;
private:
// ---------- PRIVATE METHODS ----------
/// Copy constructor (prohibited).
NCollection_UBTree(const NCollection_UBTree&) = delete;
/// Assignment operator (prohibited).
NCollection_UBTree& operator=(const NCollection_UBTree&) = delete;
// ---------- PRIVATE FIELDS ----------
TreeNode* myRoot; ///< root of the tree
TreeNode* myLastNode; ///< the last added node
occ::handle<NCollection_BaseAllocator> myAlloc; ///< Allocator for TreeNode
};
//=================================================================================================
template <class TheObjType, class TheBndType>
bool NCollection_UBTree<TheObjType, TheBndType>::Add(const TheObjType& theObj,
const TheBndType& theBnd)
{
if (IsEmpty())
{
// Accepting first object
myRoot = new (this->myAlloc) TreeNode(theObj, theBnd);
myLastNode = myRoot;
return true;
}
TreeNode* pBranch = myRoot;
bool isOutOfBranch = pBranch->Bnd().IsOut(theBnd);
for (;;)
{
// condition of stopping the search
if (isOutOfBranch || pBranch->IsLeaf())
{
TheBndType aNewBnd = theBnd;
aNewBnd.Add(pBranch->Bnd());
// put the new leaf aside on the level of pBranch
pBranch->Gemmate(aNewBnd, theObj, theBnd, this->myAlloc);
myLastNode = &pBranch->ChangeChild(1);
break;
}
// Update the bounding box of the branch
pBranch->ChangeBnd().Add(theBnd);
// Select the best child branch to accept the object:
// 1. First check if one branch is out and another one is not.
// 2. Else select the child having the least union with theBnd
int iBest = 0;
bool isOut[] = {pBranch->Child(0).Bnd().IsOut(theBnd), pBranch->Child(1).Bnd().IsOut(theBnd)};
if (isOut[0] != isOut[1])
iBest = (isOut[0] ? 1 : 0);
else
{
TheBndType aUnion[] = {theBnd, theBnd};
aUnion[0].Add(pBranch->Child(0).Bnd());
aUnion[1].Add(pBranch->Child(1).Bnd());
const double d1 = aUnion[0].SquareExtent();
const double d2 = aUnion[1].SquareExtent();
if (d1 > d2)
iBest = 1;
}
// Continue with the selected branch
isOutOfBranch = isOut[iBest];
pBranch = &pBranch->ChangeChild(iBest);
}
return true;
}
//=================================================================================================
template <class TheObjType, class TheBndType>
int NCollection_UBTree<TheObjType, TheBndType>::Select(const TreeNode& theBranch,
Selector& theSelector) const
{
// Explicit stack for iterative DFS. Covers balanced trees up to 2^64 nodes;
// Reallocate handles deeply unbalanced trees.
constexpr int THE_INIT_STACK_SIZE = 64;
NCollection_LocalArray<const TreeNode*, THE_INIT_STACK_SIZE> aStack(THE_INIT_STACK_SIZE);
int aTop = 0;
int nSel = 0;
aStack[aTop++] = &theBranch;
while (aTop > 0)
{
const TreeNode* aNode = aStack[--aTop];
if (theSelector.Reject(aNode->Bnd()))
continue;
if (aNode->IsLeaf())
{
if (theSelector.Accept(aNode->Object()))
nSel++;
if (theSelector.Stop())
break;
}
else
{
// Ensure stack has space for 2 children
if (aTop + 2 > static_cast<int>(aStack.Size()))
aStack.Reallocate(aStack.Size() * 2, true);
// Push child(1) first so child(0) is processed first (LIFO order)
aStack[aTop++] = &aNode->Child(1);
aStack[aTop++] = &aNode->Child(0);
}
}
return nSel;
}
#endif