Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /include/opencascade/NCollection_UBTree.hxx was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 // Created on: 2002-07-30
0002 // Created by: Michael SAZONOV
0003 // Copyright (c) 2002-2014 OPEN CASCADE SAS
0004 //
0005 // This file is part of Open CASCADE Technology software library.
0006 //
0007 // This library is free software; you can redistribute it and/or modify it under
0008 // the terms of the GNU Lesser General Public License version 2.1 as published
0009 // by the Free Software Foundation, with special exception defined in the file
0010 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
0011 // distribution for complete text of the license and disclaimer of any warranty.
0012 //
0013 // Alternatively, this file may be used under the terms of Open CASCADE
0014 // commercial license or contractual agreement.
0015 
0016 #ifndef NCollection_UBTree_HeaderFile
0017 #define NCollection_UBTree_HeaderFile
0018 
0019 #include <NCollection_BaseAllocator.hxx>
0020 #include <NCollection_DefineAlloc.hxx>
0021 #include <NCollection_LocalArray.hxx>
0022 
0023 /**
0024  * The algorithm of unbalanced binary tree of overlapped bounding boxes.
0025  *
0026  * Once the tree of boxes  of geometric objects is constructed, the algorithm
0027  * is capable of fast geometric selection of objects.  The tree can be easily
0028  * updated by adding to it a new object with bounding box.
0029  *
0030  * The time of adding to the tree  of one object is O(log(N)), where N is the
0031  * total number of  objects, so the time  of building a tree of  N objects is
0032  * O(N(log(N)). The search time of one object is O(log(N)).
0033  *
0034  * Defining  various classes  inheriting NCollection_UBTree::Selector  we can
0035  * perform various kinds of selection over the same b-tree object
0036  *
0037  * The object  may be of any  type allowing copying. Among  the best suitable
0038  * solutions there can  be a pointer to an object,  handled object or integer
0039  * index of object inside some  collection.  The bounding object may have any
0040  * dimension  and  geometry. The  minimal  interface  of TheBndType  (besides
0041  * public empty and copy constructor and operator =) used in UBTree algorithm
0042  * is as the following:
0043  * @code
0044  *   class MyBndType
0045  *   {
0046  *    public:
0047  *     inline void                   Add (const MyBndType& other);
0048  *     // Updates me with other bounding
0049  *
0050  *     inline bool       IsOut (const MyBndType& other) const;
0051  *     // Classifies other bounding relatively me
0052  *
0053  *     inline double          SquareExtent() const;
0054  *     // Computes the squared maximal linear extent of me.
0055  *     // (For box it is the squared diagonal of box)
0056  *   };
0057  * @endcode
0058  * To select objects you need to define a class derived from UBTree::Selector
0059  * that  should  redefine  the  necessary  virtual methods  to  maintain  the
0060  * selection condition.  The object  of this class  is also used  to retrieve
0061  * selected objects after search.
0062  */
0063 
0064 template <class TheObjType, class TheBndType>
0065 class NCollection_UBTree
0066 {
0067 public:
0068   //! Memory allocation
0069   DEFINE_STANDARD_ALLOC
0070   DEFINE_NCOLLECTION_ALLOC
0071 
0072 public:
0073   // ---------- PUBLIC TYPES ----------
0074 
0075   /**
0076    * Class defining the minimal interface of selector.
0077    */
0078   class Selector
0079   {
0080   public:
0081     /**
0082      * Constructor
0083      */
0084     Selector()
0085         : myStop(false)
0086     {
0087     }
0088 
0089     /**
0090      * Rejection base on the bounding type.
0091      * @return
0092      *   True if the bounding box does not conform to some selection conditions
0093      */
0094     virtual bool Reject(const TheBndType&) const = 0;
0095 
0096     /**
0097      * Confirm the object while making necessary tests on it. This method is
0098      * called when the bounding box of the object conforms to the conditions
0099      * (see Reject()). It is also supposed to keep record of accepted objects.
0100      * @return
0101      *   True if the object is accepted
0102      */
0103     virtual bool Accept(const TheObjType&) = 0;
0104 
0105     /**
0106      * This condition is checked after each call to Accept().
0107      * @return
0108      *   True signals that the selection process is stopped
0109      */
0110     bool Stop() const noexcept { return myStop; }
0111 
0112     /**
0113      * Destructor
0114      */
0115     virtual ~Selector() = default;
0116 
0117   protected:
0118     /**
0119      * The method Accept() should set this flag if the selection process
0120      * is to be stopped
0121      */
0122     bool myStop;
0123   };
0124 
0125   /**
0126    * Class describing the node of the tree.
0127    * Initially the tree consists of one leaf. A node can grow to a branch
0128    * holding two childs:
0129    * - one correspondent to initial node
0130    * - the new one with a new object and bounding box
0131    */
0132   class TreeNode
0133   {
0134   public:
0135     DEFINE_STANDARD_ALLOC
0136     DEFINE_NCOLLECTION_ALLOC
0137 
0138   public:
0139     TreeNode(const TheObjType& theObj, const TheBndType& theBnd)
0140         : myBnd(theBnd),
0141           myObject(theObj),
0142           myChildren(nullptr),
0143           myParent(nullptr)
0144     {
0145     }
0146 
0147     bool IsLeaf() const noexcept { return !myChildren; }
0148 
0149     bool IsRoot() const noexcept { return !myParent; }
0150 
0151     const TheBndType& Bnd() const noexcept { return myBnd; }
0152 
0153     TheBndType& ChangeBnd() noexcept { return myBnd; }
0154 
0155     const TheObjType& Object() const noexcept { return myObject; }
0156 
0157     const TreeNode& Child(const int i) const noexcept { return myChildren[i]; }
0158 
0159     TreeNode& ChangeChild(const int i) noexcept { return myChildren[i]; }
0160 
0161     const TreeNode& Parent() const noexcept { return *myParent; }
0162 
0163     TreeNode& ChangeParent() noexcept { return *myParent; }
0164 
0165     /**
0166      * Forces *this node being gemmated such a way that it becomes
0167      * a branch holding the previous content of *this node at the
0168      * first child and theObj at the second child.
0169      * @param theNewBnd
0170      *   new bounding box comprizing both child nodes.
0171      * @param theObj
0172      *   added object.
0173      * @param theBnd
0174      *   bounding box of theObj.
0175      * @param theAlloc
0176      *   allocator providing memory to the new child nodes, provided by the
0177      *   calling Tree instance.
0178      */
0179     void Gemmate(const TheBndType&                             theNewBnd,
0180                  const TheObjType&                             theObj,
0181                  const TheBndType&                             theBnd,
0182                  const occ::handle<NCollection_BaseAllocator>& theAlloc)
0183     {
0184       TreeNode* children = (TreeNode*)theAlloc->Allocate(2 * sizeof(TreeNode));
0185       new (&children[0]) TreeNode;
0186       new (&children[1]) TreeNode;
0187       children[0]          = *this;
0188       children[1].myObject = theObj;
0189       children[1].myBnd    = theBnd;
0190       children[0].myParent = children[1].myParent = this;
0191       if (!IsLeaf())
0192       {
0193         myChildren[0].myParent = children;
0194         myChildren[1].myParent = children;
0195       }
0196       myChildren = children;
0197       myBnd      = theNewBnd;
0198       myObject   = TheObjType(); // nullify myObject
0199     }
0200 
0201     /**
0202      * Kills the i-th child, and *this accepts the content of another child
0203      */
0204     void Kill(const int i, const occ::handle<NCollection_BaseAllocator>& theAlloc)
0205     {
0206       if (!IsLeaf())
0207       {
0208         TreeNode* oldChildren = myChildren;
0209         const int iopp        = 1 - i;
0210         myBnd                 = oldChildren[iopp].myBnd;
0211         myObject              = oldChildren[iopp].myObject;
0212         myChildren            = oldChildren[iopp].myChildren;
0213         if (!IsLeaf())
0214         {
0215           myChildren[0].myParent = this;
0216           myChildren[1].myParent = this;
0217         }
0218         oldChildren[iopp].~TreeNode();
0219         delNode(&oldChildren[i], theAlloc); // remove the whole branch
0220         theAlloc->Free(oldChildren);
0221       }
0222     }
0223 
0224     ~TreeNode() { myChildren = nullptr; }
0225 
0226     //! Deleter of tree node. The whole hierarchy of its children is also deleted.
0227     //! This method should be used instead of operator delete.
0228     //! Uses iterative traversal to avoid stack overflow on deeply unbalanced trees.
0229     static void delNode(TreeNode* theNode, const occ::handle<NCollection_BaseAllocator>& theAlloc)
0230     {
0231       if (!theNode)
0232         return;
0233 
0234       // Collect children arrays during pre-order traversal, free them after.
0235       constexpr int                                          THE_INIT_STACK_SIZE = 64;
0236       NCollection_LocalArray<TreeNode*, THE_INIT_STACK_SIZE> aChildArrays(THE_INIT_STACK_SIZE);
0237       NCollection_LocalArray<TreeNode*, THE_INIT_STACK_SIZE> aStack(THE_INIT_STACK_SIZE);
0238       int                                                    aNumArrays = 0;
0239       int                                                    aTop       = 0;
0240 
0241       aStack[aTop++] = theNode;
0242 
0243       while (aTop > 0)
0244       {
0245         TreeNode* aNode = aStack[--aTop];
0246         if (aNode->myChildren)
0247         {
0248           // Record children array for later freeing
0249           if (aNumArrays >= static_cast<int>(aChildArrays.Size()))
0250             aChildArrays.Reallocate(aChildArrays.Size() * 2, true);
0251           aChildArrays[aNumArrays++] = aNode->myChildren;
0252 
0253           // Push both children for traversal
0254           if (aTop + 2 > static_cast<int>(aStack.Size()))
0255             aStack.Reallocate(aStack.Size() * 2, true);
0256           aStack[aTop++] = &aNode->myChildren[1];
0257           aStack[aTop++] = &aNode->myChildren[0];
0258         }
0259         aNode->~TreeNode();
0260       }
0261 
0262       // Free all collected children arrays
0263       for (int i = 0; i < aNumArrays; ++i)
0264         theAlloc->Free(aChildArrays[i]);
0265     }
0266 
0267   private:
0268     TreeNode()
0269         : myChildren(nullptr),
0270           myParent(nullptr)
0271     {
0272     }
0273 
0274     TheBndType myBnd;      ///< bounding geometry
0275     TheObjType myObject;   ///< the object
0276     TreeNode*  myChildren; ///< 2 children forming a b-tree
0277     TreeNode*  myParent;   ///< the pointer to a parent node
0278   };
0279 
0280   // ---------- PUBLIC METHODS ----------
0281 
0282   /**
0283    * Empty constructor.
0284    */
0285   NCollection_UBTree()
0286       : myRoot(nullptr),
0287         myLastNode(nullptr),
0288         myAlloc(NCollection_BaseAllocator::CommonBaseAllocator())
0289   {
0290   }
0291 
0292   /**
0293    * Constructor.
0294    */
0295   explicit NCollection_UBTree(const occ::handle<NCollection_BaseAllocator>& theAllocator)
0296       : myRoot(nullptr),
0297         myLastNode(nullptr),
0298         myAlloc(!theAllocator.IsNull() ? theAllocator
0299                                        : NCollection_BaseAllocator::CommonBaseAllocator())
0300   {
0301   }
0302 
0303   NCollection_UBTree(NCollection_UBTree&& theOther) noexcept
0304       : myRoot(theOther.myRoot),
0305         myLastNode(theOther.myLastNode),
0306         myAlloc(std::move(theOther.myAlloc))
0307   {
0308     theOther.myRoot     = nullptr;
0309     theOther.myLastNode = nullptr;
0310   }
0311 
0312   NCollection_UBTree& operator=(NCollection_UBTree&& theOther) noexcept
0313   {
0314     if (this != &theOther)
0315     {
0316       Clear();
0317       myRoot              = theOther.myRoot;
0318       myLastNode          = theOther.myLastNode;
0319       myAlloc             = std::move(theOther.myAlloc);
0320       theOther.myRoot     = nullptr;
0321       theOther.myLastNode = nullptr;
0322     }
0323     return *this;
0324   }
0325 
0326   /**
0327    * Update the tree with a new object and its bounding box.
0328    * @param theObj
0329    *   added object
0330    * @param theBnd
0331    *   bounding box of the object.
0332    * @return
0333    *   always True
0334    */
0335   virtual bool Add(const TheObjType& theObj, const TheBndType& theBnd);
0336 
0337   /**
0338    * Searches in the tree all objects conforming to the given selector.
0339    * @return
0340    *   Number of objects accepted
0341    */
0342   virtual int Select(Selector& theSelector) const
0343   {
0344     return (IsEmpty() ? 0 : Select(Root(), theSelector));
0345   }
0346 
0347   /**
0348    * Clears the contents of the tree.
0349    * @param aNewAlloc
0350    *   Optional:   a new allocator that will be used when the tree is rebuilt
0351    *   anew. This makes sense if the memory allocator needs re-initialisation
0352    *   (like NCollection_IncAllocator).  By default the previous allocator is
0353    *   kept.
0354    */
0355   virtual void Clear(const occ::handle<NCollection_BaseAllocator>& aNewAlloc = nullptr)
0356   {
0357     if (myRoot)
0358     {
0359       TreeNode::delNode(myRoot, this->myAlloc);
0360       this->myAlloc->Free(myRoot);
0361       myRoot = nullptr;
0362     }
0363     if (!aNewAlloc.IsNull())
0364       myAlloc = aNewAlloc;
0365   }
0366 
0367   bool IsEmpty() const noexcept { return !myRoot; }
0368 
0369   /**
0370    * @return
0371    *   the root node of the tree
0372    */
0373   const TreeNode& Root() const noexcept { return *myRoot; }
0374 
0375   /**
0376    * Destructor.
0377    */
0378   virtual ~NCollection_UBTree() { Clear(); }
0379 
0380   /**
0381    * Recommended to be used only in sub-classes.
0382    * @return
0383    *   Allocator object used in this instance of UBTree.
0384    */
0385   const occ::handle<NCollection_BaseAllocator>& Allocator() const noexcept { return myAlloc; }
0386 
0387 protected:
0388   // ---------- PROTECTED METHODS ----------
0389 
0390   /**
0391    * @return
0392    *   the last added node
0393    */
0394   TreeNode& ChangeLastNode() noexcept { return *myLastNode; }
0395 
0396   /**
0397    * Searches in the branch all objects conforming to the given selector.
0398    * @return
0399    *   the number of objects accepted
0400    */
0401   int Select(const TreeNode& theBranch, Selector& theSelector) const;
0402 
0403 private:
0404   // ---------- PRIVATE METHODS ----------
0405 
0406   /// Copy constructor (prohibited).
0407   NCollection_UBTree(const NCollection_UBTree&) = delete;
0408 
0409   /// Assignment operator (prohibited).
0410   NCollection_UBTree& operator=(const NCollection_UBTree&) = delete;
0411 
0412   // ---------- PRIVATE FIELDS ----------
0413 
0414   TreeNode*                              myRoot;     ///< root of the tree
0415   TreeNode*                              myLastNode; ///< the last added node
0416   occ::handle<NCollection_BaseAllocator> myAlloc;    ///< Allocator for TreeNode
0417 };
0418 
0419 //=================================================================================================
0420 
0421 template <class TheObjType, class TheBndType>
0422 bool NCollection_UBTree<TheObjType, TheBndType>::Add(const TheObjType& theObj,
0423                                                      const TheBndType& theBnd)
0424 {
0425   if (IsEmpty())
0426   {
0427     // Accepting first object
0428     myRoot     = new (this->myAlloc) TreeNode(theObj, theBnd);
0429     myLastNode = myRoot;
0430     return true;
0431   }
0432 
0433   TreeNode* pBranch       = myRoot;
0434   bool      isOutOfBranch = pBranch->Bnd().IsOut(theBnd);
0435 
0436   for (;;)
0437   {
0438     // condition of stopping the search
0439     if (isOutOfBranch || pBranch->IsLeaf())
0440     {
0441       TheBndType aNewBnd = theBnd;
0442       aNewBnd.Add(pBranch->Bnd());
0443       // put the new leaf aside on the level of pBranch
0444       pBranch->Gemmate(aNewBnd, theObj, theBnd, this->myAlloc);
0445       myLastNode = &pBranch->ChangeChild(1);
0446       break;
0447     }
0448 
0449     // Update the bounding box of the branch
0450     pBranch->ChangeBnd().Add(theBnd);
0451 
0452     // Select the best child branch to accept the object:
0453     // 1. First check if one branch is out and another one is not.
0454     // 2. Else select the child having the least union with theBnd
0455     int  iBest   = 0;
0456     bool isOut[] = {pBranch->Child(0).Bnd().IsOut(theBnd), pBranch->Child(1).Bnd().IsOut(theBnd)};
0457     if (isOut[0] != isOut[1])
0458       iBest = (isOut[0] ? 1 : 0);
0459     else
0460     {
0461       TheBndType aUnion[] = {theBnd, theBnd};
0462       aUnion[0].Add(pBranch->Child(0).Bnd());
0463       aUnion[1].Add(pBranch->Child(1).Bnd());
0464       const double d1 = aUnion[0].SquareExtent();
0465       const double d2 = aUnion[1].SquareExtent();
0466       if (d1 > d2)
0467         iBest = 1;
0468     }
0469 
0470     // Continue with the selected branch
0471     isOutOfBranch = isOut[iBest];
0472     pBranch       = &pBranch->ChangeChild(iBest);
0473   }
0474   return true;
0475 }
0476 
0477 //=================================================================================================
0478 
0479 template <class TheObjType, class TheBndType>
0480 int NCollection_UBTree<TheObjType, TheBndType>::Select(const TreeNode& theBranch,
0481                                                        Selector&       theSelector) const
0482 {
0483   // Explicit stack for iterative DFS. Covers balanced trees up to 2^64 nodes;
0484   // Reallocate handles deeply unbalanced trees.
0485   constexpr int                                                THE_INIT_STACK_SIZE = 64;
0486   NCollection_LocalArray<const TreeNode*, THE_INIT_STACK_SIZE> aStack(THE_INIT_STACK_SIZE);
0487   int                                                          aTop = 0;
0488   int                                                          nSel = 0;
0489 
0490   aStack[aTop++] = &theBranch;
0491 
0492   while (aTop > 0)
0493   {
0494     const TreeNode* aNode = aStack[--aTop];
0495 
0496     if (theSelector.Reject(aNode->Bnd()))
0497       continue;
0498 
0499     if (aNode->IsLeaf())
0500     {
0501       if (theSelector.Accept(aNode->Object()))
0502         nSel++;
0503       if (theSelector.Stop())
0504         break;
0505     }
0506     else
0507     {
0508       // Ensure stack has space for 2 children
0509       if (aTop + 2 > static_cast<int>(aStack.Size()))
0510         aStack.Reallocate(aStack.Size() * 2, true);
0511       // Push child(1) first so child(0) is processed first (LIFO order)
0512       aStack[aTop++] = &aNode->Child(1);
0513       aStack[aTop++] = &aNode->Child(0);
0514     }
0515   }
0516   return nSel;
0517 }
0518 
0519 #endif