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