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 /*!
34 \class SbXfBox3f SbXfBox3f.h Inventor/SbXfBox3f.h
35 \brief The SbXfBox3f class is a 3 dimensional box with floating point coordinates and an attached transformation.
36
37 \ingroup base
38
39 This box class is used by many other classes in Coin
40 for data exchange. It provides storage for two box corners with
41 floating point coordinates, and for a floating point 4x4 transformation
42 matrix.
43
44 \sa SbBox2s, SbBox2f, SbBox2d, SbBox3s, SbBox3f, SbBox3d, SbMatrix.
45 */
46
47 #include <Inventor/SbXfBox3f.h>
48 #include <cfloat>
49 #include <Inventor/errors/SoDebugError.h>
50
51 // this value is used to signal an invalid inverse matrix
52 #define INVALID_TAG FLT_MAX
53
54 static SbVec3f
SbXfBox3f_get_scaled_span_vec(const SbXfBox3f & xfbox)55 SbXfBox3f_get_scaled_span_vec(const SbXfBox3f & xfbox)
56 {
57 const SbMatrix & m = xfbox.getTransform();
58
59 // FIXME: is this really correct? Won't we get the wrong result if
60 // there are rotations in the transformation matrix? 20020209 mortene.
61 float scalex = static_cast<float>(sqrt(m[0][0] * m[0][0] +
62 m[1][0] * m[1][0] +
63 m[2][0] * m[2][0]));
64 float scaley = static_cast<float>(sqrt(m[0][1] * m[0][1] +
65 m[1][1] * m[1][1] +
66 m[2][1] * m[2][1]));
67 float scalez = static_cast<float>(sqrt(m[0][2] * m[0][2] +
68 m[1][2] * m[1][2] +
69 m[2][2] * m[2][2]));
70
71 SbVec3f min, max;
72 xfbox.getBounds(min, max);
73
74 return SbVec3f((max[0] - min[0]) * scalex,
75 (max[1] - min[1]) * scaley,
76 (max[2] - min[2]) * scalez);
77 }
78
79
80 /*!
81 The default constructor makes an empty box and identity matrix.
82 */
SbXfBox3f(void)83 SbXfBox3f::SbXfBox3f(void)
84 {
85 this->matrix.makeIdentity();
86 this->invertedmatrix.makeIdentity();
87 }
88
89 /*!
90 Constructs a box with the given corners.
91
92 The coordinates of \a min should be less than the coordinates of
93 \a max if you want to make a valid box.
94 */
SbXfBox3f(const SbVec3f & boxmin,const SbVec3f & boxmax)95 SbXfBox3f::SbXfBox3f(const SbVec3f & boxmin, const SbVec3f & boxmax):
96 SbBox3f(boxmin, boxmax)
97 {
98 this->matrix.makeIdentity();
99 this->invertedmatrix.makeIdentity();
100 }
101
102 /*!
103 Constructs a box from the given SbBox3f.
104
105 The transformation is set to the identity matrix.
106 */
SbXfBox3f(const SbBox3f & box)107 SbXfBox3f::SbXfBox3f(const SbBox3f & box):
108 SbBox3f(box)
109 {
110 this->matrix.makeIdentity();
111 this->invertedmatrix.makeIdentity();
112 }
113
114 /*!
115 Default destructor does nothing.
116 */
~SbXfBox3f()117 SbXfBox3f::~SbXfBox3f()
118 {
119 }
120
121 /*!
122 Overridden from SbBox3f, as the transformations are to be kept
123 separate from the box in the SbXfBox3f class.
124 */
125 void
transform(const SbMatrix & m)126 SbXfBox3f::transform(const SbMatrix & m)
127 {
128 this->setTransform(this->matrix.multRight(m));
129 }
130
131 /*!
132 Sets the transformation to the given SbMatrix.
133 */
134 void
setTransform(const SbMatrix & m)135 SbXfBox3f::setTransform(const SbMatrix & m)
136 {
137 this->matrix = m;
138 this->makeInvInvalid(); // invalidate current inverse
139 }
140
141 /*!
142 Returns the current transformation matrix.
143 */
144 const SbMatrix &
getTransform(void) const145 SbXfBox3f::getTransform(void) const
146 {
147 return this->matrix;
148 }
149
150 /*!
151 Returns the inverse of the current transformation matrix.
152 */
153 const SbMatrix &
getInverse(void) const154 SbXfBox3f::getInverse(void) const
155 {
156 this->calcInverse();
157 return this->invertedmatrix;
158 }
159
160 /*!
161 Return the transformed center point of the box.
162 */
163 SbVec3f
getCenter(void) const164 SbXfBox3f::getCenter(void) const
165 {
166 SbVec3f orgcenter = SbBox3f::getCenter();
167 SbVec3f transcenter;
168 this->matrix.multVecMatrix(orgcenter,transcenter);
169 return transcenter;
170 }
171
172 /*!
173 Extend the boundaries of the box by the given point, i.e. make the
174 point fit inside the box if it isn't already so.
175
176 The point is assumed to be in transformed space.
177 */
178 void
extendBy(const SbVec3f & pt)179 SbXfBox3f::extendBy(const SbVec3f & pt)
180 {
181 if (this->isEmpty()) {
182 this->matrix.makeIdentity();
183 this->invertedmatrix.makeIdentity();
184 }
185
186 const SbMatrix & im = this->getInverse();
187 SbVec3f trans;
188 im.multVecMatrix(pt, trans);
189 SbBox3f::extendBy(trans);
190 }
191
192 /*!
193 Extend the boundaries of the box by the given \a bb parameter.
194 The given box is assumed to be in transformed space.
195
196 The two given boxes will be combined in such a way so that the resultant
197 bounding box always has the smallest possible volume. To accomplish this,
198 the transformation on this SbXfBox3f will sometimes be flattened before
199 it's combined with \a bb.
200 */
201 void
extendBy(const SbBox3f & bb)202 SbXfBox3f::extendBy(const SbBox3f & bb)
203 {
204 #if COIN_DEBUG
205 if (bb.isEmpty()) {
206 SoDebugError::postWarning("SbXfBox3f::extendBy",
207 "Extending box is empty.");
208 return;
209 }
210 #endif // COIN_DEBUG
211
212 if (this->isEmpty()) {
213 *this = bb;
214 this->matrix.makeIdentity();
215 this->invertedmatrix.makeIdentity();
216 return;
217 }
218
219 SbVec3f points[2] = { bb.getMin(), bb.getMax() };
220
221 // Combine bboxes while keeping the transformation matrix.
222 SbBox3f box1 = *this;
223 {
224 SbMatrix im = this->getInverse();
225 // Transform all the corners and include them into the new box.
226 for (int i=0; i < 8; i++) {
227 SbVec3f corner, dst;
228 // Find all corners the "binary" way :-)
229 corner.setValue(points[(i&4)>>2][0],
230 points[(i&2)>>1][1],
231 points[i&1][2]);
232 // Don't try to optimize the transformation out of the loop,
233 // it's not as easy as it seems.
234 im.multVecMatrix(corner, dst);
235 #if 0 // debug
236 SoDebugError::postInfo("SbXfBox3f::extendBy",
237 "point: <%f, %f, %f> -> <%f, %f, %f>",
238 corner[0], corner[1], corner[2],
239 dst[0], dst[1], dst[2]);
240 #endif // debug
241 box1.extendBy(dst);
242 }
243 }
244
245
246 // Combine bboxes with a flattened transformation matrix.
247 SbBox3f box2 = this->project();
248 {
249 for (int j=0;j<8;j++) {
250 SbVec3f corner;
251 corner.setValue(points[(j&4)>>2][0],
252 points[(j&2)>>1][1],
253 points[j&1][2]);
254 box2.extendBy(corner);
255 }
256 }
257
258 SbXfBox3f xfbox(box1);
259 xfbox.setTransform(this->matrix);
260 #if 0 // debug
261 SoDebugError::postInfo("SbXfBox3f::extendBy",
262 "kintel-volume: %f, mortene-volume: %f",
263 xfbox.getVolume(), box2.getVolume());
264 #endif // debug
265
266 // Choose result from one of the two techniques based on the volume
267 // of the resultant bbox.
268 SbBool firstsmaller;
269 float vol1 = xfbox.getVolume(), vol2 = box2.getVolume();
270 if ((vol1 != 0.0f) || (vol2 != 0.0f)) {
271 firstsmaller = (vol1 < vol2);
272 }
273 // If one dimension has zero span, we need to compare area (or
274 // length, if two dimensions have zero span).
275 else {
276 SbVec3f s1 = SbXfBox3f_get_scaled_span_vec(xfbox);
277 SbVec3f s2 = SbXfBox3f_get_scaled_span_vec(box2);
278
279 float v1 = static_cast<float>(fabs((s1[0] != 0.0f ? s1[0] : 1.0f) *
280 (s1[1] != 0.0f ? s1[1] : 1.0f) *
281 (s1[2] != 0.0f ? s1[2] : 1.0f)));
282 float v2 = static_cast<float>(fabs((s2[0] != 0.0f ? s2[0] : 1.0f) *
283 (s2[1] != 0.0f ? s2[1] : 1.0f) *
284 (s2[2] != 0.0f ? s2[2] : 1.0f)));
285
286 firstsmaller = (v1 < v2);
287 }
288
289 if (firstsmaller) {
290 this->setBounds(box1.getMin(), box1.getMax());
291 }
292 else {
293 this->setBounds(box2.getMin(), box2.getMax());
294 this->matrix.makeIdentity();
295 this->invertedmatrix.makeIdentity();
296 }
297 }
298
299 /*!
300 Extend the boundaries of the box by the given \a bb parameter.
301
302 The given box is assumed to be in transformed space.
303
304 Note: is not guaranteed to give an optimal result if used for bbox
305 calculation since the transformation matrix might change. See
306 documentation in SoGetBoundingBoxAction for more details.
307 */
308 void
extendBy(const SbXfBox3f & bb)309 SbXfBox3f::extendBy(const SbXfBox3f & bb)
310 {
311 #if COIN_DEBUG
312 if (bb.isEmpty()) {
313 SoDebugError::postWarning("SbXfBox3f::extendBy",
314 "Extending box is empty.");
315 return;
316 }
317 #endif // COIN_DEBUG
318
319 if (this->isEmpty()) {
320 *this = bb;
321 return;
322 }
323
324 #if 0 // debug
325 SoDebugError::postInfo("SbXfBox3f::extendBy",
326 "bb: <%f, %f, %f>, <%f, %f, %f>",
327 bb.getMin()[0],
328 bb.getMin()[1],
329 bb.getMin()[2],
330 bb.getMax()[0],
331 bb.getMax()[1],
332 bb.getMax()[2]);
333 #endif // debug
334
335 // Try extending while keeping the transform on "this" first.
336 SbXfBox3f box1 = *this;
337 {
338 SbVec3f points[2] = { bb.getMin(), bb.getMax() };
339 {
340 SbMatrix m = bb.getTransform();
341 m.multRight(box1.getInverse());
342
343 for (int i=0; i < 8; i++) {
344 SbVec3f corner, dst;
345 corner.setValue(points[(i&4)>>2][0],
346 points[(i&2)>>1][1],
347 points[i&1][2]);
348 m.multVecMatrix(corner, dst);
349 #if 0 // debug
350 SoDebugError::postInfo("SbXfBox3f::extendBy",
351 "corner: <%f, %f, %f>, dst <%f, %f, %f>",
352 corner[0], corner[1], corner[2],
353 dst[0], dst[1], dst[2]);
354 #endif // debug
355 static_cast<SbBox3f *>(&box1)->extendBy(dst);
356 #if 0 // debug
357 SoDebugError::postInfo("SbXfBox3f::extendBy",
358 "dst: <%f, %f, %f> -> "
359 "box1: <%f, %f, %f>, <%f, %f, %f>",
360 dst[0], dst[1], dst[2],
361 box1.getMin()[0],
362 box1.getMin()[1],
363 box1.getMin()[2],
364 box1.getMax()[0],
365 box1.getMax()[1],
366 box1.getMax()[2]);
367 #endif // debug
368 }
369 }
370 }
371
372 // Try extending while keeping the transform on bb.
373 SbXfBox3f box2 = bb;
374 {
375 SbVec3f points[2] = { this->getMin(), this->getMax() };
376 {
377 SbMatrix m = this->getTransform();
378 m.multRight(box2.getInverse());
379
380 for (int i=0; i < 8; i++) {
381 SbVec3f corner, dst;
382 corner.setValue(points[(i&4)>>2][0],
383 points[(i&2)>>1][1],
384 points[i&1][2]);
385 m.multVecMatrix(corner, dst);
386 #if 0 // debug
387 SoDebugError::postInfo("SbXfBox3f::extendBy",
388 "corner: <%f, %f, %f>, dst <%f, %f, %f>",
389 corner[0], corner[1], corner[2],
390 dst[0], dst[1], dst[2]);
391 #endif // debug
392 static_cast<SbBox3f *>(&box2)->extendBy(dst);
393 #if 0 // debug
394 SoDebugError::postInfo("SbXfBox3f::extendBy",
395 "dst: <%f, %f, %f> -> "
396 "box2: <%f, %f, %f>, <%f, %f, %f>",
397 dst[0], dst[1], dst[2],
398 box2.getMin()[0],
399 box2.getMin()[1],
400 box2.getMin()[2],
401 box2.getMax()[0],
402 box2.getMax()[1],
403 box2.getMax()[2]);
404 #endif // debug
405 }
406 }
407 }
408
409 #if 0 // debug
410 SoDebugError::postInfo("SbXfBox3f::extendBy",
411 "box1-volume: %f, box2-volume: %f",
412 box1.getVolume(), box2.getVolume());
413 #endif // debug
414
415 // Compare volumes and pick the smallest bounding box.
416 SbBool firstsmaller;
417 float vol1 = box1.getVolume(), vol2 = box2.getVolume();
418 if ((vol1 != 0.0f) || (vol2 != 0.0f)) {
419 firstsmaller = (vol1 < vol2);
420 }
421 // If one dimension has zero span, we need to compare area (or
422 // length, if two dimensions have zero span).
423 else {
424 SbVec3f s1 = SbXfBox3f_get_scaled_span_vec(box1);
425 SbVec3f s2 = SbXfBox3f_get_scaled_span_vec(box2);
426
427 float v1 = static_cast<float>(fabs((s1[0] != 0.0f ? s1[0] : 1.0f) *
428 (s1[1] != 0.0f ? s1[1] : 1.0f) *
429 (s1[2] != 0.0f ? s1[2] : 1.0f)));
430 float v2 = static_cast<float>(fabs((s2[0] != 0.0f ? s2[0] : 1.0f) *
431 (s2[1] != 0.0f ? s2[1] : 1.0f) *
432 (s2[2] != 0.0f ? s2[2] : 1.0f)));
433
434 firstsmaller = (v1 < v2);
435 }
436 *this = (firstsmaller ? box1 : box2);
437 }
438
439 /*!
440 Check if the given point lies within the boundaries of this box.
441
442 The point is assumed to be in transformed space.
443 */
444 SbBool
intersect(const SbVec3f & pt) const445 SbXfBox3f::intersect(const SbVec3f & pt) const
446 {
447 this->calcInverse();
448 SbVec3f trans;
449 this->invertedmatrix.multVecMatrix(pt, trans);
450 return SbBox3f::intersect(trans);
451 }
452
453 //
454 // tests for intersection between an axis aligned box and the
455 // 12 edges defined by the 8 points in the 'points' array.
456 //
457 static
intersect_box_edges(const SbVec3f & min,const SbVec3f & max,const SbVec3f * const points)458 SbBool intersect_box_edges(const SbVec3f & min,
459 const SbVec3f & max,
460 const SbVec3f * const points)
461 {
462 // lookup table for edges in the cube.
463 static int lines[12*2] =
464 {
465 0,1,
466 0,2,
467 0,4,
468 1,3,
469 1,5,
470 2,3,
471 2,6,
472 3,7,
473 4,5,
474 4,6,
475 5,7,
476 6,7
477 };
478
479 // need this for the innermost loop
480 SbVec3f boxpts[2];
481 boxpts[0] = min;
482 boxpts[1] = max;
483
484 // test for edge intersection
485 for (int i = 0; i < 12; i++) { // 12 edges in a cube
486 SbVec3f l1 = points[lines[i*2]];
487 SbVec3f l2 = points[lines[i*2+1]];
488 // possible optimization: reuse directional vectors
489 SbVec3f dir = l2 - l1;
490 // if the direction is a nil-vector, this means that the bounding
491 // box is flat (2D or 1D) or empty and we can just skip this vector.
492 if (dir.normalize() == 0.0f) continue;
493 SbVec3f lmin(SbMin(l1[0], l2[0]),
494 SbMin(l1[1], l2[1]),
495 SbMin(l1[2], l2[2]));
496 SbVec3f lmax(SbMax(l1[0], l2[0]),
497 SbMax(l1[1], l2[1]),
498 SbMax(l1[2], l2[2]));
499
500 // the bbox to test against is axis-aligned, and this makes it
501 // quite simple.
502 for (int j = 0; j < 3; j++) { // test planes in all three dimensions
503 for (int k = 0; k < 2; k++) { // test both min and max planes
504 // check if line crosses current plane
505 if (dir[j] != 0.0f &&
506 lmin[j] <= boxpts[k][j] && lmax[j] >= boxpts[k][j]) {
507 // find the two other coordinates
508 int t1 = j+1;
509 int t2 = j+2;
510 // do this instead of modulo 3
511 if (t1 >= 3) t1 -= 3;
512 if (t2 >= 3) t2 -= 3;
513
514 // find what we need to multiply coordinate j by to
515 // put it onto the current plane
516 float delta = static_cast<float>(fabs((boxpts[k][j] - l1[j]) / dir[j]));
517 // calculate the two other coordinates
518 float v1 = l1[t1] + delta*dir[t1];
519 float v2 = l1[t2] + delta*dir[t2];
520 if (v1 > boxpts[0][t1] && v1 < boxpts[1][t1] &&
521 v2 > boxpts[0][t2] && v2 < boxpts[1][t2]) {
522 return TRUE;
523 }
524 }
525 }
526 }
527 }
528 return FALSE;
529 }
530
531 //
532 // weak box-box intersection test: min, max defines an axis-aligned
533 // box, while boxmin, boxmax defines an box that should be transformed
534 // by matrix. This function only tests whether any of the 8
535 // (transformed) points in (boxmin, boxmax) is inside (min, max),
536 // and if any of the 12 edges in (boxmin, boxmax) intersects any of the
537 // planes in the box defined by (min, max).
538 //
539 // Use this function twice to cover all intersection cases.
540 //
541 static SbBool
intersect_box_box(const SbVec3f & min,const SbVec3f & max,const SbVec3f & boxmin,const SbVec3f & boxmax,const SbMatrix & matrix,SbBool & alignedIntersect)542 intersect_box_box(const SbVec3f & min,
543 const SbVec3f & max,
544 const SbVec3f & boxmin,
545 const SbVec3f & boxmax,
546 const SbMatrix & matrix,
547 SbBool & alignedIntersect)
548 {
549 SbVec3f transpoints[8];
550 SbBox3f alignedBox;
551 for (int i = 0; i < 8; i++) {
552 SbVec3f tmp, tmp2;
553 tmp.setValue((i&4) ? boxmin[0] : boxmax[0],
554 (i&2) ? boxmin[1] : boxmax[1],
555 (i&1) ? boxmin[2] : boxmax[2]);
556 matrix.multVecMatrix(tmp, tmp2);
557 // is point inside
558 if (tmp2[0] >= min[0] &&
559 tmp2[0] <= max[0] &&
560 tmp2[1] >= min[1] &&
561 tmp2[1] <= max[1] &&
562 tmp2[2] >= min[2] &&
563 tmp2[2] <= max[2]) {
564 return TRUE;
565 }
566 alignedBox.extendBy(tmp2);
567 transpoints[i] = tmp2;
568 }
569 // this is just an optimization:
570 // if the axis aligned box doesn't intersect the box, there
571 // is no chance for any intersection.
572 SbBox3f thisbox(min, max);
573 alignedIntersect = thisbox.intersect(alignedBox);
574
575 // only test edge intersection if aligned boxes intersect
576 if (alignedIntersect)
577 return intersect_box_edges(min, max, transpoints);
578 return FALSE;
579 }
580
581 /*!
582 Check if the given \a box lies wholly or partly within the boundaries
583 of this box.
584
585 The given box is assumed to be in transformed space.
586 */
587 SbBool
intersect(const SbBox3f & bb) const588 SbXfBox3f::intersect(const SbBox3f & bb) const
589 {
590 if (this->isEmpty() || bb.isEmpty()) {
591 #if COIN_DEBUG
592 SoDebugError::postWarning("SbXfBox3f::intersect",
593 "%s is an empty / uninitialized box",
594 this->isEmpty() ? "this" : "input argument");
595 #endif // COIN_DEBUG
596 return FALSE;
597 }
598
599 if (this->matrix == SbMatrix::identity()) return SbBox3f::intersect(bb);
600
601 //
602 // do double-test to get all intersection cases
603 //
604 SbBool alignedIntersect;
605
606 if (intersect_box_box(bb.getMin(), bb.getMax(),
607 this->getMin(), this->getMax(),
608 this->matrix, alignedIntersect)) return TRUE;
609
610 if (!alignedIntersect) return FALSE;
611
612 // will need the inverse matrix here
613 this->calcInverse();
614 return intersect_box_box(this->getMin(), this->getMax(),
615 bb.getMin(), bb.getMax(),
616 this->invertedmatrix,
617 alignedIntersect);
618 }
619
620 /*!
621 Check if two transformed boxes intersect.
622
623 \COIN_FUNCTION_EXTENSION
624
625 \since Coin 2.0
626 */
627
628 SbBool
intersect(const SbXfBox3f & xfbb) const629 SbXfBox3f::intersect(const SbXfBox3f & xfbb) const
630 {
631 const SbBox3f & bbr = xfbb;
632 SbBox3f bb(bbr);
633 SbXfBox3f me(*this);
634 me.transform(xfbb.getInverse());
635 return me.intersect(bb);
636 }
637
638
639 /*!
640 Find the span of the box in the given direction (i.e. how much room
641 in the given direction the box needs). The distance is returned as
642 the minimum and maximum distance from origo to the closest and
643 furthest plane defined by the direction vector and each of the box'
644 corners. The difference between these values gives the span.
645 */
646 void
getSpan(const SbVec3f & direction,float & dMin,float & dMax) const647 SbXfBox3f::getSpan(const SbVec3f & direction, float & dMin, float & dMax) const
648 {
649 this->project().getSpan(direction, dMin, dMax);
650 }
651
652 /*!
653 Project the SbXfBox3f into a SbBox3f.
654
655 This gives the same resulting SbBox3f as doing a SbBox3f::transform()
656 with this transformation matrix as parameter.
657 */
658 SbBox3f
project(void) const659 SbXfBox3f::project(void) const
660 {
661 SbBox3f box(this->getMin(), this->getMax());
662 if (!box.isEmpty()) box.transform(this->matrix);
663 return box;
664 }
665
666 /*!
667 Check if \a b1 and \a b2 are equal. Return 1 if they are equal,
668 or 0 if they are unequal. Note that the method will do a dumb
669 component by component comparison.
670 */
671 int
operator ==(const SbXfBox3f & b1,const SbXfBox3f & b2)672 operator ==(const SbXfBox3f & b1, const SbXfBox3f & b2)
673 {
674 return
675 (b1.getMin() == b2.getMin()) &&
676 (b1.getMax() == b2.getMax()) &&
677 (b1.matrix == b2.matrix);
678 }
679
680 /*!
681 Check if \a b1 and \a b2 are unequal. Return 0 if they are equal,
682 or 1 if they are unequal. See the note on operator==().
683 */
684 int
operator !=(const SbXfBox3f & b1,const SbXfBox3f & b2)685 operator !=(const SbXfBox3f & b1, const SbXfBox3f & b2)
686 {
687 return !(b1 == b2);
688 }
689
690 /*!
691 Return box volume. Overridden from parent class to take into account
692 the possibility of scaling in the transformation matrix.
693 */
694 float
getVolume(void) const695 SbXfBox3f::getVolume(void) const
696 {
697 if (!this->hasVolume()) return 0.0f;
698
699 // The determinant of the upper-left 3x3 matrix can be used to
700 // calculate the volume of the transformed box.
701 //
702 // By Doctor Tom at the Math Forum:
703 // ----------------------------------------------------------------
704 // <URL:http://mathforum.org/dr.math/problems/carlino11.16.97.html>
705 //
706 // Date: 11/17/97 at 19:57:10
707 // From: Doctor Tom
708 // Subject: Re:Explaining the determinant
709 //
710 // Hello Jeremy,
711 //
712 // I always think of it geometrically. Let's look in
713 // two dimensions, at the determinant of the following:
714 //
715 // | x0 y0 | = x0*y1 - x1*y0
716 // | x1 y1 |
717 //
718 // Now imagine the two vectors (x0, y0) and (x1, y1) drawn in the
719 // x-y plane from the origin. If you consider them to be two sides
720 // of a parallelogram, then the determinant is the area of the
721 // parallelogram. Well, not exactly the area, the "signed" area,
722 // in the sense that if you sweep the area clockwise, you get one
723 // sign, and the opposite sign if you sweep it in the other
724 // direction. It's just as useful a concept as considering area
725 // below the x-axis as negative in your calculus course. Swapping
726 // the vectors swaps the sign, in the same way that swapping the
727 // rows of the determinant swaps the sign. In one dimension, the
728 // determinant is just the number, but if you "plot" that number on
729 // a number line, it's the (signed) length of the line. If it goes
730 // in the positive direction from the origin, it's positive, and
731 // negative otherwise. In three dimensions, consider three vectors
732 // (x0,y0,z0), (x1,y1,z1), and (x2,y2,z2). If you draw them from
733 // the origin, they form the principle edges of a parallelepiped,
734 // and the determinant of:
735 //
736 // | x0 y0 z0 |
737 // | x1 y1 z1 |
738 // | x2 y2 z2 |
739 //
740 // is the volume of that parallelepiped.
741 // --------------------------------------------------------------
742 //
743 // this means that the determinant is the volume of a unit size cube
744 // in the coordinate system specified by a 3x3 matrix, and that we
745 // can find the volume of our box by multiplying the volume of the
746 // orthogonal box with the determinant of the upper-left 3x3 matrix.
747
748 float volume = (SbBox3f::getVolume() * this->matrix.det3());
749
750 // The determinant might be negative if e.g. negative scaling has
751 // been performed on the matrix. To rectify this, we make sure the
752 // returned volume is positive.
753 return (volume > 0) ? volume : -volume;
754 }
755
756 /*!
757 Dump the state of this object to the \a file stream. Only works in
758 debug version of library, method does nothing in an optimized compile.
759 */
760 void
print(FILE * fp) const761 SbXfBox3f::print(FILE * fp) const
762 {
763 #if COIN_DEBUG
764 SbVec3f minv, maxv;
765 this->getBounds(minv, maxv);
766 fprintf( fp, " bounds " );
767 minv.print(fp);
768 fprintf( fp, " " );
769 maxv.print(fp);
770 fprintf( fp, "\n" );
771
772 fprintf( fp, " center " );
773 this->getCenter().print(fp);
774 fprintf( fp, "\n" );
775 float x, y, z;
776 this->getOrigin(x, y, z);
777 fprintf( fp, " origin " );
778 SbVec3f(x, y, z).print(fp);
779 fprintf( fp, "\n" );
780 this->getSize(x, y, z);
781 fprintf( fp, " size " );
782 SbVec3f(x, y, z).print(fp);
783
784 fprintf( fp, "\n" );
785 fprintf( fp, " volume %f\n", this->getVolume() );
786 this->getTransform().print(fp);
787
788 fprintf( fp, " project " );
789 this->project().print(fp);
790 fprintf( fp, "\n" );
791 #endif // COIN_DEBUG
792 }
793
794 void
calcInverse(void) const795 SbXfBox3f::calcInverse(void) const
796 {
797 // det4() is checked against VALID_LIMIT to determine if the inverse
798 // matrix can be calculated.
799 const float VALID_LIMIT = 1.0e-12f;
800
801 if (this->invertedmatrix[0][0] == INVALID_TAG) {
802 if (SbAbs(this->matrix.det4()) > VALID_LIMIT) {
803 const_cast<SbXfBox3f *>(this)->invertedmatrix = this->matrix.inverse();
804 }
805 else {
806 #if COIN_DEBUG && 0 // disabled
807 const SbMatrix & m = this->matrix;
808 SoDebugError::postWarning("SbXfBox3f::setTransform",
809 "invalid matrix (can't be inverted)");
810 SoDebugError::postWarning("SbXfBox3f::setTransform",
811 "%f %f %f %f",
812 m[0][0], m[0][1], m[0][2], m[0][3]);
813 SoDebugError::postWarning("SbXfBox3f::setTransform",
814 "%f %f %f %f",
815 m[1][0], m[1][1], m[1][2], m[1][3]);
816 SoDebugError::postWarning("SbXfBox3f::setTransform",
817 "%f %f %f %f",
818 m[2][0], m[2][1], m[2][2], m[2][3]);
819 SoDebugError::postWarning("SbXfBox3f::setTransform",
820 "%f %f %f %f",
821 m[3][0], m[3][1], m[3][2], m[3][3]);
822 #endif // COIN_DEBUG
823
824 // Degenerate transforms are fixed by projecting box. This will
825 // transform the min and max points (using the normal matrix,
826 // not the inverse), and leave us with an identity transform.
827 SbXfBox3f * thisp = const_cast<SbXfBox3f *>(this); // cast away constness
828 *thisp = SbXfBox3f(this->project());
829
830 // FIXME: this degenerate-transform fix looks like bad
831 // engineering. It's the caller who does something wrong when
832 // combining transforms into SbXfBox3f to make a non-inversible
833 // matrix. This will for instance happen when calculating bboxes
834 // for a scene with scale transforms with 0 components.
835 // 20010627 mortene.
836 }
837 }
838 }
839
840 void
makeInvInvalid(void)841 SbXfBox3f::makeInvInvalid(void)
842 {
843 this->invertedmatrix[0][0] = INVALID_TAG;
844 }
845
846 #undef INVALID_TAG
847