Skip to main content

euca_math/
aabb.rs

1use crate::Vec3;
2use serde::{Deserialize, Serialize};
3
4/// Axis-aligned bounding box.
5#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
6pub struct Aabb {
7    pub min: Vec3,
8    pub max: Vec3,
9}
10
11impl Aabb {
12    /// Create an AABB from min and max corners.
13    #[inline]
14    pub fn new(min: Vec3, max: Vec3) -> Self {
15        Self { min, max }
16    }
17
18    /// Compute the AABB enclosing all given points.
19    /// Returns `None` if the iterator is empty.
20    pub fn from_points(points: impl IntoIterator<Item = Vec3>) -> Option<Self> {
21        let mut iter = points.into_iter();
22        let first = iter.next()?;
23        let mut min = first;
24        let mut max = first;
25        for p in iter {
26            min = Vec3::new(min.x.min(p.x), min.y.min(p.y), min.z.min(p.z));
27            max = Vec3::new(max.x.max(p.x), max.y.max(p.y), max.z.max(p.z));
28        }
29        Some(Self { min, max })
30    }
31
32    /// Center point of the AABB.
33    #[inline]
34    pub fn center(self) -> Vec3 {
35        (self.min + self.max) * 0.5
36    }
37
38    /// Half-extents (size from center to each face).
39    #[inline]
40    pub fn extents(self) -> Vec3 {
41        (self.max - self.min) * 0.5
42    }
43
44    /// Full size of the AABB.
45    #[inline]
46    pub fn size(self) -> Vec3 {
47        self.max - self.min
48    }
49
50    /// Check if a point is inside the AABB.
51    #[inline]
52    pub fn contains_point(self, point: Vec3) -> bool {
53        point.x >= self.min.x
54            && point.x <= self.max.x
55            && point.y >= self.min.y
56            && point.y <= self.max.y
57            && point.z >= self.min.z
58            && point.z <= self.max.z
59    }
60
61    /// Check if two AABBs overlap.
62    #[inline]
63    pub fn intersects(self, other: Self) -> bool {
64        self.min.x <= other.max.x
65            && self.max.x >= other.min.x
66            && self.min.y <= other.max.y
67            && self.max.y >= other.min.y
68            && self.min.z <= other.max.z
69            && self.max.z >= other.min.z
70    }
71
72    /// Return the union of two AABBs (smallest AABB enclosing both).
73    #[inline]
74    pub fn union(self, other: Self) -> Self {
75        Self {
76            min: Vec3::new(
77                self.min.x.min(other.min.x),
78                self.min.y.min(other.min.y),
79                self.min.z.min(other.min.z),
80            ),
81            max: Vec3::new(
82                self.max.x.max(other.max.x),
83                self.max.y.max(other.max.y),
84                self.max.z.max(other.max.z),
85            ),
86        }
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn contains_point() {
96        let aabb = Aabb::new(Vec3::new(-1.0, -1.0, -1.0), Vec3::new(1.0, 1.0, 1.0));
97        assert!(aabb.contains_point(Vec3::ZERO));
98        assert!(aabb.contains_point(Vec3::new(1.0, 1.0, 1.0)));
99        assert!(!aabb.contains_point(Vec3::new(2.0, 0.0, 0.0)));
100    }
101
102    #[test]
103    fn intersects() {
104        let a = Aabb::new(Vec3::new(0.0, 0.0, 0.0), Vec3::new(2.0, 2.0, 2.0));
105        let b = Aabb::new(Vec3::new(1.0, 1.0, 1.0), Vec3::new(3.0, 3.0, 3.0));
106        let c = Aabb::new(Vec3::new(5.0, 5.0, 5.0), Vec3::new(6.0, 6.0, 6.0));
107
108        assert!(a.intersects(b));
109        assert!(!a.intersects(c));
110    }
111
112    #[test]
113    fn from_points() {
114        let points = vec![
115            Vec3::new(1.0, -2.0, 3.0),
116            Vec3::new(-1.0, 4.0, 0.0),
117            Vec3::new(2.0, 0.0, -1.0),
118        ];
119        let aabb = Aabb::from_points(points).unwrap();
120        assert_eq!(aabb.min, Vec3::new(-1.0, -2.0, -1.0));
121        assert_eq!(aabb.max, Vec3::new(2.0, 4.0, 3.0));
122    }
123
124    #[test]
125    fn center_and_extents() {
126        let aabb = Aabb::new(Vec3::new(0.0, 0.0, 0.0), Vec3::new(4.0, 6.0, 8.0));
127        assert_eq!(aabb.center(), Vec3::new(2.0, 3.0, 4.0));
128        assert_eq!(aabb.extents(), Vec3::new(2.0, 3.0, 4.0));
129        assert_eq!(aabb.size(), Vec3::new(4.0, 6.0, 8.0));
130    }
131
132    #[test]
133    fn from_empty_points() {
134        let result = Aabb::from_points(std::iter::empty());
135        assert!(result.is_none());
136    }
137}