Skip to main content

rustsim_geometry/
aabb.rs

1//! Axis-aligned bounding boxes in 2-D and 3-D.
2
3use crate::vec2::Vec2;
4use crate::vec3::Vec3;
5
6/// 2-D axis-aligned bounding box.
7#[derive(Debug, Clone, Copy, PartialEq)]
8pub struct Aabb2 {
9    /// Minimum corner (inclusive).
10    pub min: Vec2,
11    /// Maximum corner (inclusive).
12    pub max: Vec2,
13}
14
15impl Aabb2 {
16    /// Construct from two corners; the result is normalised so `min <= max`
17    /// on each axis.
18    #[inline]
19    pub fn from_points(a: Vec2, b: Vec2) -> Self {
20        Self {
21            min: [a[0].min(b[0]), a[1].min(b[1])],
22            max: [a[0].max(b[0]), a[1].max(b[1])],
23        }
24    }
25
26    /// Degenerate box at a single point.
27    #[inline]
28    pub fn point(p: Vec2) -> Self {
29        Self { min: p, max: p }
30    }
31
32    /// Expand this box to include `p` in place.
33    #[inline]
34    pub fn expand(&mut self, p: Vec2) {
35        self.min = [self.min[0].min(p[0]), self.min[1].min(p[1])];
36        self.max = [self.max[0].max(p[0]), self.max[1].max(p[1])];
37    }
38
39    /// Is `p` inside the closed box?
40    #[inline]
41    pub fn contains(&self, p: Vec2) -> bool {
42        p[0] >= self.min[0] && p[0] <= self.max[0] && p[1] >= self.min[1] && p[1] <= self.max[1]
43    }
44
45    /// Do the two boxes overlap (closed intervals)?
46    #[inline]
47    pub fn intersects(&self, other: &Aabb2) -> bool {
48        self.min[0] <= other.max[0]
49            && self.max[0] >= other.min[0]
50            && self.min[1] <= other.max[1]
51            && self.max[1] >= other.min[1]
52    }
53
54    /// Width and height.
55    #[inline]
56    pub fn size(&self) -> Vec2 {
57        [self.max[0] - self.min[0], self.max[1] - self.min[1]]
58    }
59
60    /// Geometric centre.
61    #[inline]
62    pub fn center(&self) -> Vec2 {
63        [
64            0.5 * (self.min[0] + self.max[0]),
65            0.5 * (self.min[1] + self.max[1]),
66        ]
67    }
68}
69
70/// 3-D axis-aligned bounding box.
71#[derive(Debug, Clone, Copy, PartialEq)]
72pub struct Aabb3 {
73    /// Minimum corner (inclusive).
74    pub min: Vec3,
75    /// Maximum corner (inclusive).
76    pub max: Vec3,
77}
78
79impl Aabb3 {
80    /// Construct from two corners, normalised per axis.
81    #[inline]
82    pub fn from_points(a: Vec3, b: Vec3) -> Self {
83        Self {
84            min: [a[0].min(b[0]), a[1].min(b[1]), a[2].min(b[2])],
85            max: [a[0].max(b[0]), a[1].max(b[1]), a[2].max(b[2])],
86        }
87    }
88
89    /// Degenerate box at a single point.
90    #[inline]
91    pub fn point(p: Vec3) -> Self {
92        Self { min: p, max: p }
93    }
94
95    /// Expand this box to include `p` in place.
96    #[inline]
97    pub fn expand(&mut self, p: Vec3) {
98        self.min = [
99            self.min[0].min(p[0]),
100            self.min[1].min(p[1]),
101            self.min[2].min(p[2]),
102        ];
103        self.max = [
104            self.max[0].max(p[0]),
105            self.max[1].max(p[1]),
106            self.max[2].max(p[2]),
107        ];
108    }
109
110    /// Is `p` inside the closed box?
111    #[inline]
112    pub fn contains(&self, p: Vec3) -> bool {
113        p[0] >= self.min[0]
114            && p[0] <= self.max[0]
115            && p[1] >= self.min[1]
116            && p[1] <= self.max[1]
117            && p[2] >= self.min[2]
118            && p[2] <= self.max[2]
119    }
120
121    /// Do the two boxes overlap?
122    #[inline]
123    pub fn intersects(&self, other: &Aabb3) -> bool {
124        self.min[0] <= other.max[0]
125            && self.max[0] >= other.min[0]
126            && self.min[1] <= other.max[1]
127            && self.max[1] >= other.min[1]
128            && self.min[2] <= other.max[2]
129            && self.max[2] >= other.min[2]
130    }
131
132    /// Width, depth, height.
133    #[inline]
134    pub fn size(&self) -> Vec3 {
135        [
136            self.max[0] - self.min[0],
137            self.max[1] - self.min[1],
138            self.max[2] - self.min[2],
139        ]
140    }
141
142    /// Geometric centre.
143    #[inline]
144    pub fn center(&self) -> Vec3 {
145        [
146            0.5 * (self.min[0] + self.max[0]),
147            0.5 * (self.min[1] + self.max[1]),
148            0.5 * (self.min[2] + self.max[2]),
149        ]
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use super::*;
156
157    #[test]
158    fn aabb2_contains_and_intersects() {
159        let a = Aabb2::from_points([0.0, 0.0], [10.0, 10.0]);
160        assert!(a.contains([5.0, 5.0]));
161        assert!(!a.contains([11.0, 5.0]));
162
163        let b = Aabb2::from_points([5.0, 5.0], [15.0, 15.0]);
164        let c = Aabb2::from_points([100.0, 100.0], [200.0, 200.0]);
165        assert!(a.intersects(&b));
166        assert!(!a.intersects(&c));
167    }
168
169    #[test]
170    fn aabb3_size_and_center() {
171        let a = Aabb3::from_points([0.0, 0.0, 0.0], [2.0, 4.0, 8.0]);
172        assert_eq!(a.size(), [2.0, 4.0, 8.0]);
173        assert_eq!(a.center(), [1.0, 2.0, 4.0]);
174    }
175}