1 /**************************************************************************\
2  * Copyright (c) Kongsberg Oil & Gas Technologies AS
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  *
12  * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the distribution.
15  *
16  * Neither the name of the copyright holder nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32 
33 #include <Inventor/SbBSPTree.h>
34 #include <Inventor/SbSphere.h>
35 #include <cassert>
36 #include <cstdlib>
37 #include <cstdio>
38 #include <cfloat>
39 
40 #include "coindefs.h"
41 
42 // define this to do a sorted split (slower, but more efficient?)
43 //#define BSP_SORTED_SPLIT
44 
45 
46 
47 /*!
48   \class SbBSPTree SbBSPTree.h Inventor/SbBSPTree.h
49   \brief The SbBSPTree class provides a binary space partitioning container.
50 
51   \ingroup base
52 
53   This class can be used to organize searches for 3D points or normals
54   in a set in O(log(n)) time.
55 
56   Note: SbBSPTree is an extension to the original Open Inventor API.
57 */
58 
59 
60 class coin_bspnode
61 {
62 public:
63   coin_bspnode(SbList <SbVec3f> *array);
64   ~coin_bspnode();
65 
66   int addPoint(const SbVec3f &pt, const int maxpts);
67   int findPoint(const SbVec3f &pt) const;
68   void findPoints(const SbSphere &sphere, SbList <int> &array);
69   void findPoints(const SbSphere &sphere, SbIntList & array);
70   int removePoint(const SbVec3f &pt);
71   void updateIndex(const SbVec3f & pt, int previdx, int newidx);
72 
73 private:
74   void sort();
75   void split();
76   int leftOf(const SbVec3f &pt) const;
77 
78   enum {
79     // do not change these values!
80     DIM_YZ = 0,
81     DIM_XZ = 1,
82     DIM_XY = 2,
83     DIM_NONE
84   };
85 
86   coin_bspnode *left;
87   coin_bspnode *right;
88   int dimension;   // which dimension?
89   double position;  // position in dimension (double to avoid floating point precision problems)
90   SbList <int> indices;
91   SbList <SbVec3f> *pointsArray;
92 };
93 
coin_bspnode(SbList<SbVec3f> * ptsarray)94 coin_bspnode::coin_bspnode(SbList <SbVec3f> *ptsarray)
95   : indices(4)
96 {
97   this->left = this->right = NULL;
98   this->pointsArray = ptsarray;
99   this->dimension = DIM_NONE;
100 }
101 
~coin_bspnode()102 coin_bspnode::~coin_bspnode()
103 {
104   delete left;
105   delete right;
106 }
107 
108 inline int
leftOf(const SbVec3f & pt) const109 coin_bspnode::leftOf(const SbVec3f &pt) const
110 {
111   return double(pt[this->dimension]) < this->position;
112 }
113 
114 int
addPoint(const SbVec3f & pt,const int maxpts)115 coin_bspnode::addPoint(const SbVec3f &pt, const int maxpts)
116 {
117   if (this->left) { // node has been split
118     if (this->leftOf(pt)) return this->left->addPoint(pt, maxpts);
119     else return this->right->addPoint(pt, maxpts);
120   }
121   else if (this->indices.getLength() >= maxpts) {
122     split();
123     return this->addPoint(pt, maxpts);
124   }
125   else {
126     int n = this->indices.getLength();
127     int i;
128     SbVec3f tmp;
129     for (i = 0; i < n; i++) {
130       tmp = (*pointsArray)[this->indices[i]];
131       if (pt == tmp) break;
132     }
133     if (i == n) {
134       int idx = this->pointsArray->getLength();
135       this->pointsArray->append(pt);
136       this->indices.append(idx);
137       return idx;
138     }
139     return this->indices[i];
140   }
141 }
142 
143 int
findPoint(const SbVec3f & pt) const144 coin_bspnode::findPoint(const SbVec3f &pt) const
145 {
146   if (this->left) {
147     if (this->leftOf(pt)) return this->left->findPoint(pt);
148     else return this->right->findPoint(pt);
149   }
150   else {
151     int i, n = this->indices.getLength();
152     for (i = 0; i < n; i++) {
153       SbVec3f arrpt = (*pointsArray)[this->indices[i]];
154       if (pt == arrpt) return this->indices[i];
155     }
156   }
157   return -1;
158 }
159 
160 void
findPoints(const SbSphere & sphere,SbList<int> & array)161 coin_bspnode::findPoints(const SbSphere &sphere, SbList <int> &array)
162 {
163   if (this->left) {
164     SbVec3f min, max;
165     min = max = sphere.getCenter();
166     min[this->dimension] -= sphere.getRadius();
167     max[this->dimension] += sphere.getRadius();
168 
169     if (this->leftOf(min)) this->left->findPoints(sphere, array);
170     if (!this->leftOf(max)) this->right->findPoints(sphere, array);
171   }
172   else {
173     int i, n = this->indices.getLength();
174     for (i = 0; i < n; i++) {
175       SbVec3f pt = (*pointsArray)[this->indices[i]];
176       if (sphere.pointInside(pt)) array.append(this->indices[i]);
177     }
178   }
179 }
180 
181 void
findPoints(const SbSphere & sphere,SbIntList & array)182 coin_bspnode::findPoints(const SbSphere &sphere, SbIntList & array)
183 {
184   if (this->left) {
185     SbVec3f min, max;
186     min = max = sphere.getCenter();
187     min[this->dimension] -= sphere.getRadius();
188     max[this->dimension] += sphere.getRadius();
189 
190     if (this->leftOf(min)) this->left->findPoints(sphere, array);
191     if (!this->leftOf(max)) this->right->findPoints(sphere, array);
192   }
193   else {
194     int i, n = this->indices.getLength();
195     for (i = 0; i < n; i++) {
196       SbVec3f pt = (*pointsArray)[this->indices[i]];
197       if (sphere.pointInside(pt)) array.append(this->indices[i]);
198     }
199   }
200 }
201 
202 /*
203   Used to update index after a point is removed.
204 */
205 void
updateIndex(const SbVec3f & pt,int previdx,int newidx)206 coin_bspnode::updateIndex(const SbVec3f & pt, int previdx, int newidx)
207 {
208   if (this->left) {
209     if (this->leftOf(pt)) this->left->updateIndex(pt, previdx, newidx);
210     else this->right->updateIndex(pt, previdx, newidx);
211   }
212   else {
213     int i, n = this->indices.getLength();
214     for (i = 0; i < n; i++) {
215       if (this->indices[i] == previdx) {
216         this->indices[i] = newidx;
217         return;
218       }
219     }
220   }
221 }
222 
223 int
removePoint(const SbVec3f & pt)224 coin_bspnode::removePoint(const SbVec3f &pt)
225 {
226   if (this->left) {
227     if (this->leftOf(pt)) return this->left->removePoint(pt);
228     else return this->right->removePoint(pt);
229   }
230   else {
231     int i, n = this->indices.getLength();
232     for (i = 0; i < n; i++) {
233       SbVec3f arrpt = (*pointsArray)[this->indices[i]];
234       if (pt == arrpt) {
235         int idx = this->indices[i];
236         // just remove point from index array here. The invoker will
237         // remove the point from the pointsArray
238         this->indices.removeFast(i);
239         return idx;
240       }
241     }
242   }
243   return -1;
244 }
245 
246 void
split()247 coin_bspnode::split()
248 {
249   assert(this->left == NULL && this->right == NULL);
250   this->left = new coin_bspnode(this->pointsArray);
251   this->right = new coin_bspnode(this->pointsArray);
252 
253   SbBox3f box;
254   int i, n = this->indices.getLength();
255   for (i = 0; i < n; i++) {
256     box.extendBy((*pointsArray)[this->indices[i]]);
257   }
258   SbVec3f diag = box.getMax() - box.getMin();
259   int dim;
260   double pos;
261 
262   if (diag[0] > diag[1]) {
263     if (diag[0] > diag[2]) dim = DIM_YZ;
264     else dim = DIM_XY;
265   }
266   else {
267     if (diag[1] > diag[2]) dim = DIM_XZ;
268     else dim = DIM_XY;
269   }
270 
271   this->dimension = dim; // set the dimension
272 
273   float mid = (box.getMin()[dim] + box.getMax()[dim]) / 2.0f;
274 #ifdef BSP_SORTED_SPLIT
275   this->sort(); // sort vertices on ascending dimension values
276 
277   int splitidx = n / 2;
278   pos = ((*pointsArray)[this->indices[splitidx-1]][dim]+
279     (*pointsArray)[this->indices[splitidx]][dim]) / 2.0f;
280 
281   // got to check and adjust for special cases
282   if (pos == box.getMin()[dim] || pos == box.getMax()[dim]) {
283     pos = (pos + mid) / 2.0f;
284   }
285 
286 #else
287   pos = (double(box.getMin()[this->dimension])+double(box.getMax()[this->dimension])) / 2.0;
288 #endif // BSP_SORTED_SPLIT
289 
290   this->position = pos;
291 
292   for (i = 0; i < n; i++) {
293     int idx = this->indices[i];
294     if (this->leftOf((*pointsArray)[idx]))
295       this->left->indices.append(idx);
296     else
297       this->right->indices.append(idx);
298   }
299 
300 //   fprintf(stderr,"bsp split: %.3f %.3f %.3f, %.3f %.3f %.3f "
301 //        "==> (%d %d) %d %.3f\n",
302 //        box.min[0], box.min[1], box.min[2],
303 //        box.max[0], box.max[1], box.max[2],
304 //        this->left->indices.getLength(), this->right->indices.getLength(),
305 //        this->dimension, this->position);
306 
307 //   for (i = 0; i < n; i++) {
308 //     SbVec3f p;
309 //     this->pointsArray->getElem(this->indices[i], p);
310 //     fprintf(stderr, "pt %d: %.3f %.3f %.3f\n", i, p[0], p[1], p[2]);
311 //   }
312 
313 
314 #if COIN_DEBUG
315   if (!this->left->indices.getLength() ||
316       !this->right->indices.getLength()) {
317     fprintf(stderr,"Left:\n");
318     n = this->indices.getLength();
319     const SbVec3f * pts = this->pointsArray->getArrayPtr();
320     for (i = 0; i < n; i++) {
321       SbVec3f vec = pts[this->indices[i]];
322       fprintf(stderr,"pt: %f %f %f\n",
323               vec[0], vec[1], vec[2]);
324     }
325     fprintf(stderr,"pos: %f\n",
326             pos);
327     fprintf(stderr,"mid: %f\n",
328             mid);
329     fprintf(stderr,"dim: %d\n", dim);
330     fprintf(stderr,"diag: %f %f %f\n",
331             diag[0], diag[1], diag[2]);
332 
333 #ifdef BSP_SORTED_SPLIT
334     fprintf(stderr,"splitidx: %d\n", splitidx);
335 #endif
336   }
337 
338 #endif
339   assert(this->left->indices.getLength() && this->right->indices.getLength());
340 
341 
342   // will never be used anymore
343   this->indices.truncate(0, TRUE);
344 }
345 
346 //
347 // an implementation of the shellsort algorithm
348 //
349 void
sort()350 coin_bspnode::sort()
351 {
352   int num = this->indices.getLength();
353   int dim = this->dimension;
354   const SbVec3f * points = this->pointsArray->getArrayPtr();
355   int i, j, distance;
356   int idx;
357   for (distance = 1; distance <= num/9; distance = 3*distance + 1) ;
358   for (; distance > 0; distance /= 3) {
359     for (i = distance; i < num; i++) {
360       idx = this->indices[i];
361       j = i;
362       while (j >= distance &&
363              points[this->indices[j-distance]][dim] > points[idx][dim]) {
364         this->indices[j] = this->indices[j-distance];
365         j -= distance;
366       }
367       this->indices[j] = idx;
368     }
369   }
370 }
371 
372 /*!
373   Constructor with \a maxnodepts specifying the maximum number of
374   points in a node before it must be split, and \a initsize
375   is the number of initially allocated points in the growable
376   points array. If you know approximately the number of points
377   which will be added to the tree, it will help the performance
378   if you supply this in \a initsize.
379  */
SbBSPTree(const int maxnodepts,const int initsize)380 SbBSPTree::SbBSPTree(const int maxnodepts, const int initsize)
381   : pointsArray(initsize),
382     userdataArray(initsize)
383 {
384   this->topnode = new coin_bspnode(&this->pointsArray);
385   this->maxnodepoints = maxnodepts;
386 }
387 
388 /*!
389   Destructor. Frees used memory.
390 */
~SbBSPTree()391 SbBSPTree::~SbBSPTree()
392 {
393   delete this->topnode;
394 }
395 
396 /*!
397   Returns the number of points in the BSP tree.
398 */
399 int
numPoints() const400 SbBSPTree::numPoints() const
401 {
402   return this->pointsArray.getLength();
403 }
404 
405 /*!
406   Returns the point at index \a idx.
407   \sa SbBSPTree::numPoints()
408 */
409 SbVec3f
getPoint(const int idx) const410 SbBSPTree::getPoint(const int idx) const
411 {
412   assert(idx < this->pointsArray.getLength());
413   return this->pointsArray[idx];
414 }
415 
416 /*!
417   \overload
418 */
419 void
getPoint(const int idx,SbVec3f & pt) const420 SbBSPTree::getPoint(const int idx, SbVec3f &pt) const
421 {
422   assert(idx < this->pointsArray.getLength());
423   pt = this->pointsArray[idx];
424 }
425 
426 /*!
427   Returns the user data for the point at index \a idx.
428   \sa SbBSPTree::addPoint()
429   \sa SbBSPTree::numPoints()
430 */
431 void *
getUserData(const int idx) const432 SbBSPTree::getUserData(const int idx) const
433 {
434   assert(idx < this->userdataArray.getLength());
435   return this->userdataArray[idx];
436 }
437 
438 /*!
439   Sets the user data for the point at index \a idx to \a data.
440   \sa SbBSPTree::addPoint()
441   \sa SbBSPTree::numPoints()
442 */
443 void
setUserData(const int idx,void * const data)444 SbBSPTree::setUserData(const int idx, void * const data)
445 {
446   assert(idx < this->userdataArray.getLength());
447   this->userdataArray[idx] = data;
448 }
449 
450 /*!
451   Adds a new point \a pt to the BSP tree, and returns the index to
452   the new point. The user data for that point will be set to \a data.
453 
454   If the point already exists in the BSP tree, the index to the
455   old point will be returned. The user data for that point will
456   not be changed.
457 
458   \sa SbBSPTree::findPoint()
459 */
460 int
addPoint(const SbVec3f & pt,void * const data)461 SbBSPTree::addPoint(const SbVec3f &pt, void * const data)
462 {
463   this->boundingBox.extendBy(pt);
464   int ret = this->topnode->addPoint(pt, this->maxnodepoints);
465   if (ret == this->userdataArray.getLength()) {
466     this->userdataArray.append(data);
467   }
468   return ret;
469 }
470 
471 /*!
472   Removes the point with coordinates \a pt, and returns the index
473   to the removed point. -1 is returned if no point with those
474   coordinates could be found.
475 */
476 int
removePoint(const SbVec3f & pt)477 SbBSPTree::removePoint(const SbVec3f &pt)
478 {
479   int idx = this->topnode->removePoint(pt);
480   if (idx >= 0) {
481     // SbList::removeFast() will move the last item onto the removed item
482     // to avoid copying/moving all the data. We need to notify the node that
483     // has that last point that the index has changed.
484     int lastidx = this->pointsArray.getLength() - 1;
485     if (lastidx != idx) {
486       // update index
487       this->topnode->updateIndex(this->pointsArray[lastidx], lastidx, idx);
488     }
489     // actually remove the point (copy lastidx onto idx, decrement size)
490     this->pointsArray.removeFast(idx);
491     this->userdataArray.removeFast(idx);
492   }
493   return idx;
494 }
495 
496 /*!
497   Removes the point at index \a idx.
498   \sa SbBSPTree::numPoints()
499 */
500 void
removePoint(const int idx)501 SbBSPTree::removePoint(const int idx)
502 {
503   assert(idx < this->pointsArray.getLength());
504   this->removePoint(this->pointsArray[idx]);
505 }
506 
507 /*!
508   Will search the tree, and return the index to the point
509   with coordinates matching \a pos. If no such point can be
510   found, -1 is returned.
511 */
512 int
findPoint(const SbVec3f & pos) const513 SbBSPTree::findPoint(const SbVec3f &pos) const
514 {
515   return topnode->findPoint(pos);
516 }
517 
518 /*!
519   Will empty all points from the BSP tree.
520 */
521 void
clear(const int COIN_UNUSED_ARG (initsize))522 SbBSPTree::clear(const int COIN_UNUSED_ARG(initsize))
523 {
524   delete this->topnode;
525   this->topnode = NULL;
526   this->pointsArray.truncate(0, TRUE);
527   this->userdataArray.truncate(0, TRUE);
528   this->topnode = new coin_bspnode(&this->pointsArray);
529   this->boundingBox.makeEmpty();
530 }
531 
532 /*!
533   Will return the bounding box of all points in the BSP tree.
534 */
535 const SbBox3f &
getBBox() const536 SbBSPTree::getBBox() const
537 {
538   return this->boundingBox;
539 }
540 
541 /*!
542   \overload
543 */
544 int
findClosest(const SbVec3f & pos) const545 SbBSPTree::findClosest(const SbVec3f &pos) const
546 {
547   int n = this->pointsArray.getLength();
548   if (n < 32) { // points are very scattered when few are inserted
549     SbVec3f tmp;
550     int smallidx = -1;
551     float smalldist = FLT_MAX;
552     for (int i = 0; i < n; i++) {
553       tmp = this->pointsArray[i];
554       float dist = (tmp-pos).sqrLength();
555       if (dist < smalldist) {
556         smalldist = dist;
557         smallidx = i;
558       }
559     }
560     return smallidx;
561   }
562   SbVec3f center =
563     (this->boundingBox.getMin() +
564      this->boundingBox.getMax()) * 0.5f;
565   center -= pos;
566 
567   float siz = center.length() * 2 +
568     (this->boundingBox.getMax()-this->boundingBox.getMin()).length();
569 
570   float currsize = siz / 65536.0f;  // max 16 iterations (too much?).
571 
572   SbSphere sphere(pos, currsize);
573   SbList <int> tmparray; // use only one array to avoid reallocs
574   int idx = -1;
575 
576   // double size of sphere until a vertex is found
577   while (currsize < siz) {
578     sphere.setRadius(currsize);
579     tmparray.truncate(0);
580     idx = this->findClosest(sphere, tmparray);
581     if (idx >= 0) return idx;
582     currsize *= 2;
583   }
584   assert(0);
585   return -1; // this should not happen!
586 }
587 
588 /*!
589   Returns a pointer to the array of points inserted into the BPS tree.
590 */
591 const SbVec3f *
getPointsArrayPtr(void) const592 SbBSPTree::getPointsArrayPtr(void) const
593 {
594   return this->pointsArray.getArrayPtr();
595 }
596 
597 /*!
598   Will return indices to all points inside \a sphere.
599 
600   \since Coin 2.3
601 */
602 void
findPoints(const SbSphere & sphere,SbIntList & array) const603 SbBSPTree::findPoints(const SbSphere & sphere, SbIntList & array) const
604 {
605   this->topnode->findPoints(sphere, array);
606 }
607 
608 /*!
609   Will return the index to the point closest to the center of \a
610   sphere. Indices to all points inside the sphere is returned in
611   \a arr. If no points can be found inside the sphere, -1 is
612   returned.
613 
614   \since Coin 2.3
615 */
616 int
findClosest(const SbSphere & sphere,SbIntList & arr) const617 SbBSPTree::findClosest(const SbSphere & sphere, SbIntList & arr) const
618 {
619   this->findPoints(sphere, arr);
620   SbVec3f pos = sphere.getCenter();
621   int n = arr.getLength();
622   int closeidx = -1;
623   float closedist = FLT_MAX;
624   for (int i = 0; i < n; i++) {
625     int idx = arr[i];
626     float tmp = (pos-this->pointsArray[idx]).sqrLength();
627     if (tmp < closedist) {
628       closeidx = idx;
629       closedist = tmp;
630     }
631   }
632   return closeidx;
633 }
634 
635 
636 /*!
637   WARNING: Please don't use this function. It can cause hard to find
638   bugs on the Windows platform if your application is linked against a
639   different CRT than your Coin DLL.
640 
641   Use int findClosest(const SbSphere &sphere, SbIntList & arr)
642   instead.
643 */
644 int
findClosest(const SbSphere & sphere,SbList<int> & arr) const645 SbBSPTree::findClosest(const SbSphere &sphere,
646                        SbList <int> &arr) const
647 {
648   this->findPoints(sphere, arr);
649   SbVec3f pos = sphere.getCenter();
650   int n = arr.getLength();
651   int closeidx = -1;
652   float closedist = FLT_MAX;
653   for (int i = 0; i < n; i++) {
654     int idx = arr[i];
655     float tmp = (pos-this->pointsArray[idx]).sqrLength();
656     if (tmp < closedist) {
657       closeidx = idx;
658       closedist = tmp;
659     }
660   }
661   return closeidx;
662 }
663 
664 /*!
665   WARNING: Please don't use this function. It can cause hard to find
666   bugs on the Windows platform if your application is linked against a
667   different CRT than your Coin DLL.
668 
669   Use void findPoints(const SbSphere &sphere, SbIntList & array)
670   instead.
671 */
672 void
findPoints(const SbSphere & sphere,SbList<int> & array) const673 SbBSPTree::findPoints(const SbSphere &sphere,
674                       SbList <int> &array) const
675 {
676   this->topnode->findPoints(sphere, array);
677 }
678 
679 #ifdef COIN_TEST_SUITE
680 
BOOST_AUTO_TEST_CASE(initialized)681 BOOST_AUTO_TEST_CASE(initialized)
682 {
683   SbBSPTree bsp;
684   SbVec3f p0(0.0f, 0.0f, 0.0f);
685   SbVec3f p1(1.0f, 0.0f, 0.0f);
686   SbVec3f p2(2.0f, 0.0f, 0.0f);
687   void * userdata0 = reinterpret_cast<void*> (&p0);
688   void * userdata1 = reinterpret_cast<void*> (&p1);
689   void * userdata2 = reinterpret_cast<void*> (&p2);
690 
691   BOOST_CHECK_MESSAGE(bsp.addPoint(p0, userdata0) == 0, "unexpected index");
692   BOOST_CHECK_MESSAGE(bsp.addPoint(p1, userdata1) == 1, "unexpected index");
693   BOOST_CHECK_MESSAGE(bsp.addPoint(p2, userdata2) == 2, "unexpected index");
694   BOOST_CHECK_MESSAGE(bsp.addPoint(p2, userdata2) == 2, "unexpected index");
695   BOOST_CHECK_MESSAGE(bsp.numPoints() == 3, "wrong number of points in the tree");
696 
697   BOOST_CHECK_MESSAGE(bsp.findPoint(p0) == 0, "wrong index");
698   BOOST_CHECK_MESSAGE(bsp.getUserData(0) == userdata0, "wrong userdata");
699   BOOST_CHECK_MESSAGE(bsp.findPoint(p1) == 1, "wrong index");
700   BOOST_CHECK_MESSAGE(bsp.getUserData(1) == userdata1, "wrong userdata");
701   BOOST_CHECK_MESSAGE(bsp.findPoint(p2) == 2, "wrong index");
702   BOOST_CHECK_MESSAGE(bsp.getUserData(2) == userdata2, "wrong userdata");
703 
704   BOOST_CHECK_MESSAGE(bsp.numPoints() == 3, "wrong number of points in the tree");
705   BOOST_CHECK_MESSAGE(bsp.getPointsArrayPtr()[0] == p0, "wrong point at index 0");
706   BOOST_CHECK_MESSAGE(bsp.getPointsArrayPtr()[1] == p1, "wrong point at index 1");
707   BOOST_CHECK_MESSAGE(bsp.getPointsArrayPtr()[2] == p2, "wrong point at index 2");
708 
709   BOOST_CHECK_MESSAGE(bsp.removePoint(p1) == 1, "unable to remove point");
710   BOOST_CHECK_MESSAGE(bsp.numPoints() == 2, "wrong number of points after removePoint().");
711   BOOST_CHECK_MESSAGE(bsp.getPointsArrayPtr()[0] == p0, "wrong point at index 0");
712   BOOST_CHECK_MESSAGE(bsp.getUserData(0) == userdata0, "wrong userdata");
713   BOOST_CHECK_MESSAGE(bsp.getPointsArrayPtr()[1] == p2, "wrong point at index 1");
714   BOOST_CHECK_MESSAGE(bsp.getUserData(1) == userdata2, "wrong userdata");
715 
716   BOOST_CHECK_MESSAGE(bsp.removePoint(p0) >= 0, "unable to remove point");
717   BOOST_CHECK_MESSAGE(bsp.removePoint(p2) >= 0, "unable to remove point");
718   BOOST_CHECK_MESSAGE(bsp.numPoints() == 0, "wrong number of points after removing all points.");
719 
720 }
721 
722 #endif // COIN_TEST_SUITE
723