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