summaryrefslogtreecommitdiff
path: root/inc/NCollection_UBTree.hxx
blob: f4e7545d2f340fff172b270f35c3806fcec5b4df (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
// File:      NCollection_UBTree.hxx
// Created:   30.07.02 09:51:33
// Author:    Michael SAZONOV
// Copyright: Open CASCADE 2002

#ifndef NCollection_UBTree_HeaderFile
#define NCollection_UBTree_HeaderFile

#include <NCollection_BaseAllocator.hxx>

#ifdef WNT
// Disable the warning: "operator new unmatched by delete"
#pragma warning (push)
#pragma warning (disable:4291)
#endif

/**
 * 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 Standard_Boolean       IsOut (const MyBndType& other) const;
 *     // Classifies other bounding relatively me
 * 
 *     inline Standard_Real          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:
  // ---------- PUBLIC TYPES ----------

  /**
   * Class defining the minimal interface of selector.
   */
  class Selector
  {
  public:
    /**
     * Constructor
     */
    Selector () : myStop(Standard_False) {}

    /**
     * Rejection base on the bounding type.
     * @return
     *   True if the bounding box does not conform to some selection conditions
     */
    virtual Standard_Boolean 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 Standard_Boolean Accept (const TheObjType&) = 0;

    /**
     * This condition is checked after each call to Accept().
     * @return
     *   True signals that the selection process is stopped
     */
    Standard_Boolean Stop () const { return myStop; }

    /**
     * Destructor
     */
    virtual ~Selector () {}

  protected:
    /**
     * The method Accept() should set this flag if the selection process
     * is to be stopped
     */
    Standard_Boolean 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:
    TreeNode (const TheObjType& theObj, const TheBndType& theBnd)
      : myBnd(theBnd), myObject(theObj), myChildren(0), myParent(0) {}

    Standard_Boolean       IsLeaf       () const { return !myChildren; }
    Standard_Boolean       IsRoot       () const { return !myParent; }
    const TheBndType&      Bnd          () const { return myBnd; }
    TheBndType&            ChangeBnd    ()       { return myBnd; }
    const TheObjType&      Object       () const { return myObject; }
    const TreeNode&        Child        (const Standard_Integer i) const
                                                 { return myChildren[i]; }
    TreeNode&              ChangeChild  (const Standard_Integer i)
                                                 { return myChildren[i]; }
    const TreeNode&        Parent       () const { return *myParent; }
    TreeNode&              ChangeParent ()       { 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.
     * @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 Handle(NCollection_BaseAllocator)& theAlloc)
    {
      //TreeNode *children = new TreeNode [2];
      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 Standard_Integer i,
                             const Handle(NCollection_BaseAllocator)& theAlloc)
    {
      if (!IsLeaf()) {
        TreeNode *oldChildren = myChildren;
        const Standard_Integer 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[0].myChildren = oldChildren[1].myChildren = 0L;
        // delete [] oldChildren;
        oldChildren[iopp].~TreeNode();
        delNode(&oldChildren[i], theAlloc); // remove the whole branch
        theAlloc->Free(oldChildren);
      }
    }

//  ~TreeNode () { if (myChildren) delete [] myChildren; }
    ~TreeNode () { myChildren = 0L; }

    /**
     * Allocator of a tree node.
     */
    void * operator new (size_t theSize,
                         const Handle(NCollection_BaseAllocator)& theAllocator) 
    { return theAllocator->Allocate(theSize); }

    /**
     * Allocator of a tree node.
     */
    void * operator new (size_t,
                         void * theMem) 
    { return theMem; }

    /**
     * Deleter of tree node. The whole hierarchy of its children also deleted.
     * This method should be used instead of operator delete.
     */ 
    static void delNode (TreeNode * theNode,
                         Handle(NCollection_BaseAllocator)& theAlloc)
    {
      if (theNode) {
        if (theNode -> myChildren) {
          delNode (&theNode -> myChildren[0], theAlloc);
          delNode (&theNode -> myChildren[1], theAlloc);
          theAlloc->Free(theNode -> myChildren);
        }
        theNode->~TreeNode();
      }
    }

  private:
    TreeNode () : myChildren(0L), myParent(0L) {}

    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 ----------

  /**
   * Constructor.
   */
  NCollection_UBTree
    (const Handle(NCollection_BaseAllocator)& theAllocator=0L)
      : myRoot(0L), myLastNode(0L)
  {
    if (theAllocator.IsNull())
      myAlloc = NCollection_BaseAllocator::CommonBaseAllocator();
    else
      myAlloc = theAllocator;
  }

  /**
   * 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
   */
  Standard_EXPORT virtual Standard_Boolean Add (const TheObjType& theObj,
                                                const TheBndType& theBnd);

  /**
   * Searches in the tree all objects conforming to the given selector.
   * return
   *   Number of objects accepted
   */
  virtual Standard_Integer 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 Handle(NCollection_BaseAllocator)& aNewAlloc = 0L)
//      { if (myRoot) delete myRoot; myRoot = 0L; }
  {
    if (myRoot) {
      TreeNode::delNode (myRoot, this->myAlloc);
      this->myAlloc->Free (myRoot);
      myRoot = 0L;
    }
    if (aNewAlloc.IsNull() == Standard_False)
      myAlloc = aNewAlloc;
  }

  Standard_Boolean IsEmpty () const { return !myRoot; }

  /**
   * @return
   *   the root node of the tree
   */
  const TreeNode& Root () const { return *myRoot; }

  /**
   * Desctructor.
   */
  virtual ~NCollection_UBTree () { Clear(); }

  /**
   * Recommended to be used only in sub-classes.
   * @return
   *   Allocator object used in this instance of UBTree.
   */
  const Handle(NCollection_BaseAllocator)& Allocator () const
  { return myAlloc; }

 protected:
  // ---------- PROTECTED METHODS ----------

  /**
   * @return
   *   the last added node
   */
  TreeNode& ChangeLastNode () { return *myLastNode; }

  /**
   * Searches in the branch all objects conforming to the given selector.
   * @return
   *   the number of objects accepted
   */
  Standard_EXPORT Standard_Integer Select (const TreeNode& theBranch,
                                           Selector& theSelector) const;

 private:
  // ---------- PRIVATE METHODS ----------

  /// Copy constructor (prohibited).
  NCollection_UBTree (const NCollection_UBTree&);

  /// Assignment operator (prohibited).
  NCollection_UBTree& operator = (const NCollection_UBTree&);

  // ---------- PRIVATE FIELDS ----------

  TreeNode                            *myRoot;    ///< root of the tree
  TreeNode                            *myLastNode;///< the last added node
  Handle(NCollection_BaseAllocator)    myAlloc;   ///< Allocator for TreeNode
};

// ================== METHODS TEMPLATES =====================
//=======================================================================
//function : Add
//purpose  : Updates the tree with a new object and its bounding box
//=======================================================================

template <class TheObjType, class TheBndType>
Standard_Boolean 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 Standard_True;
  }

  TreeNode *pBranch = myRoot;
  Standard_Boolean 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
    Standard_Integer iBest = 0;
    Standard_Boolean 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 Standard_Real d1 = aUnion[0].SquareExtent();
      const Standard_Real d2 = aUnion[1].SquareExtent();
      if (d1 > d2)
        iBest = 1;
    }

    // Continue with the selected branch
    isOutOfBranch = isOut[iBest];
    pBranch = &pBranch->ChangeChild(iBest);
  }
  return Standard_True;
}

//=======================================================================
//function : Select
//purpose  : Recursively searches in the branch all objects conforming 
//           to the given selector.
//           Returns the number of objects found.
//=======================================================================

template <class TheObjType, class TheBndType>
Standard_Integer NCollection_UBTree<TheObjType,TheBndType>::Select
                                 (const TreeNode& theBranch,
                                  Selector&       theSelector) const
{
  // Try to reject the branch by bounding box
  if (theSelector.Reject (theBranch.Bnd()))
    return 0;

  Standard_Integer nSel = 0;

  if (theBranch.IsLeaf()) {
    // It is a leaf => try to accept the object
    if (theSelector.Accept (theBranch.Object()))
      nSel++;
  }
  else {
    // It is a branch => select from its children
    nSel += Select (theBranch.Child(0), theSelector);
    if (!theSelector.Stop())
      nSel += Select (theBranch.Child(1), theSelector);
  }

  return nSel;
}

// ======================================================================
/**
 * Declaration of handled version of NCollection_UBTree.
 * In the macros below the arguments are:
 * _HUBTREE      - the desired name of handled class
 * _OBJTYPE      - the name of the object type
 * _BNDTYPE      - the name of the bounding box type
 * _HPARENT      - the name of parent class (usually MMgt_TShared)
 */
#define DEFINE_HUBTREE(_HUBTREE, _OBJTYPE, _BNDTYPE, _HPARENT)          \
class _HUBTREE : public _HPARENT                                        \
{                                                                       \
 public:                                                                \
  typedef NCollection_UBTree <_OBJTYPE, _BNDTYPE> UBTree;               \
                                                                        \
  _HUBTREE () : myTree(new UBTree) {}                                   \
  /* Empty constructor */                                               \
  _HUBTREE (const Handle_NCollection_BaseAllocator& theAlloc)           \
     : myTree(new UBTree(theAlloc)) {}                                  \
  /* Constructor */                                                     \
                                                                        \
  /* Access to the methods of UBTree */                                 \
                                                                        \
  Standard_Boolean Add (const _OBJTYPE& theObj,                         \
                        const _BNDTYPE& theBnd)                         \
        { return ChangeTree().Add (theObj, theBnd); }                   \
                                                                        \
  Standard_Integer Select (UBTree::Selector& theSelector) const         \
        { return Tree().Select (theSelector); }                         \
                                                                        \
  void Clear () { ChangeTree().Clear (); }                              \
                                                                        \
  Standard_Boolean IsEmpty () const { return Tree().IsEmpty(); }        \
                                                                        \
  const UBTree::TreeNode& Root () const { return Tree().Root(); }       \
                                                                        \
                                                                        \
  /* Access to the tree algorithm */                                    \
                                                                        \
  const UBTree& Tree () const { return *myTree; }                       \
  UBTree&       ChangeTree () { return *myTree; }                       \
                                                                        \
  ~_HUBTREE () { delete myTree; }                                       \
  /* Destructor */                                                      \
                                                                        \
  DEFINE_STANDARD_RTTI (_HUBTREE)                                       \
  /* Type management */                                                 \
                                                                        \
 private:                                                               \
  /* Copying and assignment are prohibited  */                          \
  _HUBTREE (UBTree*);                                                   \
  _HUBTREE (const _HUBTREE&);                                           \
  void operator = (const _HUBTREE&);                                    \
                                                                        \
 private:                                                               \
  UBTree       *myTree;        /* pointer to the tree algorithm */      \
};                                                                      \
DEFINE_STANDARD_HANDLE (_HUBTREE, _HPARENT)

#define IMPLEMENT_HUBTREE(_HUBTREE, _HPARENT)                           \
IMPLEMENT_STANDARD_HANDLE (_HUBTREE, _HPARENT)                          \
IMPLEMENT_STANDARD_RTTIEXT(_HUBTREE, _HPARENT)

#ifdef WNT
#pragma warning (pop)
#endif

#endif