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