1use crate::{
2 algebra::{Matrix4, Vector3},
3 math::Matrix4Ext,
4 visitor::{Visit, VisitResult, Visitor},
5};
6
7#[derive(Copy, Clone, Debug)]
8pub struct AxisAlignedBoundingBox {
9 pub min: Vector3<f32>,
10 pub max: Vector3<f32>,
11}
12
13impl Default for AxisAlignedBoundingBox {
14 #[inline]
15 fn default() -> Self {
16 Self {
17 min: Vector3::new(f32::MAX, f32::MAX, f32::MAX),
18 max: Vector3::new(-f32::MAX, -f32::MAX, -f32::MAX),
19 }
20 }
21}
22
23impl AxisAlignedBoundingBox {
24 #[inline]
25 pub const fn unit() -> Self {
26 Self::from_min_max(Vector3::new(-0.5, -0.5, -0.5), Vector3::new(0.5, 0.5, 0.5))
27 }
28
29 #[inline]
30 pub fn from_radius(radius: f32) -> Self {
31 Self {
32 min: Vector3::new(-radius, -radius, -radius),
33 max: Vector3::new(radius, radius, radius),
34 }
35 }
36
37 #[inline]
38 pub const fn from_min_max(min: Vector3<f32>, max: Vector3<f32>) -> Self {
39 Self { min, max }
40 }
41
42 #[inline]
43 pub fn from_points(points: &[Vector3<f32>]) -> Self {
44 let mut aabb = AxisAlignedBoundingBox::default();
45 for pt in points {
46 aabb.add_point(*pt);
47 }
48 aabb
49 }
50
51 #[inline]
52 pub fn add_point(&mut self, a: Vector3<f32>) {
53 if a.x < self.min.x {
54 self.min.x = a.x;
55 }
56 if a.y < self.min.y {
57 self.min.y = a.y;
58 }
59 if a.z < self.min.z {
60 self.min.z = a.z;
61 }
62
63 if a.x > self.max.x {
64 self.max.x = a.x;
65 }
66 if a.y > self.max.y {
67 self.max.y = a.y;
68 }
69 if a.z > self.max.z {
70 self.max.z = a.z;
71 }
72 }
73
74 #[inline]
75 pub fn inflate(&mut self, delta: Vector3<f32>) {
76 self.min -= delta.scale(0.5);
77 self.max += delta.scale(0.5);
78 }
79
80 #[inline]
81 pub fn add_box(&mut self, other: Self) {
82 self.add_point(other.min);
83 self.add_point(other.max);
84 }
85
86 #[inline]
87 pub fn corners(&self) -> [Vector3<f32>; 8] {
88 [
89 Vector3::new(self.min.x, self.min.y, self.min.z),
90 Vector3::new(self.min.x, self.min.y, self.max.z),
91 Vector3::new(self.max.x, self.min.y, self.max.z),
92 Vector3::new(self.max.x, self.min.y, self.min.z),
93 Vector3::new(self.min.x, self.max.y, self.min.z),
94 Vector3::new(self.min.x, self.max.y, self.max.z),
95 Vector3::new(self.max.x, self.max.y, self.max.z),
96 Vector3::new(self.max.x, self.max.y, self.min.z),
97 ]
98 }
99
100 #[inline]
101 pub fn offset(&mut self, v: Vector3<f32>) {
102 self.min += v;
103 self.max += v;
104 }
105
106 #[inline]
107 pub fn center(&self) -> Vector3<f32> {
108 (self.max + self.min).scale(0.5)
109 }
110
111 #[inline]
112 pub fn half_extents(&self) -> Vector3<f32> {
113 (self.max - self.min).scale(0.5)
114 }
115
116 #[inline]
117 pub fn invalidate(&mut self) {
118 *self = Default::default();
119 }
120
121 #[inline]
122 pub fn is_contains_point(&self, point: Vector3<f32>) -> bool {
123 point.x >= self.min.x
124 && point.x <= self.max.x
125 && point.y >= self.min.y
126 && point.y <= self.max.y
127 && point.z >= self.min.z
128 && point.z <= self.max.z
129 }
130
131 #[inline]
132 pub fn is_intersects_sphere(&self, position: Vector3<f32>, radius: f32) -> bool {
133 let r2 = radius.powi(2);
134 let mut dmin = 0.0;
135
136 if position.x < self.min.x {
137 dmin += (position.x - self.min.x).powi(2);
138 } else if position.x > self.max.x {
139 dmin += (position.x - self.max.x).powi(2);
140 }
141
142 if position.y < self.min.y {
143 dmin += (position.y - self.min.y).powi(2);
144 } else if position.y > self.max.y {
145 dmin += (position.y - self.max.y).powi(2);
146 }
147
148 if position.z < self.min.z {
149 dmin += (position.z - self.min.z).powi(2);
150 } else if position.z > self.max.z {
151 dmin += (position.z - self.max.z).powi(2);
152 }
153
154 dmin <= r2
155 || ((position.x >= self.min.x)
156 && (position.x <= self.max.x)
157 && (position.y >= self.min.y)
158 && (position.y <= self.max.y)
159 && (position.z >= self.min.z)
160 && (position.z <= self.max.z))
161 }
162
163 #[inline]
164 pub fn intersect_aabb(&self, other: &Self) -> bool {
165 let self_center = self.center();
166 let self_half_extents = self.half_extents();
167
168 let other_half_extents = other.half_extents();
169 let other_center = other.center();
170
171 if (self_center.x - other_center.x).abs() > (self_half_extents.x + other_half_extents.x) {
172 return false;
173 }
174
175 if (self_center.y - other_center.y).abs() > (self_half_extents.y + other_half_extents.y) {
176 return false;
177 }
178
179 if (self_center.z - other_center.z).abs() > (self_half_extents.z + other_half_extents.z) {
180 return false;
181 }
182
183 true
184 }
185
186 #[inline]
192 #[must_use]
193 pub fn transform(&self, m: &Matrix4<f32>) -> AxisAlignedBoundingBox {
194 let basis = m.basis();
195
196 let mut transformed = Self {
197 min: m.position(),
198 max: m.position(),
199 };
200
201 for i in 0..3 {
202 for j in 0..3 {
203 let a = basis[(i, j)] * self.min[j];
204 let b = basis[(i, j)] * self.max[j];
205 if a < b {
206 transformed.min[i] += a;
207 transformed.max[i] += b;
208 } else {
209 transformed.min[i] += b;
210 transformed.max[i] += a;
211 }
212 }
213 }
214
215 transformed
216 }
217
218 #[inline]
219 pub fn split(&self) -> [AxisAlignedBoundingBox; 8] {
220 let center = self.center();
221 let min = &self.min;
222 let max = &self.max;
223 [
224 AxisAlignedBoundingBox::from_min_max(
225 Vector3::new(min.x, min.y, min.z),
226 Vector3::new(center.x, center.y, center.z),
227 ),
228 AxisAlignedBoundingBox::from_min_max(
229 Vector3::new(center.x, min.y, min.z),
230 Vector3::new(max.x, center.y, center.z),
231 ),
232 AxisAlignedBoundingBox::from_min_max(
233 Vector3::new(min.x, min.y, center.z),
234 Vector3::new(center.x, center.y, max.z),
235 ),
236 AxisAlignedBoundingBox::from_min_max(
237 Vector3::new(center.x, min.y, center.z),
238 Vector3::new(max.x, center.y, max.z),
239 ),
240 AxisAlignedBoundingBox::from_min_max(
241 Vector3::new(min.x, center.y, min.z),
242 Vector3::new(center.x, max.y, center.z),
243 ),
244 AxisAlignedBoundingBox::from_min_max(
245 Vector3::new(center.x, center.y, min.z),
246 Vector3::new(max.x, max.y, center.z),
247 ),
248 AxisAlignedBoundingBox::from_min_max(
249 Vector3::new(min.x, center.y, center.z),
250 Vector3::new(center.x, max.y, max.z),
251 ),
252 AxisAlignedBoundingBox::from_min_max(
253 Vector3::new(center.x, center.y, center.z),
254 Vector3::new(max.x, max.y, max.z),
255 ),
256 ]
257 }
258}
259
260impl Visit for AxisAlignedBoundingBox {
261 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
262 visitor.enter_region(name)?;
263
264 self.min.visit("Min", visitor)?;
265 self.max.visit("Max", visitor)?;
266
267 visitor.leave_region()
268 }
269}
270
271#[cfg(test)]
272mod test {
273 use crate::algebra::{Matrix4, Vector3};
274 use crate::math::aabb::AxisAlignedBoundingBox;
275
276 #[test]
277 fn test_aabb_transform() {
278 let aabb = AxisAlignedBoundingBox {
279 min: Vector3::new(0.0, 0.0, 0.0),
280 max: Vector3::new(1.0, 1.0, 1.0),
281 };
282
283 let transform = Matrix4::new_translation(&Vector3::new(1.0, 1.0, 1.0))
284 * Matrix4::new_nonuniform_scaling(&Vector3::new(2.0, 2.0, 2.0));
285
286 let transformed_aabb = aabb.transform(&transform);
287
288 assert_eq!(transformed_aabb.min, Vector3::new(1.0, 1.0, 1.0));
289 assert_eq!(transformed_aabb.max, Vector3::new(3.0, 3.0, 3.0));
290 }
291}