1use crate::{Mat4, Vec3};
2
3#[derive(Clone, Copy, Debug, PartialEq)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6pub struct Aabb {
7 pub min: Vec3,
9 pub max: Vec3,
11}
12
13#[derive(Clone, Copy, Debug, PartialEq)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16pub struct Sphere {
17 pub center: Vec3,
19 pub radius: f32,
21}
22
23impl Aabb {
24 pub const ZERO: Self = Self::new(Vec3::ZERO, Vec3::ZERO);
26
27 #[inline]
29 pub const fn new(min: Vec3, max: Vec3) -> Self {
30 Self { min, max }
31 }
32
33 pub fn from_points(points: &[Vec3]) -> Self {
35 let Some((first, rest)) = points.split_first() else {
36 return Self::ZERO;
37 };
38 let mut min = *first;
39 let mut max = *first;
40 for point in rest {
41 min.x = min.x.min(point.x);
42 min.y = min.y.min(point.y);
43 min.z = min.z.min(point.z);
44 max.x = max.x.max(point.x);
45 max.y = max.y.max(point.y);
46 max.z = max.z.max(point.z);
47 }
48 Self::new(min, max)
49 }
50
51 #[inline]
53 pub fn center(self) -> Vec3 {
54 (self.min + self.max) * 0.5
55 }
56
57 #[inline]
59 pub fn half_extents(self) -> Vec3 {
60 (self.max - self.min) * 0.5
61 }
62
63 #[inline]
65 pub fn contains_point(self, point: Vec3) -> bool {
66 point.x >= self.min.x
67 && point.y >= self.min.y
68 && point.z >= self.min.z
69 && point.x <= self.max.x
70 && point.y <= self.max.y
71 && point.z <= self.max.z
72 }
73
74 #[inline]
76 pub fn intersects_aabb(self, other: Self) -> bool {
77 self.min.x <= other.max.x
78 && self.max.x >= other.min.x
79 && self.min.y <= other.max.y
80 && self.max.y >= other.min.y
81 && self.min.z <= other.max.z
82 && self.max.z >= other.min.z
83 }
84
85 #[inline]
87 pub fn merge(self, other: Self) -> Self {
88 Self::new(
89 Vec3::new(
90 self.min.x.min(other.min.x),
91 self.min.y.min(other.min.y),
92 self.min.z.min(other.min.z),
93 ),
94 Vec3::new(
95 self.max.x.max(other.max.x),
96 self.max.y.max(other.max.y),
97 self.max.z.max(other.max.z),
98 ),
99 )
100 }
101
102 pub fn transform(self, matrix: Mat4) -> Self {
104 let min = self.min;
105 let max = self.max;
106 let corners = [
107 Vec3::new(min.x, min.y, min.z),
108 Vec3::new(max.x, min.y, min.z),
109 Vec3::new(min.x, max.y, min.z),
110 Vec3::new(max.x, max.y, min.z),
111 Vec3::new(min.x, min.y, max.z),
112 Vec3::new(max.x, min.y, max.z),
113 Vec3::new(min.x, max.y, max.z),
114 Vec3::new(max.x, max.y, max.z),
115 ];
116 let mut transformed = [Vec3::ZERO; 8];
117 for (out, corner) in transformed.iter_mut().zip(corners) {
118 *out = matrix.mul_vec3(corner);
119 }
120 Self::from_points(&transformed)
121 }
122
123 #[inline]
125 pub fn surface_area(self) -> f32 {
126 let extents = self.max - self.min;
127 let x = extents.x.max(0.0);
128 let y = extents.y.max(0.0);
129 let z = extents.z.max(0.0);
130 2.0 * (x * y + y * z + z * x)
131 }
132
133 #[inline]
135 pub fn bounding_sphere(self) -> Sphere {
136 let center = self.center();
137 Sphere::new(center, self.max.distance(center))
138 }
139}
140
141impl Default for Aabb {
142 #[inline]
143 fn default() -> Self {
144 Self::ZERO
145 }
146}
147
148impl Sphere {
149 #[inline]
151 pub const fn new(center: Vec3, radius: f32) -> Self {
152 Self { center, radius }
153 }
154
155 #[inline]
157 pub fn contains_point(self, point: Vec3) -> bool {
158 point.distance(self.center) <= self.radius
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[test]
167 fn aabb_from_points_handles_empty_and_extents() {
168 assert_eq!(Aabb::from_points(&[]), Aabb::ZERO);
169 let aabb = Aabb::from_points(&[Vec3::new(-1.0, 2.0, 0.0), Vec3::new(3.0, -2.0, 1.0)]);
170 assert_eq!(aabb.min, Vec3::new(-1.0, -2.0, 0.0));
171 assert_eq!(aabb.max, Vec3::new(3.0, 2.0, 1.0));
172 assert_eq!(aabb.center(), Vec3::new(1.0, 0.0, 0.5));
173 assert_eq!(aabb.half_extents(), Vec3::new(2.0, 2.0, 0.5));
174 }
175
176 #[test]
177 fn aabb_contains_intersects_merges_and_measures_area() {
178 let a = Aabb::new(Vec3::ZERO, Vec3::new(1.0, 1.0, 1.0));
179 let b = Aabb::new(Vec3::new(0.5, 0.5, 0.5), Vec3::new(2.0, 2.0, 2.0));
180 assert!(a.contains_point(Vec3::new(0.5, 0.5, 0.5)));
181 assert!(a.intersects_aabb(b));
182 assert_eq!(a.merge(b).max, Vec3::new(2.0, 2.0, 2.0));
183 assert_eq!(a.surface_area(), 6.0);
184 }
185}