1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
use parry2d_f64::bounding_volume::BoundingVolume as _;
use parry3d_f64::bounding_volume::BoundingVolume as _;

use super::{Point, Vector};

/// An axis-aligned bounding box (AABB)
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
#[repr(C)]
pub struct Aabb<const D: usize> {
    /// The minimum coordinates of the AABB
    pub min: Point<D>,

    /// The maximum coordinates of the AABB
    pub max: Point<D>,
}

impl<const D: usize> Aabb<D> {
    /// Determine whether the AABB contains a given point
    pub fn contains(&self, point: impl Into<Point<D>>) -> bool {
        let point = point.into();

        let min = self
            .min
            .coords
            .components
            .into_iter()
            .zip(point.coords.components);
        for (min, p) in min {
            if min > p {
                return false;
            }
        }

        let max = self
            .max
            .coords
            .components
            .into_iter()
            .zip(point.coords.components);
        for (max, p) in max {
            if max < p {
                return false;
            }
        }

        true
    }
}

impl Aabb<2> {
    /// Construct a 2-dimensional AABB from a list of points
    ///
    /// The resulting AABB will contain all the points.
    pub fn from_points(
        points: impl IntoIterator<Item = impl Into<Point<2>>>,
    ) -> Self {
        let points: Vec<_> = points
            .into_iter()
            .map(|point| point.into().to_na())
            .collect();
        parry2d_f64::bounding_volume::Aabb::from_points(&points).into()
    }

    /// Construct a 2-dimensional AABB from a Parry AABB
    pub fn from_parry(aabb: parry2d_f64::bounding_volume::Aabb) -> Self {
        Self {
            min: aabb.mins.into(),
            max: aabb.maxs.into(),
        }
    }

    /// Convert the AABB to a Parry AABB
    pub fn to_parry(self) -> parry2d_f64::bounding_volume::Aabb {
        parry2d_f64::bounding_volume::Aabb {
            mins: self.min.to_na(),
            maxs: self.max.to_na(),
        }
    }

    /// Merge this AABB with another
    pub fn merged(&self, other: &Self) -> Self {
        self.to_parry().merged(&other.to_parry()).into()
    }
}

impl Aabb<3> {
    /// Construct a 3-dimensional AABB from a list of points
    ///
    /// The resulting AABB will contain all the points.
    pub fn from_points(
        points: impl IntoIterator<Item = impl Into<Point<3>>>,
    ) -> Self {
        let points: Vec<_> = points
            .into_iter()
            .map(|point| point.into().to_na())
            .collect();
        parry3d_f64::bounding_volume::Aabb::from_points(&points).into()
    }

    /// Construct a 3-dimensional AABB from a Parry AABB
    pub fn from_parry(aabb: parry3d_f64::bounding_volume::Aabb) -> Self {
        Self {
            min: aabb.mins.into(),
            max: aabb.maxs.into(),
        }
    }

    /// Convert the AABB to a Parry AABB
    pub fn to_parry(self) -> parry3d_f64::bounding_volume::Aabb {
        parry3d_f64::bounding_volume::Aabb {
            mins: self.min.to_na(),
            maxs: self.max.to_na(),
        }
    }

    /// Access the vertices of the AABB
    pub fn vertices(&self) -> [Point<3>; 8] {
        self.to_parry().vertices().map(Into::into)
    }

    /// Compute the center point of the AABB
    pub fn center(&self) -> Point<3> {
        self.to_parry().center().into()
    }

    /// Compute the size of the AABB
    pub fn size(&self) -> Vector<3> {
        self.to_parry().extents().into()
    }

    /// Compute an AABB that includes an additional point
    pub fn include_point(self, point: &Point<3>) -> Self {
        let mut aabb = self.to_parry();
        aabb.take_point(point.to_na());

        Self::from_parry(aabb)
    }

    /// Merge this AABB with another
    pub fn merged(&self, other: &Self) -> Self {
        self.to_parry().merged(&other.to_parry()).into()
    }
}

impl From<parry2d_f64::bounding_volume::Aabb> for Aabb<2> {
    fn from(aabb: parry2d_f64::bounding_volume::Aabb) -> Self {
        Self::from_parry(aabb)
    }
}

impl From<parry3d_f64::bounding_volume::Aabb> for Aabb<3> {
    fn from(aabb: parry3d_f64::bounding_volume::Aabb) -> Self {
        Self::from_parry(aabb)
    }
}

#[cfg(test)]
mod tests {
    use super::Aabb;

    #[test]
    fn contains() {
        let aabb = Aabb::<2>::from_points([[1., 1.], [3., 3.]]);

        assert!(aabb.contains([2., 2.]));

        assert!(!aabb.contains([0., 0.]));
        assert!(!aabb.contains([4., 0.]));
        assert!(!aabb.contains([4., 4.]));
        assert!(!aabb.contains([0., 4.]));

        assert!(!aabb.contains([2., 0.]));
        assert!(!aabb.contains([2., 4.]));
        assert!(!aabb.contains([0., 2.]));
        assert!(!aabb.contains([4., 2.]));
    }
}