Skip to main content

arris_math/
aabb.rs

1//! Axis-aligned bounding boxes: the cheap reject a boolean's face pairs
2//! and a renderer's camera fit are both over.
3
4use crate::Point3;
5
6/// The axis-aligned box around a set of points: `min ≤ max` on every axis,
7/// both finite. A box is never empty; an empty point set has no box.
8///
9/// ```
10/// use arris_math::Aabb;
11///
12/// let b = Aabb::of_points(&[[0.0, 0.0, 0.0], [2.0, -1.0, 3.0]]).unwrap();
13/// assert_eq!(b.min, [0.0, -1.0, 0.0]);
14/// assert_eq!(b.max, [2.0, 0.0, 3.0]);
15/// assert_eq!(b.extent(), [2.0, 1.0, 3.0]);
16/// assert_eq!(b.center(), [1.0, -0.5, 1.5]);
17/// ```
18#[derive(Debug, Clone, Copy, PartialEq)]
19pub struct Aabb {
20    /// The smallest coordinate on each axis.
21    pub min: [f64; 3],
22    /// The largest coordinate on each axis.
23    pub max: [f64; 3],
24}
25
26impl Aabb {
27    /// The box around `points`, or `None` for no points.
28    pub fn of_points<'a>(points: impl IntoIterator<Item = &'a [f64; 3]>) -> Option<Aabb> {
29        let mut it = points.into_iter();
30        let first = *it.next()?;
31        let mut b = Aabb {
32            min: first,
33            max: first,
34        };
35        for p in it {
36            b = b.union(Aabb { min: *p, max: *p });
37        }
38        Some(b)
39    }
40
41    /// The smallest box containing both.
42    pub fn union(self, other: Aabb) -> Aabb {
43        let mut b = self;
44        for ((lo, hi), (olo, ohi)) in b
45            .min
46            .iter_mut()
47            .zip(b.max.iter_mut())
48            .zip(other.min.iter().zip(other.max.iter()))
49        {
50            *lo = lo.min(*olo);
51            *hi = hi.max(*ohi);
52        }
53        b
54    }
55
56    /// `max - min` per axis.
57    pub fn extent(&self) -> [f64; 3] {
58        [
59            self.max[0] - self.min[0],
60            self.max[1] - self.min[1],
61            self.max[2] - self.min[2],
62        ]
63    }
64
65    /// The midpoint.
66    pub fn center(&self) -> [f64; 3] {
67        [
68            0.5 * (self.min[0] + self.max[0]),
69            0.5 * (self.min[1] + self.max[1]),
70            0.5 * (self.min[2] + self.max[2]),
71        ]
72    }
73
74    /// The length of the box's diagonal: the scale of what it contains.
75    pub fn diagonal(&self) -> f64 {
76        let e = self.extent();
77        (e[0] * e[0] + e[1] * e[1] + e[2] * e[2]).sqrt()
78    }
79
80    /// The box holding one point, of zero extent.
81    ///
82    /// ```
83    /// use arris_math::{Aabb, Point3};
84    ///
85    /// let b = Aabb::of_point(Point3::new(1.0, 2.0, 3.0));
86    /// assert_eq!(b.min, b.max);
87    /// assert_eq!(b.extent(), [0.0; 3]);
88    /// ```
89    pub fn of_point(p: Point3) -> Aabb {
90        Aabb {
91            min: [p.x, p.y, p.z],
92            max: [p.x, p.y, p.z],
93        }
94    }
95
96    /// `true` when the two boxes share a point, touching included: the
97    /// cheap reject before a face pair is intersected. Boxes that only
98    /// touch on a face intersect, so a caller that needs a margin
99    /// [`Aabb::inflated`] one of them by its tolerance first.
100    ///
101    /// ```
102    /// use arris_math::Aabb;
103    ///
104    /// let a = Aabb { min: [0.0; 3], max: [1.0; 3] };
105    /// let touching = Aabb { min: [1.0, 0.0, 0.0], max: [2.0, 1.0, 1.0] };
106    /// let clear = Aabb { min: [1.5, 0.0, 0.0], max: [2.0, 1.0, 1.0] };
107    /// assert!(a.intersects(&touching));
108    /// assert!(!a.intersects(&clear));
109    /// ```
110    pub fn intersects(&self, other: &Aabb) -> bool {
111        (0..3).all(|i| self.min[i] <= other.max[i] && other.min[i] <= self.max[i])
112    }
113
114    /// The box grown by `by` on every side; a negative `by` shrinks it,
115    /// and never past a point — the centre holds.
116    ///
117    /// ```
118    /// use arris_math::Aabb;
119    ///
120    /// let b = Aabb { min: [0.0; 3], max: [2.0; 3] };
121    /// assert_eq!(b.inflated(0.5).min, [-0.5; 3]);
122    /// assert_eq!(b.inflated(-5.0).extent(), [0.0; 3], "never past a point");
123    /// ```
124    pub fn inflated(&self, by: f64) -> Aabb {
125        let mut out = *self;
126        for i in 0..3 {
127            let centre = 0.5 * (self.min[i] + self.max[i]);
128            out.min[i] = (self.min[i] - by).min(centre);
129            out.max[i] = (self.max[i] + by).max(centre);
130        }
131        out
132    }
133}