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