1use super::{Mat4, Vec3, Vec4, EPSILON};
21
22#[derive(Debug, Clone, Copy, PartialEq)]
28#[repr(C)]
29pub struct Aabb {
30 pub min: Vec3,
32 pub max: Vec3,
34}
35
36impl Aabb {
37 pub const INVALID: Self = Self {
42 min: Vec3::new(f32::INFINITY, f32::INFINITY, f32::INFINITY),
43 max: Vec3::new(f32::NEG_INFINITY, f32::NEG_INFINITY, f32::NEG_INFINITY),
44 };
45
46 #[inline]
52 pub fn from_min_max(min_pt: Vec3, max_pt: Vec3) -> Self {
53 Self {
54 min: Vec3::new(
55 min_pt.x.min(max_pt.x),
56 min_pt.y.min(max_pt.y),
57 min_pt.z.min(max_pt.z),
58 ),
59 max: Vec3::new(
60 min_pt.x.max(max_pt.x),
61 min_pt.y.max(max_pt.y),
62 min_pt.z.max(max_pt.z),
63 ),
64 }
65 }
66
67 #[inline]
72 pub fn from_center_half_extents(center: Vec3, half_extents: Vec3) -> Self {
73 let safe_half_extents = Vec3::new(
74 half_extents.x.abs(),
75 half_extents.y.abs(),
76 half_extents.z.abs(),
77 ); Self {
79 min: center - safe_half_extents,
80 max: center + safe_half_extents,
81 }
82 }
83
84 #[inline]
86 pub fn from_point(point: Vec3) -> Self {
87 Self {
88 min: point,
89 max: point,
90 }
91 }
92
93 pub fn from_points(points: &[Vec3]) -> Option<Self> {
99 if points.is_empty() {
100 return None;
101 }
102
103 let mut min_pt = points[0];
104 let mut max_pt = points[0];
105
106 for point in points.iter().skip(1) {
107 min_pt.x = min_pt.x.min(point.x);
108 min_pt.y = min_pt.y.min(point.y);
109 min_pt.z = min_pt.z.min(point.z);
110
111 max_pt.x = max_pt.x.max(point.x);
112 max_pt.y = max_pt.y.max(point.y);
113 max_pt.z = max_pt.z.max(point.z);
114 }
115
116 Some(Self {
117 min: min_pt,
118 max: max_pt,
119 })
120 }
121
122 #[inline]
124 pub fn center(&self) -> Vec3 {
125 (self.min + self.max) * 0.5
126 }
127
128 #[inline]
130 pub fn half_extents(&self) -> Vec3 {
131 (self.max - self.min) * 0.5
132 }
133
134 #[inline]
136 pub fn size(&self) -> Vec3 {
137 self.max - self.min
138 }
139
140 #[inline]
143 pub fn is_valid(&self) -> bool {
144 self.min.x <= self.max.x && self.min.y <= self.max.y && self.min.z <= self.max.z
145 }
146
147 #[inline]
149 pub fn contains_point(&self, point: Vec3) -> bool {
150 point.x >= self.min.x
151 && point.x <= self.max.x
152 && point.y >= self.min.y
153 && point.y <= self.max.y
154 && point.z >= self.min.z
155 && point.z <= self.max.z
156 }
157
158 #[inline]
163 pub fn intersects_aabb(&self, other: &Aabb) -> bool {
164 (self.min.x <= other.max.x && self.max.x >= other.min.x)
165 && (self.min.y <= other.max.y && self.max.y >= other.min.y)
166 && (self.min.z <= other.max.z && self.max.z >= other.min.z)
167 }
168
169 #[inline]
171 pub fn merge(&self, other: &Aabb) -> Self {
172 Self {
173 min: Vec3::new(
174 self.min.x.min(other.min.x),
175 self.min.y.min(other.min.y),
176 self.min.z.min(other.min.z),
177 ),
178 max: Vec3::new(
179 self.max.x.max(other.max.x),
180 self.max.y.max(other.max.y),
181 self.max.z.max(other.max.z),
182 ),
183 }
184 }
185
186 #[inline]
188 pub fn merged_with_point(&self, point: Vec3) -> Self {
189 Self {
190 min: Vec3::new(
191 self.min.x.min(point.x),
192 self.min.y.min(point.y),
193 self.min.z.min(point.z),
194 ),
195 max: Vec3::new(
196 self.max.x.max(point.x),
197 self.max.y.max(point.y),
198 self.max.z.max(point.z),
199 ),
200 }
201 }
202
203 pub fn transform(&self, matrix: &Mat4) -> Self {
214 let center = self.center();
215 let half_extents = self.half_extents();
216 let transformed_center_v4 = *matrix * Vec4::from_vec3(center, 1.0);
217
218 let transformed_center = if (transformed_center_v4.w - 1.0).abs() > EPSILON
219 && transformed_center_v4.w.abs() > EPSILON
220 {
221 transformed_center_v4.truncate() / transformed_center_v4.w
222 } else {
223 transformed_center_v4.truncate()
224 };
225
226 let x_abs_col = Vec3::new(
227 matrix.cols[0][0].abs(),
228 matrix.cols[0][1].abs(),
229 matrix.cols[0][2].abs(),
230 );
231 let y_abs_col = Vec3::new(
232 matrix.cols[1][0].abs(),
233 matrix.cols[1][1].abs(),
234 matrix.cols[1][2].abs(),
235 );
236 let z_abs_col = Vec3::new(
237 matrix.cols[2][0].abs(),
238 matrix.cols[2][1].abs(),
239 matrix.cols[2][2].abs(),
240 );
241
242 let new_half_extents =
243 x_abs_col * half_extents.x + y_abs_col * half_extents.y + z_abs_col * half_extents.z;
244
245 Aabb::from_center_half_extents(transformed_center, new_half_extents)
246 }
247}
248
249impl Default for Aabb {
250 #[inline]
252 fn default() -> Self {
253 Self::INVALID
254 }
255}
256
257#[cfg(test)]
259mod tests {
260 use super::*;
261 use crate::math::{approx_eq, matrix::Mat4, vector::Vec3}; use std::f32::consts::PI;
263
264 fn vec3_approx_eq(a: Vec3, b: Vec3) -> bool {
265 approx_eq(a.x, b.x) && approx_eq(a.y, b.y) && approx_eq(a.z, b.z)
266 }
267
268 fn aabb_approx_eq(a: Aabb, b: Aabb) -> bool {
270 vec3_approx_eq(a.min, b.min) && vec3_approx_eq(a.max, b.max)
271 }
272
273 #[test]
274 fn test_aabb_from_min_max() {
275 let aabb = Aabb::from_min_max(Vec3::new(1.0, 2.0, 3.0), Vec3::new(4.0, 5.0, 6.0));
276 assert_eq!(aabb.min, Vec3::new(1.0, 2.0, 3.0));
277 assert_eq!(aabb.max, Vec3::new(4.0, 5.0, 6.0));
278
279 let aabb_swapped = Aabb::from_min_max(Vec3::new(4.0, 5.0, 6.0), Vec3::new(1.0, 2.0, 3.0));
281 assert_eq!(aabb_swapped.min, Vec3::new(1.0, 2.0, 3.0));
282 assert_eq!(aabb_swapped.max, Vec3::new(4.0, 5.0, 6.0));
283 }
284
285 #[test]
286 fn test_aabb_from_center_half_extents() {
287 let center = Vec3::new(10.0, 20.0, 30.0);
288 let half_extents = Vec3::new(1.0, 2.0, 3.0);
289 let aabb = Aabb::from_center_half_extents(center, half_extents);
290
291 assert_eq!(aabb.min, Vec3::new(9.0, 18.0, 27.0));
292 assert_eq!(aabb.max, Vec3::new(11.0, 22.0, 33.0));
293 assert!(aabb_approx_eq(aabb, Aabb::from_min_max(aabb.min, aabb.max)));
294 }
295
296 #[test]
297 fn test_aabb_from_point() {
298 let p = Vec3::new(5.0, 6.0, 7.0);
299 let aabb = Aabb::from_point(p);
300
301 assert_eq!(aabb.min, p);
302 assert_eq!(aabb.max, p);
303 assert!(aabb.is_valid());
304 }
305
306 #[test]
307 fn test_aabb_from_points() {
308 assert!(Aabb::from_points(&[]).is_none());
309
310 let points = [
311 Vec3::new(1.0, 5.0, -1.0),
312 Vec3::new(0.0, 2.0, 3.0),
313 Vec3::new(4.0, 8.0, 0.0),
314 ];
315 let aabb = Aabb::from_points(&points).unwrap();
316
317 assert_eq!(aabb.min, Vec3::new(0.0, 2.0, -1.0));
318 assert_eq!(aabb.max, Vec3::new(4.0, 8.0, 3.0));
319 }
320
321 #[test]
322 fn test_aabb_utils() {
323 let aabb = Aabb::from_min_max(Vec3::new(-1.0, 0.0, 1.0), Vec3::new(3.0, 2.0, 5.0));
324
325 assert!(vec3_approx_eq(aabb.center(), Vec3::new(1.0, 1.0, 3.0)));
326 assert!(vec3_approx_eq(aabb.size(), Vec3::new(4.0, 2.0, 4.0)));
327 assert!(vec3_approx_eq(
328 aabb.half_extents(),
329 Vec3::new(2.0, 1.0, 2.0)
330 ));
331 assert!(aabb.is_valid());
332 assert!(!Aabb::INVALID.is_valid());
333 assert!(Aabb::from_point(Vec3::ZERO).is_valid());
334 }
335
336 #[test]
337 fn test_aabb_contains_point() {
338 let aabb = Aabb::from_min_max(Vec3::new(0.0, 0.0, 0.0), Vec3::new(1.0, 1.0, 1.0));
339 assert!(aabb.contains_point(Vec3::new(0.5, 0.5, 0.5)));
341
342 assert!(aabb.contains_point(Vec3::new(0.0, 0.5, 0.5)));
344 assert!(aabb.contains_point(Vec3::new(1.0, 0.5, 0.5)));
345 assert!(aabb.contains_point(Vec3::new(0.5, 0.0, 0.5)));
346 assert!(aabb.contains_point(Vec3::new(0.5, 1.0, 0.5)));
347 assert!(aabb.contains_point(Vec3::new(0.5, 0.5, 0.0)));
348 assert!(aabb.contains_point(Vec3::new(0.5, 0.5, 1.0)));
349 assert!(aabb.contains_point(Vec3::new(0.0, 0.0, 0.0)));
350 assert!(aabb.contains_point(Vec3::new(1.0, 1.0, 1.0)));
351
352 assert!(!aabb.contains_point(Vec3::new(1.1, 0.5, 0.5)));
354 assert!(!aabb.contains_point(Vec3::new(-0.1, 0.5, 0.5)));
355 assert!(!aabb.contains_point(Vec3::new(0.5, 1.1, 0.5)));
356 assert!(!aabb.contains_point(Vec3::new(0.5, -0.1, 0.5)));
357 assert!(!aabb.contains_point(Vec3::new(0.5, 0.5, 1.1)));
358 assert!(!aabb.contains_point(Vec3::new(0.5, 0.5, -0.1)));
359 }
360
361 #[test]
362 fn test_aabb_intersects_aabb() {
363 let aabb1 = Aabb::from_min_max(Vec3::new(0.0, 0.0, 0.0), Vec3::new(2.0, 2.0, 2.0));
364
365 let aabb2 = Aabb::from_min_max(Vec3::new(0.0, 0.0, 0.0), Vec3::new(2.0, 2.0, 2.0));
367 assert!(aabb1.intersects_aabb(&aabb2));
368
369 let aabb3 = Aabb::from_min_max(Vec3::new(1.0, 1.0, 1.0), Vec3::new(3.0, 3.0, 3.0));
371 assert!(aabb1.intersects_aabb(&aabb3));
372 assert!(aabb3.intersects_aabb(&aabb1));
373
374 let aabb4 = Aabb::from_min_max(Vec3::new(2.0, 0.0, 0.0), Vec3::new(3.0, 2.0, 2.0));
376 assert!(aabb1.intersects_aabb(&aabb4));
377 assert!(aabb4.intersects_aabb(&aabb1));
378
379 let aabb5 = Aabb::from_min_max(Vec3::new(0.5, 0.5, 0.5), Vec3::new(1.5, 1.5, 1.5));
381 assert!(aabb1.intersects_aabb(&aabb5));
382 assert!(aabb5.intersects_aabb(&aabb1));
383
384 let aabb6 = Aabb::from_min_max(Vec3::new(2.1, 0.0, 0.0), Vec3::new(3.0, 2.0, 2.0));
386 assert!(!aabb1.intersects_aabb(&aabb6));
387 assert!(!aabb6.intersects_aabb(&aabb1));
388
389 let aabb7 = Aabb::from_min_max(Vec3::new(0.0, 2.1, 0.0), Vec3::new(2.0, 3.0, 2.0));
391 assert!(!aabb1.intersects_aabb(&aabb7));
392 assert!(!aabb7.intersects_aabb(&aabb1));
393
394 let aabb8 = Aabb::from_min_max(Vec3::new(0.0, 0.0, 2.1), Vec3::new(2.0, 2.0, 3.0));
396 assert!(!aabb1.intersects_aabb(&aabb8));
397 assert!(!aabb8.intersects_aabb(&aabb1));
398 }
399
400 #[test]
401 fn test_aabb_merge() {
402 let aabb1 = Aabb::from_min_max(Vec3::new(0.0, 0.0, 0.0), Vec3::new(1.0, 1.0, 1.0));
403 let aabb2 = Aabb::from_min_max(Vec3::new(0.5, 0.5, 0.5), Vec3::new(1.5, 1.5, 1.5));
404 let merged_aabb = aabb1.merge(&aabb2);
405
406 assert_eq!(merged_aabb.min, Vec3::new(0.0, 0.0, 0.0));
407 assert_eq!(merged_aabb.max, Vec3::new(1.5, 1.5, 1.5));
408
409 let point = Vec3::new(-1.0, 0.5, 2.0);
410 let merged_point = aabb1.merged_with_point(point);
411
412 assert_eq!(merged_point.min, Vec3::new(-1.0, 0.0, 0.0));
413 assert_eq!(merged_point.max, Vec3::new(1.0, 1.0, 2.0));
414
415 let merged_with_invalid = Aabb::INVALID.merge(&aabb1);
417 assert!(aabb_approx_eq(merged_with_invalid, aabb1));
418
419 let merged_with_invalid_pt = Aabb::INVALID.merged_with_point(point);
420 assert!(aabb_approx_eq(
421 merged_with_invalid_pt,
422 Aabb::from_point(point)
423 ));
424 }
425
426 #[test]
427 fn test_aabb_transform() {
428 let aabb = Aabb::from_min_max(Vec3::new(-1.0, -1.0, -1.0), Vec3::new(1.0, 1.0, 1.0)); let matrix = Mat4::from_translation(Vec3::new(10.0, 0.0, 0.0)); let transformed_aabb = aabb.transform(&matrix);
431 let expected_aabb =
432 Aabb::from_min_max(Vec3::new(9.0, -1.0, -1.0), Vec3::new(11.0, 1.0, 1.0));
433
434 assert!(aabb_approx_eq(transformed_aabb, expected_aabb));
435
436 let matrix_rot = Mat4::from_rotation_y(PI / 4.0); let transformed_rot_aabb = aabb.transform(&matrix_rot);
439
440 let sqrt2 = 2.0f32.sqrt();
443
444 assert!(approx_eq(transformed_rot_aabb.min.x, -sqrt2));
445 assert!(approx_eq(transformed_rot_aabb.max.x, sqrt2));
446 assert!(approx_eq(transformed_rot_aabb.min.y, -1.0)); assert!(approx_eq(transformed_rot_aabb.max.y, 1.0));
448 assert!(approx_eq(transformed_rot_aabb.min.z, -sqrt2));
449 assert!(approx_eq(transformed_rot_aabb.max.z, sqrt2));
450
451 let matrix_scale = Mat4::from_scale(Vec3::new(2.0, 1.0, 0.5));
453 let transformed_scale_aabb = aabb.transform(&matrix_scale);
454 let expected_scale_aabb =
455 Aabb::from_min_max(Vec3::new(-2.0, -1.0, -0.5), Vec3::new(2.0, 1.0, 0.5));
456
457 assert!(aabb_approx_eq(transformed_scale_aabb, expected_scale_aabb));
458 }
459}