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 SbBox2d Inventor/SbBox2d.h
35   \brief The SbBox2d class is a 2 dimensional box with double precision
36   corner coordinates.
37 
38   \ingroup base
39 
40   This box class is used by many other classes in Coin for data
41   exchange and storage. It provides two box corners with double
42   precision coordinates, which is among other things useful for
43   representing screen or canvas dimensions in normalized coordinates.
44 
45   This class is a Coin extension.
46 
47   \sa SbBox2s, SbBox2f, SbBox3s, SbBox3f, SbBox3d, SbXfBox3f.
48 
49   \since Coin 2.0
50   \since TGS Inventor 2.6
51 */
52 
53 #include <Inventor/SbBox2d.h>
54 
55 #include <limits>
56 
57 #include <Inventor/SbBox2f.h>
58 #include <Inventor/SbBox2s.h>
59 #include <Inventor/SbBox2i32.h>
60 #if COIN_DEBUG
61 #include <Inventor/errors/SoDebugError.h>
62 #endif // COIN_DEBUG
63 
64 /*!
65   \fn SbBox2d::SbBox2d(void)
66 
67   The default constructor makes an empty box.
68 */
69 
70 /*!
71   \fn SbBox2d::SbBox2d(double xmin, double ymin, double xmax, double ymax)
72 
73   Constructs a box with the given corners.
74 
75   \a xmin should be less than \a xmax and \a ymin should be less than
76   \a ymax if you want to make a valid box.
77 */
78 
79 /*!
80   \fn SbBox2d::SbBox2d(const SbVec2d & min, const SbVec2d & max)
81 
82   Constructs a box with the given lower left and upper right corners.
83 
84   The coordinates of \a min should be less than the coordinates of
85   \a max if you want to make a valid box.
86 */
87 
88 /*!
89   \fn SbBox2d & SbBox2d::setBounds(double xmin, double ymin, double xmax, double ymax)
90 
91   Reset the boundaries of the box.
92 
93   \a xmin should be less than \a xmax and \a ymin should be less than
94   \a ymax if you want to make a valid box.
95 
96   Returns reference to self.
97 
98   \sa getBounds().
99 */
100 
101 /*!
102   \fn SbBox2d & SbBox2d::setBounds(const SbVec2d & min, const SbVec2d & max)
103 
104   Reset the boundaries of the box with the given corners.
105 
106   The coordinates of \a min should be less than the coordinates of
107   \a max if you want to make a valid box.
108 
109   Returns reference to self.
110 
111   \sa getBounds().
112 */
113 
114 /*!
115   Reset the boundaries of the box with the given \a box boundaries.
116 
117   Returns reference to self.
118 
119   \sa getBounds()
120 */
121 SbBox2d &
setBounds(const SbBox2f & box)122 SbBox2d::setBounds(const SbBox2f & box)
123 {
124   if (box.isEmpty()) {
125     makeEmpty();
126   } else {
127     minpt.setValue(box.getMin());
128     maxpt.setValue(box.getMax());
129   }
130   return *this;
131 }
132 
133 /*!
134   Reset the boundaries of the box with the given \a box boundaries.
135 
136   Returns reference to self.
137 
138   \sa getBounds()
139 */
140 SbBox2d &
setBounds(const SbBox2s & box)141 SbBox2d::setBounds(const SbBox2s & box)
142 {
143   if (box.isEmpty()) {
144     makeEmpty();
145   } else {
146     minpt.setValue(box.getMin());
147     maxpt.setValue(box.getMax());
148   }
149   return *this;
150 }
151 
152 /*!
153   Reset the boundaries of the box with the given \a box boundaries.
154 
155   Returns reference to self.
156 
157   \sa getBounds()
158 */
159 SbBox2d &
setBounds(const SbBox2i32 & box)160 SbBox2d::setBounds(const SbBox2i32 & box)
161 {
162   if (box.isEmpty()) {
163     makeEmpty();
164   } else {
165     minpt.setValue(box.getMin());
166     maxpt.setValue(box.getMax());
167   }
168   return *this;
169 }
170 
171 /*!
172   Marks this as an empty box.
173 
174   \sa isEmpty().
175 */
176 void
makeEmpty(void)177 SbBox2d::makeEmpty(void)
178 {
179   minpt.setValue(std::numeric_limits<double>::max(), std::numeric_limits<double>::max());
180   maxpt.setValue(-std::numeric_limits<double>::max(), -std::numeric_limits<double>::max());
181 }
182 
183 /*!
184   \fn SbBool SbBox2d::isEmpty(void) const
185 
186   Check if this has been marked as an empty box.
187 
188   \sa makeEmpty().
189 */
190 
191 /*!
192   \fn SbBool SbBox2d::hasArea(void) const
193 
194   Check if the box has "positive" area, i.e. the lower left corner is
195   actually lower and more to the left than the other corner point.
196 */
197 
198 /*!
199   \fn const SbVec2d & SbBox2d::getMin(void) const
200 
201   Returns the lower left corner of the box.
202 
203   \sa getOrigin(), getMax().
204 */
205 
206 /*!
207   \fn SbVec2d & SbBox2d::getMin(void)
208 
209   Returns the lower left corner of the box.
210 
211   \sa getOrigin(), getMax().
212 */
213 
214 /*!
215   \fn const SbVec2d & SbBox2d::getMax(void) const
216 
217   Returns the upper right corner of the box.
218 
219   \sa getMin().
220 */
221 
222 /*!
223   \fn SbVec2d & SbBox2d::getMax(void)
224 
225   Returns the upper right corner of the box.
226 
227   \sa getMin().
228 */
229 
230 /*!
231   \fn SbVec2d SbBox2d::getCenter(void) const
232 
233   Returns the center point of the box.
234 */
235 
236 /*!
237   Extend the boundaries of the box by the given point, i.e. make the
238   box fit around the \a point if it isn't already situated within it.
239 */
240 void
extendBy(const SbVec2d & point)241 SbBox2d::extendBy(const SbVec2d & point)
242 {
243   // The explicit cast to double is done to humour the HPUX aCC
244   // compiler, which will otherwise say ``Template deduction failed to
245   // find a match for the call to 'SbMin'''. mortene.
246   this->minpt.setValue(SbMin(static_cast<double>(point[0]), static_cast<double>(this->minpt[0])),
247                        SbMin(static_cast<double>(point[1]), static_cast<double>(this->minpt[1])));
248   this->maxpt.setValue(SbMax(static_cast<double>(point[0]), static_cast<double>(this->maxpt[0])),
249                        SbMax(static_cast<double>(point[1]), static_cast<double>(this->maxpt[1])));
250 }
251 
252 /*!
253   Extend the boundaries of the box by the given \a box parameter. This
254   is equal to calling the above method twice with the corner points.
255 */
256 void
extendBy(const SbBox2d & box)257 SbBox2d::extendBy(const SbBox2d & box)
258 {
259   if (box.isEmpty()) { return; }
260 
261   this->extendBy(box.getMin());
262   this->extendBy(box.getMax());
263 }
264 
265 /*!
266   Check if \a point lies within the boundaries of this box.
267 */
268 SbBool
intersect(const SbVec2d & point) const269 SbBox2d::intersect(const SbVec2d & point) const
270 {
271   if ((point[0] >= this->minpt[0]) && (point[0] <= this->maxpt[0]) &&
272       (point[1] >= this->minpt[1]) && (point[1] <= this->maxpt[1])) return TRUE;
273   return FALSE;
274 }
275 
276 /*!
277   Check if \a box lies wholly or partly within the boundaries
278   of this box.
279 */
280 SbBool
intersect(const SbBox2d & box) const281 SbBox2d::intersect(const SbBox2d & box) const
282 {
283   if ((box.getMax()[0] < this->getMin()[0]) ||
284       (box.getMax()[1] < this->getMin()[1]) ||
285       (box.getMin()[0] > this->getMax()[0]) ||
286       (box.getMin()[1] > this->getMax()[1])) return FALSE;
287   return TRUE;
288 }
289 
290 /*!
291   Check if a a line from \a a to \a b intersects the box, and return the
292   coordinates of the union line in \a ia and \a ib.
293 
294   This function is a Coin extension.
295 */
296 SbBool
findIntersection(const SbVec2d & a,const SbVec2d & b,SbVec2d & ia,SbVec2d & ib) const297 SbBox2d::findIntersection(const SbVec2d & a, const SbVec2d & b, SbVec2d & ia, SbVec2d & ib) const
298 {
299   // FIXME: this function should be tested thoroughly...
300 
301   // we place point a and b in their respective blocks, and handle cases accordingly
302   //
303   //   block-ids     intersection
304   //   6 | 7 | 8      candidates
305   //  ---+---+---       +-8-+
306   //   3 | 4 | 5        2   4
307   //  ---+---+---       +-1-+
308   //   0 | 1 | 2
309   static int candidates[9] = { 1|2, 1, 1|4, 2, 0, 4, 2|8, 8, 4|8 };
310 
311   int blocka = 0, blockb = 0;
312   if ( a[0] < this->minpt[0] ) blocka += 0;
313   else if ( a[0] <= this->maxpt[0] ) blocka += 1;
314   else blocka += 2;
315   if ( a[1] < this->minpt[1] ) blocka += 0;
316   else if ( a[1] <= this->maxpt[1] ) blocka += 3;
317   else blocka += 6;
318   if ( b[0] < this->minpt[0] ) blockb += 0;
319   else if ( b[0] <= this->maxpt[0] ) blockb += 1;
320   else blockb += 2;
321   if ( b[1] < this->minpt[1] ) blockb += 0;
322   else if ( b[1] <= this->maxpt[1] ) blockb += 3;
323   else blockb += 6;
324   int enterwalls = candidates[blocka];
325   int leavewalls = candidates[blockb];
326   // both a and b can be outside box in the same way
327   if ( (enterwalls & leavewalls) != 0 ) return FALSE;
328 
329   SbBool foundia = FALSE;
330   if ( blocka == 4 ) {
331     ia = a;
332     foundia = TRUE;
333   }
334   if ( !foundia && (enterwalls & 1) ) {
335     do {
336       if ( blockb == 0 || blockb == 1 || blockb == 2 ) break;
337       SbVec2d vec = b - a;
338       double t = (this->minpt[1] - a[1]) / vec[1];
339       if ( t < 0.0 || t > 1.0 ) break;
340       ia = a + vec * t;
341       if ( ia[0] < this->minpt[0] || ia[0] > this->maxpt[0] ) break;
342       foundia = TRUE;
343     } while ( FALSE );
344   }
345   if ( !foundia && (enterwalls & 2) ) {
346     do {
347       if ( blockb == 0 || blockb == 3 || blockb == 6 ) break;
348       SbVec2d vec = b - a;
349       double t = (this->minpt[0] - a[0]) / vec[0];
350       if ( t < 0.0 || t > 1.0 ) break;
351       ia = a + vec * t;
352       if ( ia[1] < this->minpt[1] || ia[1] > this->maxpt[1] ) break;
353       foundia = TRUE;
354     } while ( FALSE );
355   }
356   if ( !foundia && (enterwalls & 4) ) {
357     do {
358       if ( blockb == 2 || blockb == 5 || blockb == 8 ) break;
359       SbVec2d vec = b - a;
360       double t = (this->maxpt[0] - a[0]) / vec[0];
361       if ( t < 0.0 || t > 1.0 ) break;
362       ia = a + vec * t;
363       if ( ia[1] < this->minpt[1] || ia[1] > this->maxpt[1] ) break;
364       foundia = TRUE;
365     } while ( FALSE );
366   }
367   if ( !foundia && (enterwalls & 8) ) {
368     do {
369       if ( blockb == 6 || blockb == 7 || blockb == 8 ) break;
370       SbVec2d vec = b - a;
371       double t = (this->maxpt[1] - a[1]) / vec[1];
372       if ( t < 0.0 || t > 1.0 ) break;
373       ia = a + vec * t;
374       if ( ia[0] < this->minpt[0] || ia[0] > this->maxpt[0] ) break;
375       foundia = TRUE;
376     } while ( FALSE );
377   }
378   if ( !foundia ) return FALSE;
379 
380   SbBool foundib = FALSE;
381   if ( blockb == 4 ) {
382     ib = b;
383     foundib = TRUE;
384   }
385   if ( !foundib && (leavewalls & 1) ) {
386     do {
387       if ( blocka == 0 || blocka == 1 || blocka == 2 ) break;
388       SbVec2d vec = a - b;
389       double t = (this->minpt[1] - b[1]) / vec[1];
390       if ( t < 0.0 || t > 1.0 ) break;
391       ib = b + vec * t;
392       if ( ib[0] < this->minpt[0] || ib[0] > this->maxpt[0] ) break;
393       foundib = TRUE;
394     } while ( FALSE );
395   }
396   if ( !foundib && (leavewalls & 2) ) {
397     do {
398       if ( blocka == 0 || blocka == 3 || blocka == 6 ) break;
399       SbVec2d vec = a - b;
400       double t = (this->minpt[0] - b[0]) / vec[0];
401       if ( t < 0.0 || t > 1.0 ) break;
402       ib = b + vec * t;
403       if ( ib[1] < this->minpt[1] || ib[1] > this->maxpt[1] ) break;
404       foundib = TRUE;
405     } while ( FALSE );
406   }
407   if ( !foundib && (leavewalls & 4) ) {
408     do {
409       if ( blocka == 2 || blocka == 5 || blocka == 8 ) break;
410       SbVec2d vec = a - b;
411       double t = (this->maxpt[0] - b[0]) / vec[0];
412       if ( t < 0.0 || t > 1.0 ) break;
413       ib = b + vec * t;
414       if ( ib[1] < this->minpt[1] || ib[1] > this->maxpt[1] ) break;
415       foundib = TRUE;
416     } while ( FALSE );
417   }
418   if ( !foundib && (leavewalls & 8) ) {
419     do {
420       if ( blocka == 6 || blocka == 7 || blocka == 8 ) break;
421       SbVec2d vec = a - b;
422       double t = (this->maxpt[1] - b[1]) / vec[1];
423       if ( t < 0.0 || t > 1.0 ) break;
424       ib = b + vec * t;
425       if ( ib[0] < this->minpt[0] || ib[0] > this->maxpt[0] ) break;
426       foundib = TRUE;
427     } while ( FALSE );
428   }
429   if ( !foundib ) return FALSE;
430 
431   return TRUE;
432 } // findIntersection()
433 
434 /*!
435   Return the point on the box closest to the given point \a p.
436  */
437 SbVec2d
getClosestPoint(const SbVec2d & p) const438 SbBox2d::getClosestPoint(const SbVec2d & p) const
439 {
440   SbVec2d closest = p;
441 
442   SbVec2d center = this->getCenter();
443   double devx = closest[0] - center[0];
444   double devy = closest[1] - center[1];
445   double halfwidth = (maxpt[0] - minpt[0]) / 2.0f;
446   double halfheight = (maxpt[1] - minpt[1]) / 2.0f;
447 
448   // Move point to be on the nearest line of the box.
449   if (fabs(devx) > fabs(devy))
450     closest[0] = center[0] + halfwidth * ((devx < 0.0f) ? -1.0f : 1.0f);
451   else
452     closest[1] = center[1] + halfheight * ((devy < 0.0f) ? -1.0f : 1.0f);
453 
454   // Clamp to be inside box.
455   closest[0] = SbMin(SbMax(closest[0], this->minpt[0]), this->maxpt[0]);
456   closest[1] = SbMin(SbMax(closest[1], this->minpt[1]), this->maxpt[1]);
457 
458   return closest;
459 }
460 
461 /*!
462   \fn void SbBox2d::getBounds(double & xmin, double & ymin, double & xmax, double & ymax) const
463 
464   Returns the box boundaries.
465 
466   \sa setBounds(), getMin(), getMax().
467 */
468 
469 /*!
470   \fn void SbBox2d::getBounds(SbVec2d & min, SbVec2d & max) const
471 
472   Returns the box min and max corner points.
473 
474   \sa setBounds(), getMin(), getMax().
475 */
476 
477 /*!
478   \fn void SbBox2d::getOrigin(double & originX, double & originY) const
479 
480   Returns the coordinates of the box origin (i.e. the lower left corner).
481 
482   \sa getMin().
483 */
484 
485 /*!
486   \fn void SbBox2d::getSize(double & sizeX, double & sizeY) const
487 
488   Returns width and height of box.
489 */
490 
491 /*!
492   \fn double SbBox2d::getAspectRatio(void) const
493 
494   Returns aspect ratio of box, which is defined as box width divided on
495   box height.
496 */
497 
498 /*!
499   \fn int operator == (const SbBox2d & b1, const SbBox2d & b2)
500   \relates SbBox2d
501 
502   Check \a b1 and \a b2 for equality.
503 */
504 
505 /*!
506   \fn int operator != (const SbBox2d & b1, const SbBox2d & b2)
507   \relates SbBox2d
508 
509   Check \a b1 and \a b2 for inequality.
510 */
511 
512 #ifdef COIN_TEST_SUITE
BOOST_AUTO_TEST_CASE(checkSize)513 BOOST_AUTO_TEST_CASE(checkSize) {
514   SbVec2d min(1,2);
515   SbVec2d max(3,4);
516 
517   SbVec2d diff = max - min;
518 
519 
520   SbBox2d box(min, max);
521 
522   BOOST_CHECK_MESSAGE(box.getSize() == diff,
523                       "Box has incorrect size");
524 
525 }
526 #endif //COIN_TEST_SUITE
527