fj_math/
aabb.rs

1use parry2d_f64::bounding_volume::BoundingVolume as _;
2use parry3d_f64::bounding_volume::BoundingVolume as _;
3
4use super::{Point, Vector};
5
6/// An axis-aligned bounding box (AABB)
7#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
8#[repr(C)]
9pub struct Aabb<const D: usize> {
10    /// The minimum coordinates of the AABB
11    pub min: Point<D>,
12
13    /// The maximum coordinates of the AABB
14    pub max: Point<D>,
15}
16
17impl<const D: usize> Aabb<D> {
18    /// Determine whether the AABB contains a given point
19    pub fn contains(&self, point: impl Into<Point<D>>) -> bool {
20        let point = point.into();
21
22        let min = self
23            .min
24            .coords
25            .components
26            .into_iter()
27            .zip(point.coords.components);
28        for (min, p) in min {
29            if min > p {
30                return false;
31            }
32        }
33
34        let max = self
35            .max
36            .coords
37            .components
38            .into_iter()
39            .zip(point.coords.components);
40        for (max, p) in max {
41            if max < p {
42                return false;
43            }
44        }
45
46        true
47    }
48}
49
50impl Aabb<2> {
51    /// Construct a 2-dimensional AABB from a list of points
52    ///
53    /// The resulting AABB will contain all the points.
54    pub fn from_points(
55        points: impl IntoIterator<Item = impl Into<Point<2>>>,
56    ) -> Self {
57        let points: Vec<_> = points
58            .into_iter()
59            .map(|point| point.into().to_na())
60            .collect();
61        parry2d_f64::bounding_volume::Aabb::from_points(&points).into()
62    }
63
64    /// Construct a 2-dimensional AABB from a Parry AABB
65    pub fn from_parry(aabb: parry2d_f64::bounding_volume::Aabb) -> Self {
66        Self {
67            min: aabb.mins.into(),
68            max: aabb.maxs.into(),
69        }
70    }
71
72    /// Convert the AABB to a Parry AABB
73    pub fn to_parry(self) -> parry2d_f64::bounding_volume::Aabb {
74        parry2d_f64::bounding_volume::Aabb {
75            mins: self.min.to_na(),
76            maxs: self.max.to_na(),
77        }
78    }
79
80    /// Merge this AABB with another
81    pub fn merged(&self, other: &Self) -> Self {
82        self.to_parry().merged(&other.to_parry()).into()
83    }
84}
85
86impl Aabb<3> {
87    /// Construct a 3-dimensional AABB from a list of points
88    ///
89    /// The resulting AABB will contain all the points.
90    pub fn from_points(
91        points: impl IntoIterator<Item = impl Into<Point<3>>>,
92    ) -> Self {
93        let points: Vec<_> = points
94            .into_iter()
95            .map(|point| point.into().to_na())
96            .collect();
97        parry3d_f64::bounding_volume::Aabb::from_points(&points).into()
98    }
99
100    /// Construct a 3-dimensional AABB from a Parry AABB
101    pub fn from_parry(aabb: parry3d_f64::bounding_volume::Aabb) -> Self {
102        Self {
103            min: aabb.mins.into(),
104            max: aabb.maxs.into(),
105        }
106    }
107
108    /// Convert the AABB to a Parry AABB
109    pub fn to_parry(self) -> parry3d_f64::bounding_volume::Aabb {
110        parry3d_f64::bounding_volume::Aabb {
111            mins: self.min.to_na(),
112            maxs: self.max.to_na(),
113        }
114    }
115
116    /// Access the vertices of the AABB
117    pub fn vertices(&self) -> [Point<3>; 8] {
118        self.to_parry().vertices().map(Into::into)
119    }
120
121    /// Compute the center point of the AABB
122    pub fn center(&self) -> Point<3> {
123        self.to_parry().center().into()
124    }
125
126    /// Compute the size of the AABB
127    pub fn size(&self) -> Vector<3> {
128        self.to_parry().extents().into()
129    }
130
131    /// Compute an AABB that includes an additional point
132    pub fn include_point(self, point: &Point<3>) -> Self {
133        let mut aabb = self.to_parry();
134        aabb.take_point(point.to_na());
135
136        Self::from_parry(aabb)
137    }
138
139    /// Merge this AABB with another
140    pub fn merged(&self, other: &Self) -> Self {
141        self.to_parry().merged(&other.to_parry()).into()
142    }
143}
144
145impl From<parry2d_f64::bounding_volume::Aabb> for Aabb<2> {
146    fn from(aabb: parry2d_f64::bounding_volume::Aabb) -> Self {
147        Self::from_parry(aabb)
148    }
149}
150
151impl From<parry3d_f64::bounding_volume::Aabb> for Aabb<3> {
152    fn from(aabb: parry3d_f64::bounding_volume::Aabb) -> Self {
153        Self::from_parry(aabb)
154    }
155}
156
157#[cfg(test)]
158mod tests {
159    use super::Aabb;
160
161    #[test]
162    fn contains() {
163        let aabb = Aabb::<2>::from_points([[1., 1.], [3., 3.]]);
164
165        assert!(aabb.contains([2., 2.]));
166
167        assert!(!aabb.contains([0., 0.]));
168        assert!(!aabb.contains([4., 0.]));
169        assert!(!aabb.contains([4., 4.]));
170        assert!(!aabb.contains([0., 4.]));
171
172        assert!(!aabb.contains([2., 0.]));
173        assert!(!aabb.contains([2., 4.]));
174        assert!(!aabb.contains([0., 2.]));
175        assert!(!aabb.contains([4., 2.]));
176    }
177}