1
2 //
3 // This source file is part of appleseed.
4 // Visit https://appleseedhq.net/ for additional information and resources.
5 //
6 // This software is released under the MIT license.
7 //
8 // Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
9 // Copyright (c) 2014-2018 Francois Beaune, The appleseedhq Organization
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
12 // of this software and associated documentation files (the "Software"), to deal
13 // in the Software without restriction, including without limitation the rights
14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 // copies of the Software, and to permit persons to whom the Software is
16 // furnished to do so, subject to the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 // THE SOFTWARE.
28 //
29
30 #pragma once
31
32 // appleseed.foundation headers.
33 #include "foundation/math/minmax.h"
34 #include "foundation/math/scalar.h"
35 #include "foundation/math/vector.h"
36
37 // Imath headers.
38 #ifdef APPLESEED_ENABLE_IMATH_INTEROP
39 #include "foundation/platform/_beginexrheaders.h"
40 #include "Imath/ImathBox.h"
41 #include "foundation/platform/_endexrheaders.h"
42 #endif
43
44 // Standard headers.
45 #include <algorithm>
46 #include <cassert>
47 #include <cmath>
48 #include <cstddef>
49 #include <limits>
50
51 namespace foundation
52 {
53
54 //
55 // N-dimensional integral axis-aligned bounding box [min, max] class and operations.
56 // The boundary of the bounding box is considered to belong to the bounding box.
57 //
58
59 template <typename T, size_t N>
60 class AABBBase
61 {
62 public:
63 // Value, vector and AABB types.
64 typedef T ValueType;
65 typedef Vector<T, N> VectorType;
66 typedef AABBBase<T, N> AABBType;
67
68 // Dimension.
69 static const size_t Dimension = N;
70
71 // Bounds.
72 VectorType min, max;
73
74 // Constructors.
75 #if APPLESEED_COMPILER_CXX_DEFAULTED_FUNCTIONS
76 AABBBase() = default; // leave all components uninitialized
77 #else
AABBBase()78 AABBBase() {} // leave all components uninitialized
79 #endif
80 AABBBase(
81 const VectorType& min, // lower bound
82 const VectorType& max); // upper bound
83
84 // Construct a bounding box from another bounding box of a different type.
85 template <typename U>
86 AABBBase(const AABBBase<U, N>& rhs);
87
88 #ifdef APPLESEED_ENABLE_IMATH_INTEROP
89
90 // Implicit construction from an Imath::Box.
91 AABBBase(const Imath::Box<typename ImathVecEquivalent<T, N>::Type>& rhs);
92
93 // Reinterpret this bounding box as an Imath::Box.
94 operator Imath::Box<typename ImathVecEquivalent<T, N>::Type>&();
95 operator const Imath::Box<typename ImathVecEquivalent<T, N>::Type>&() const;
96
97 #endif
98
99 // Return an invalidated bounding box.
100 static AABBType invalid();
101
102 // Compute the intersection between two bounding boxes.
103 static AABBType intersect(const AABBType& a, const AABBType& b);
104
105 // Return true if two bounding boxes overlap.
106 static bool overlap(const AABBType& a, const AABBType& b);
107
108 // Unchecked array subscripting. [0] is min, [1] is max.
109 VectorType& operator[](const size_t i);
110 const VectorType& operator[](const size_t i) const;
111
112 // Invalidate the bounding box (give it a negative volume).
113 void invalidate();
114
115 // Insert a point or another bounding box into the bounding box.
116 void insert(const VectorType& v);
117 void insert(const AABBType& b);
118
119 // Grow the bounding box by a fixed amount in every dimension.
120 void grow(const VectorType& v);
121
122 // Translate the bounding box.
123 void translate(const VectorType& v);
124
125 // Return true if the extent of the bounding box is positive or
126 // null along all dimensions.
127 bool is_valid() const;
128
129 // Return the rank of the bounding box (the number of dimensions
130 // along which the bounding box has a strictly positive extent).
131 size_t rank() const;
132
133 // Compute the extent of the bounding box.
134 VectorType extent() const;
135 ValueType extent(const size_t dim) const;
136
137 // Return the volume of the bounding box.
138 T volume() const;
139
140 // Compute the 2^N corner points of the bounding box.
141 size_t get_corner_count() const;
142 VectorType compute_corner(const size_t i) const;
143
144 // Return true if the bounding box contains a given point.
145 bool contains(const VectorType& v) const;
146 };
147
148 // Exact inequality and equality tests.
149 template <typename T, size_t N> bool operator!=(const AABBBase<T, N>& lhs, const AABBBase<T, N>& rhs);
150 template <typename T, size_t N> bool operator==(const AABBBase<T, N>& lhs, const AABBBase<T, N>& rhs);
151
152 // Bounding box arithmetic.
153 template <typename T, size_t N> AABBBase<T, N> operator+ (const AABBBase<T, N>& lhs, const AABBBase<T, N>& rhs);
154 template <typename T, size_t N> AABBBase<T, N> operator* (const AABBBase<T, N>& lhs, const T rhs);
155 template <typename T, size_t N> AABBBase<T, N> operator* (const T lhs, const AABBBase<T, N>& rhs);
156 template <typename T, size_t N> AABBBase<T, N>& operator+=(AABBBase<T, N>& lhs, const AABBBase<T, N>& rhs);
157 template <typename T, size_t N> AABBBase<T, N>& operator*=(AABBBase<T, N>& lhs, const T rhs);
158
159
160 //
161 // N-dimensional floating-point axis-aligned bounding box [min, max] class.
162 //
163
164 template <typename T, size_t N>
165 class AABB
166 : public AABBBase<T, N>
167 {
168 public:
169 // Value, vector and AABB types.
170 typedef T ValueType;
171 typedef Vector<T, N> VectorType;
172 typedef AABB<T, N> AABBType;
173
174 // Constructors.
175 #if APPLESEED_COMPILER_CXX_DEFAULTED_FUNCTIONS
176 AABB() = default; // leave all components uninitialized
177 #else
AABB()178 AABB() {} // leave all components uninitialized
179 #endif
180 AABB(
181 const VectorType& min, // lower bound
182 const VectorType& max); // upper bound
183
184 // Construct a bounding box from another bounding box of a different type.
185 template <typename U>
186 AABB(const AABB<U, N>& rhs);
187
188 // Construct a bounding box from an integral one.
189 AABB(const AABBBase<T, N>& rhs);
190
191 // Return the amount of overlapping between two bounding boxes.
192 // Returns 0.0 if the bounding boxes are disjoint, 1.0 if one
193 // is contained in the other.
194 static ValueType overlap_ratio(const AABBType& a, const AABBType& b);
195
196 // Return the ratio of the extent of a to the extent of b.
197 static ValueType extent_ratio(const AABBType& a, const AABBType& b);
198
199 // Robustly grow the bounding box by a given epsilon factor.
200 void robust_grow(const ValueType eps);
201
202 // Return the diameter of the bounding box.
203 ValueType diameter() const;
204
205 // Return the square diameter of the bounding box.
206 ValueType square_diameter() const;
207
208 // Return the radius of the bounding box.
209 ValueType radius() const;
210
211 // Return the square radius of the bounding box.
212 ValueType square_radius() const;
213
214 // Compute the center of the bounding box.
215 VectorType center() const;
216 ValueType center(const size_t dim) const;
217
218 // Compute the extent of the bounding box.
219 VectorType extent() const;
220 ValueType extent(const size_t dim) const;
221
222 // Return the volume of the bounding box.
223 T volume() const;
224 };
225
226 // Compute the surface area of a 3D bounding box.
227 template <typename T> T half_surface_area(const AABB<T, 3>& bbox);
228 template <typename T> T surface_area(const AABB<T, 3>& bbox);
229
230 // Approximate equality tests.
231 template <typename T, size_t N> bool feq(const AABBBase<T, N>& lhs, const AABBBase<T, N>& rhs);
232 template <typename T, size_t N> bool feq(const AABBBase<T, N>& lhs, const AABBBase<T, N>& rhs, const T eps);
233
234
235 //
236 // Full specializations for 1D, 2D, 3D and 4D vectors of type int, float and double.
237 //
238
239 typedef AABB<float, 1> AABB1f;
240 typedef AABB<double, 1> AABB1d;
241
242 typedef AABB<float, 2> AABB2f;
243 typedef AABB<double, 2> AABB2d;
244
245 typedef AABB<float, 3> AABB3f;
246 typedef AABB<double, 3> AABB3d;
247
248 typedef AABB<float, 4> AABB4f;
249 typedef AABB<double, 4> AABB4d;
250
251 typedef AABBBase<int, 1> AABB1i;
252 typedef AABBBase<size_t, 1> AABB1u;
253
254 typedef AABBBase<int, 2> AABB2i;
255 typedef AABBBase<size_t, 2> AABB2u;
256
257 typedef AABBBase<int, 3> AABB3i;
258 typedef AABBBase<size_t, 3> AABB3u;
259
260 typedef AABBBase<int, 4> AABB4i;
261 typedef AABBBase<size_t, 4> AABB4u;
262
263
264 //
265 // N-dimensional integral axis-aligned bounding box class and operations.
266 //
267
268 template <typename T, size_t N>
AABBBase(const VectorType & min_,const VectorType & max_)269 inline AABBBase<T, N>::AABBBase(
270 const VectorType& min_,
271 const VectorType& max_)
272 : min(min_)
273 , max(max_)
274 {
275 }
276
277 template <typename T, size_t N>
278 template <typename U>
AABBBase(const AABBBase<U,N> & rhs)279 inline AABBBase<T, N>::AABBBase(const AABBBase<U, N>& rhs)
280 : min(VectorType(rhs.min))
281 , max(VectorType(rhs.max))
282 {
283 }
284
285 #ifdef APPLESEED_ENABLE_IMATH_INTEROP
286
287 template <typename T, size_t N>
AABBBase(const Imath::Box<typename ImathVecEquivalent<T,N>::Type> & rhs)288 inline AABBBase<T, N>::AABBBase(const Imath::Box<typename ImathVecEquivalent<T, N>::Type>& rhs)
289 : min(rhs.min)
290 , max(rhs.max)
291 {
292 }
293
294 template <typename T, size_t N>
295 inline AABBBase<T, N>::operator Imath::Box<typename ImathVecEquivalent<T, N>::Type>&()
296 {
297 return reinterpret_cast<Imath::Box<typename ImathVecEquivalent<T, N>::Type>&>(*this);
298 }
299
300 template <typename T, size_t N>
301 inline AABBBase<T, N>::operator const Imath::Box<typename ImathVecEquivalent<T, N>::Type>&() const
302 {
303 return reinterpret_cast<const Imath::Box<typename ImathVecEquivalent<T, N>::Type>&>(*this);
304 }
305
306 #endif
307
308 template <typename T, size_t N>
invalid()309 inline AABBBase<T, N> AABBBase<T, N>::invalid()
310 {
311 AABBBase<T, N> bbox;
312 bbox.invalidate();
313 return bbox;
314 }
315
316 template <typename T, size_t N>
intersect(const AABBType & a,const AABBType & b)317 inline AABBBase<T, N> AABBBase<T, N>::intersect(const AABBType& a, const AABBType& b)
318 {
319 assert(a.is_valid());
320 assert(b.is_valid());
321
322 AABBType intersection;
323
324 for (size_t i = 0; i < N; ++i)
325 {
326 intersection.min[i] = std::max(a.min[i], b.min[i]);
327 intersection.max[i] = std::min(a.max[i], b.max[i]);
328 }
329
330 return intersection;
331 }
332
333 template <typename T, size_t N>
overlap(const AABBType & a,const AABBType & b)334 inline bool AABBBase<T, N>::overlap(const AABBType& a, const AABBType& b)
335 {
336 assert(a.is_valid());
337 assert(b.is_valid());
338
339 for (size_t i = 0; i < N; ++i)
340 {
341 if (a.min[i] > b.max[i] || a.max[i] < b.min[i])
342 return false;
343 }
344
345 return true;
346 }
347
348 template <typename T, size_t N>
349 inline Vector<T, N>& AABBBase<T, N>::operator[](const size_t i)
350 {
351 assert(i < 2);
352 return (&min)[i];
353 }
354
355 template <typename T, size_t N>
356 inline const Vector<T, N>& AABBBase<T, N>::operator[](const size_t i) const
357 {
358 assert(i < 2);
359 return (&min)[i];
360 }
361
362 template <typename T, size_t N>
invalidate()363 inline void AABBBase<T, N>::invalidate()
364 {
365 for (size_t i = 0; i < N; ++i)
366 {
367 min[i] = std::numeric_limits<T>::max();
368 max[i] = signed_min<T>();
369 }
370 }
371
372 template <typename T, size_t N>
insert(const VectorType & v)373 inline void AABBBase<T, N>::insert(const VectorType& v)
374 {
375 for (size_t i = 0; i < N; ++i)
376 {
377 if (min[i] > v[i])
378 min[i] = v[i];
379 if (max[i] < v[i])
380 max[i] = v[i];
381 }
382 }
383
384 template <typename T, size_t N>
insert(const AABBType & b)385 inline void AABBBase<T, N>::insert(const AABBType& b)
386 {
387 for (size_t i = 0; i < N; ++i)
388 {
389 if (min[i] > b.min[i])
390 min[i] = b.min[i];
391 if (max[i] < b.max[i])
392 max[i] = b.max[i];
393 }
394 }
395
396 template <typename T, size_t N>
grow(const VectorType & v)397 inline void AABBBase<T, N>::grow(const VectorType& v)
398 {
399 assert(is_valid());
400
401 min -= v;
402 max += v;
403 }
404
405 template <typename T, size_t N>
translate(const VectorType & v)406 inline void AABBBase<T, N>::translate(const VectorType& v)
407 {
408 assert(is_valid());
409
410 min += v;
411 max += v;
412 }
413
414 template <typename T, size_t N>
is_valid()415 inline bool AABBBase<T, N>::is_valid() const
416 {
417 for (size_t i = 0; i < N; ++i)
418 {
419 // Return false if NaN values creep in.
420 if (!(min[i] <= max[i]))
421 return false;
422 }
423
424 return true;
425 }
426
427 template <typename T, size_t N>
rank()428 inline size_t AABBBase<T, N>::rank() const
429 {
430 size_t r = 0;
431
432 for (size_t i = 0; i < N; ++i)
433 {
434 if (min[i] < max[i])
435 ++r;
436 }
437
438 return r;
439 }
440
441 template <typename T, size_t N>
get_corner_count()442 inline size_t AABBBase<T, N>::get_corner_count() const
443 {
444 return 1UL << N;
445 }
446
447 template <typename T, size_t N>
compute_corner(const size_t i)448 Vector<T, N> AABBBase<T, N>::compute_corner(const size_t i) const
449 {
450 assert(is_valid());
451
452 VectorType p;
453
454 for (size_t d = 0; d < N; ++d)
455 p[d] = i & (size_t(1) << d) ? max[d] : min[d];
456
457 return p;
458 }
459
460 template <typename T, size_t N>
contains(const VectorType & v)461 inline bool AABBBase<T, N>::contains(const VectorType& v) const
462 {
463 assert(is_valid());
464
465 for (size_t i = 0; i < N; ++i)
466 {
467 if (v[i] < min[i] || v[i] > max[i])
468 return false;
469 }
470
471 return true;
472 }
473
474 template <typename T, size_t N>
475 inline bool operator!=(const AABBBase<T, N>& lhs, const AABBBase<T, N>& rhs)
476 {
477 return lhs.min != rhs.min || lhs.max != rhs.max;
478 }
479
480 template <typename T, size_t N>
481 inline bool operator==(const AABBBase<T, N>& lhs, const AABBBase<T, N>& rhs)
482 {
483 return !(lhs != rhs);
484 }
485
486 template <typename T, size_t N>
487 inline AABBBase<T, N> operator+(const AABBBase<T, N>& lhs, const AABBBase<T, N>& rhs)
488 {
489 return AABBBase<T, N>(lhs.min + rhs.min, lhs.max + rhs.max);
490 }
491
492 template <typename T, size_t N>
493 inline AABBBase<T, N> operator*(const AABBBase<T, N>& lhs, const T rhs)
494 {
495 return AABBBase<T, N>(lhs.min * rhs, lhs.max * rhs);
496 }
497
498 template <typename T, size_t N>
499 inline AABBBase<T, N> operator*(const T lhs, const AABBBase<T, N>& rhs)
500 {
501 return AABBBase<T, N>(lhs * rhs.min, lhs * rhs.max);
502 }
503
504 template <typename T, size_t N>
505 inline AABBBase<T, N>& operator+=(AABBBase<T, N>& lhs, const AABBBase<T, N>& rhs)
506 {
507 lhs.min += rhs.min;
508 lhs.max += rhs.max;
509 return lhs;
510 }
511
512 template <typename T, size_t N>
513 inline AABBBase<T, N>& operator*=(AABBBase<T, N>& lhs, const T rhs)
514 {
515 lhs.min *= rhs;
516 lhs.max *= rhs;
517 return lhs;
518 }
519
520 template <typename T, size_t N>
extent()521 inline Vector<T, N> AABBBase<T, N>::extent() const
522 {
523 assert(is_valid());
524
525 return max - min + VectorType(1);
526 }
527
528 template <typename T, size_t N>
extent(const size_t dim)529 inline T AABBBase<T, N>::extent(const size_t dim) const
530 {
531 assert(is_valid());
532
533 return max[dim] - min[dim] + T(1);
534 }
535
536 template <typename T, size_t N>
volume()537 inline T AABBBase<T, N>::volume() const
538 {
539 assert(is_valid());
540
541 const VectorType e = extent();
542
543 ValueType volume = e[0];
544
545 for (size_t i = 1; i < N; ++i)
546 volume *= e[i];
547
548 return volume;
549 }
550
551
552 //
553 // Floating-point bounding box specialization.
554 //
555 //
556
557 template <typename T, size_t N>
AABB(const VectorType & min_,const VectorType & max_)558 inline AABB<T, N>::AABB(
559 const VectorType& min_,
560 const VectorType& max_)
561 {
562 AABBType::min = min_;
563 AABBType::max = max_;
564 }
565
566 template <typename T, size_t N>
567 template <typename U>
AABB(const AABB<U,N> & rhs)568 inline AABB<T, N>::AABB(const AABB<U, N>& rhs)
569 {
570 AABBType::min = VectorType(rhs.min);
571 AABBType::max = VectorType(rhs.max);
572 }
573
574 template <typename T, size_t N>
AABB(const AABBBase<T,N> & rhs)575 inline AABB<T,N>::AABB(const AABBBase<T, N>& rhs)
576 {
577 AABBType::min = VectorType(rhs.min);
578 AABBType::max = VectorType(rhs.max);
579 }
580
581 template <typename T, size_t N>
overlap_ratio(const AABBType & a,const AABBType & b)582 inline T AABB<T, N>::overlap_ratio(const AABBType& a, const AABBType& b)
583 {
584 assert(a.is_valid());
585 assert(b.is_valid());
586
587 T ratio = T(1.0);
588
589 for (size_t i = 0; i < N; ++i)
590 {
591 const T amin = a.min[i];
592 const T amax = a.max[i];
593 const T bmin = b.min[i];
594 const T bmax = b.max[i];
595
596 const T overlap = std::min(amax, bmax) - std::max(amin, bmin);
597
598 if (overlap <= T(0.0))
599 return T(0.0);
600
601 ratio *= overlap / std::min(amax - amin, bmax - bmin);
602 }
603
604 return ratio;
605 }
606
607 template <typename T, size_t N>
extent_ratio(const AABBType & a,const AABBType & b)608 inline T AABB<T, N>::extent_ratio(const AABBType& a, const AABBType& b)
609 {
610 assert(a.is_valid());
611 assert(b.is_valid());
612
613 const VectorType ea = a.extent();
614 const VectorType eb = b.extent();
615
616 T ratio = T(1.0);
617
618 for (size_t i = 0; i < N; ++i)
619 {
620 if (ea[i] != eb[i])
621 ratio *= ea[i] / eb[i];
622 }
623
624 return ratio;
625 }
626
627 template <typename T, size_t N>
robust_grow(const ValueType eps)628 inline void AABB<T, N>::robust_grow(const ValueType eps)
629 {
630 assert(AABBType::is_valid());
631
632 const VectorType c = ValueType(0.5) * (AABBType::min + AABBType::max);
633 const VectorType e = AABBType::max - AABBType::min;
634
635 for (size_t i = 0; i < N; ++i)
636 {
637 const ValueType dominant_factor =
638 foundation::max( // namespace qualification required
639 std::abs(c[i]),
640 e[i],
641 ValueType(1.0));
642
643 const ValueType delta = dominant_factor * eps;
644
645 AABBType::min[i] -= delta;
646 AABBType::max[i] += delta;
647 }
648 }
649
650 template <typename T, size_t N>
diameter()651 inline T AABB<T, N>::diameter() const
652 {
653 assert(AABBType::is_valid());
654
655 return norm(AABBType::extent());
656 }
657
658 template <typename T, size_t N>
square_diameter()659 inline T AABB<T, N>::square_diameter() const
660 {
661 assert(AABBType::is_valid());
662
663 return square_norm(AABBType::extent());
664 }
665
666 template <typename T, size_t N>
radius()667 inline T AABB<T, N>::radius() const
668 {
669 assert(AABBType::is_valid());
670
671 return T(0.5) * diameter();
672 }
673
674 template <typename T, size_t N>
square_radius()675 inline T AABB<T, N>::square_radius() const
676 {
677 assert(AABBType::is_valid());
678
679 return T(0.25) * square_diameter();
680 }
681
682 template <typename T, size_t N>
center()683 inline Vector<T, N> AABB<T, N>::center() const
684 {
685 assert(AABBType::is_valid());
686
687 return ValueType(0.5) * (AABBType::min + AABBType::max);
688 }
689
690 template <typename T, size_t N>
center(const size_t dim)691 inline T AABB<T, N>::center(const size_t dim) const
692 {
693 assert(AABBType::is_valid());
694
695 return ValueType(0.5) * (AABBType::min[dim] + AABBType::max[dim]);
696 }
697
698 template <typename T, size_t N>
extent()699 inline Vector<T, N> AABB<T, N>::extent() const
700 {
701 assert(AABBType::is_valid());
702
703 return AABBType::max - AABBType::min;
704 }
705
706 template <typename T, size_t N>
extent(const size_t dim)707 inline T AABB<T, N>::extent(const size_t dim) const
708 {
709 assert(AABBType::is_valid());
710
711 return AABBType::max[dim] - AABBType::min[dim];
712 }
713
714 template <typename T, size_t N>
volume()715 inline T AABB<T, N>::volume() const
716 {
717 assert(AABBType::is_valid());
718
719 const VectorType e = AABBType::max - AABBType::min;
720
721 ValueType volume = e[0];
722
723 for (size_t i = 1; i < N; ++i)
724 volume *= e[i];
725
726 return volume;
727 }
728
729 template <typename T>
half_surface_area(const AABB<T,3> & bbox)730 inline T half_surface_area(const AABB<T, 3>& bbox)
731 {
732 assert(bbox.is_valid());
733
734 const Vector<T, 3> e = bbox.max - bbox.min;
735
736 return e[0] * e[1] + e[0] * e[2] + e[1] * e[2];
737 }
738
739 template <typename T>
surface_area(const AABB<T,3> & bbox)740 inline T surface_area(const AABB<T, 3>& bbox)
741 {
742 const T h = half_surface_area(bbox);
743 return h + h;
744 }
745
746 template <typename T, size_t N>
feq(const AABBBase<T,N> & lhs,const AABBBase<T,N> & rhs)747 inline bool feq(const AABBBase<T, N>& lhs, const AABBBase<T, N>& rhs)
748 {
749 return feq(lhs.min, rhs.min) && feq(lhs.max, rhs.max);
750 }
751
752 template <typename T, size_t N>
feq(const AABBBase<T,N> & lhs,const AABBBase<T,N> & rhs,const T eps)753 inline bool feq(const AABBBase<T, N>& lhs, const AABBBase<T, N>& rhs, const T eps)
754 {
755 return feq(lhs.min, rhs.min, eps) && feq(lhs.max, rhs.max, eps);
756 }
757
758
759 } // namespace foundation
760