1use super::{Mat4, Vec2, Vec3, Vec4, EPSILON};
21
22#[derive(Debug, Clone, Copy, PartialEq, Default)]
29#[repr(C)]
30pub struct Rect2D {
31 pub min: Vec2,
33 pub max: Vec2,
35}
36
37impl Rect2D {
38 pub const ZERO: Self = Self {
40 min: Vec2::ZERO,
41 max: Vec2::ZERO,
42 };
43
44 pub const fn from_min_max(min: Vec2, max: Vec2) -> Self {
46 Self { min, max }
47 }
48
49 pub const fn from_min_size(min: Vec2, size: Vec2) -> Self {
51 Self {
52 min,
53 max: Vec2::new(min.x + size.x, min.y + size.y),
54 }
55 }
56
57 pub fn from_center_size(center: Vec2, size: Vec2) -> Self {
59 let half = Vec2::new(size.x * 0.5, size.y * 0.5);
60 Self {
61 min: Vec2::new(center.x - half.x, center.y - half.y),
62 max: Vec2::new(center.x + half.x, center.y + half.y),
63 }
64 }
65
66 pub fn width(&self) -> f32 {
68 self.max.x - self.min.x
69 }
70
71 pub fn height(&self) -> f32 {
73 self.max.y - self.min.y
74 }
75
76 pub fn size(&self) -> Vec2 {
78 Vec2::new(self.width(), self.height())
79 }
80
81 pub fn center(&self) -> Vec2 {
83 Vec2::new(
84 (self.min.x + self.max.x) * 0.5,
85 (self.min.y + self.max.y) * 0.5,
86 )
87 }
88
89 pub fn contains(&self, point: Vec2) -> bool {
92 point.x >= self.min.x
93 && point.x < self.max.x
94 && point.y >= self.min.y
95 && point.y < self.max.y
96 }
97}
98
99#[derive(Debug, Clone, Copy, PartialEq)]
105#[repr(C)]
106pub struct Aabb {
107 pub min: Vec3,
109 pub max: Vec3,
111}
112
113impl Aabb {
114 pub const INVALID: Self = Self {
119 min: Vec3::new(f32::INFINITY, f32::INFINITY, f32::INFINITY),
120 max: Vec3::new(f32::NEG_INFINITY, f32::NEG_INFINITY, f32::NEG_INFINITY),
121 };
122
123 #[inline]
129 pub fn from_min_max(min_pt: Vec3, max_pt: Vec3) -> Self {
130 Self {
131 min: Vec3::new(
132 min_pt.x.min(max_pt.x),
133 min_pt.y.min(max_pt.y),
134 min_pt.z.min(max_pt.z),
135 ),
136 max: Vec3::new(
137 min_pt.x.max(max_pt.x),
138 min_pt.y.max(max_pt.y),
139 min_pt.z.max(max_pt.z),
140 ),
141 }
142 }
143
144 #[inline]
149 pub fn from_center_half_extents(center: Vec3, half_extents: Vec3) -> Self {
150 let safe_half_extents = half_extents.abs();
151 Self {
152 min: center - safe_half_extents,
153 max: center + safe_half_extents,
154 }
155 }
156
157 #[inline]
159 pub fn from_half_extents(half_extents: Vec3) -> Self {
160 let safe_half_extents = half_extents.abs();
161 Self {
162 min: -safe_half_extents,
163 max: safe_half_extents,
164 }
165 }
166
167 #[inline]
169 pub fn from_point(point: Vec3) -> Self {
170 Self {
171 min: point,
172 max: point,
173 }
174 }
175
176 pub fn from_points(points: &[Vec3]) -> Option<Self> {
182 if points.is_empty() {
183 return None;
184 }
185
186 let mut min_pt = points[0];
187 let mut max_pt = points[0];
188
189 for point in points.iter().skip(1) {
190 min_pt.x = min_pt.x.min(point.x);
191 min_pt.y = min_pt.y.min(point.y);
192 min_pt.z = min_pt.z.min(point.z);
193
194 max_pt.x = max_pt.x.max(point.x);
195 max_pt.y = max_pt.y.max(point.y);
196 max_pt.z = max_pt.z.max(point.z);
197 }
198
199 Some(Self {
200 min: min_pt,
201 max: max_pt,
202 })
203 }
204
205 #[inline]
207 pub fn center(&self) -> Vec3 {
208 (self.min + self.max) * 0.5
209 }
210
211 #[inline]
213 pub fn half_extents(&self) -> Vec3 {
214 (self.max - self.min) * 0.5
215 }
216
217 #[inline]
219 pub fn size(&self) -> Vec3 {
220 self.max - self.min
221 }
222
223 #[inline]
226 pub fn is_valid(&self) -> bool {
227 self.min.x <= self.max.x && self.min.y <= self.max.y && self.min.z <= self.max.z
228 }
229
230 #[inline]
232 pub fn contains_point(&self, point: Vec3) -> bool {
233 point.x >= self.min.x
234 && point.x <= self.max.x
235 && point.y >= self.min.y
236 && point.y <= self.max.y
237 && point.z >= self.min.z
238 && point.z <= self.max.z
239 }
240
241 #[inline]
246 pub fn intersects_aabb(&self, other: &Aabb) -> bool {
247 (self.min.x <= other.max.x && self.max.x >= other.min.x)
248 && (self.min.y <= other.max.y && self.max.y >= other.min.y)
249 && (self.min.z <= other.max.z && self.max.z >= other.min.z)
250 }
251
252 #[inline]
254 pub fn merge(&self, other: &Aabb) -> Self {
255 Self {
256 min: Vec3::new(
257 self.min.x.min(other.min.x),
258 self.min.y.min(other.min.y),
259 self.min.z.min(other.min.z),
260 ),
261 max: Vec3::new(
262 self.max.x.max(other.max.x),
263 self.max.y.max(other.max.y),
264 self.max.z.max(other.max.z),
265 ),
266 }
267 }
268
269 #[inline]
271 pub fn merged_with_point(&self, point: Vec3) -> Self {
272 Self {
273 min: Vec3::new(
274 self.min.x.min(point.x),
275 self.min.y.min(point.y),
276 self.min.z.min(point.z),
277 ),
278 max: Vec3::new(
279 self.max.x.max(point.x),
280 self.max.y.max(point.y),
281 self.max.z.max(point.z),
282 ),
283 }
284 }
285
286 pub fn transform(&self, matrix: &Mat4) -> Self {
297 let center = self.center();
298 let half_extents = self.half_extents();
299 let transformed_center_v4 = *matrix * Vec4::from_vec3(center, 1.0);
300
301 let transformed_center = if (transformed_center_v4.w - 1.0).abs() > EPSILON
302 && transformed_center_v4.w.abs() > EPSILON
303 {
304 transformed_center_v4.truncate() / transformed_center_v4.w
305 } else {
306 transformed_center_v4.truncate()
307 };
308
309 let x_abs_col = Vec3::new(
310 matrix.cols[0][0].abs(),
311 matrix.cols[0][1].abs(),
312 matrix.cols[0][2].abs(),
313 );
314 let y_abs_col = Vec3::new(
315 matrix.cols[1][0].abs(),
316 matrix.cols[1][1].abs(),
317 matrix.cols[1][2].abs(),
318 );
319 let z_abs_col = Vec3::new(
320 matrix.cols[2][0].abs(),
321 matrix.cols[2][1].abs(),
322 matrix.cols[2][2].abs(),
323 );
324
325 let new_half_extents =
326 x_abs_col * half_extents.x + y_abs_col * half_extents.y + z_abs_col * half_extents.z;
327
328 Aabb::from_center_half_extents(transformed_center, new_half_extents)
329 }
330
331 #[inline]
335 pub fn surface_area(&self) -> f32 {
336 let d = self.max - self.min;
337 2.0 * (d.x * d.y + d.y * d.z + d.z * d.x)
338 }
339
340 #[inline]
342 pub fn contains_aabb(&self, other: &Aabb) -> bool {
343 self.min.x <= other.min.x
344 && self.max.x >= other.max.x
345 && self.min.y <= other.min.y
346 && self.max.y >= other.max.y
347 && self.min.z <= other.min.z
348 && self.max.z >= other.max.z
349 }
350
351 pub fn intersect_ray(&self, origin: Vec3, inv_dir: Vec3) -> Option<f32> {
356 let t1 = (self.min.x - origin.x) * inv_dir.x;
357 let t2 = (self.max.x - origin.x) * inv_dir.x;
358 let t3 = (self.min.y - origin.y) * inv_dir.y;
359 let t4 = (self.max.y - origin.y) * inv_dir.y;
360 let t5 = (self.min.z - origin.z) * inv_dir.z;
361 let t6 = (self.max.z - origin.z) * inv_dir.z;
362
363 let tmin = t1.min(t2).max(t3.min(t4)).max(t5.min(t6));
364 let tmax = t1.max(t2).min(t3.max(t4)).min(t5.max(t6));
365
366 if tmax < 0.0 || tmin > tmax {
367 return None;
368 }
369
370 Some(tmin)
371 }
372}
373
374impl Default for Aabb {
375 #[inline]
377 fn default() -> Self {
378 Self::INVALID
379 }
380}
381
382#[cfg(test)]
384mod tests {
385 use super::*;
386 use crate::math::{approx_eq, matrix::Mat4, vector::Vec3}; use std::f32::consts::PI;
388
389 fn vec3_approx_eq(a: Vec3, b: Vec3) -> bool {
390 approx_eq(a.x, b.x) && approx_eq(a.y, b.y) && approx_eq(a.z, b.z)
391 }
392
393 fn aabb_approx_eq(a: Aabb, b: Aabb) -> bool {
395 vec3_approx_eq(a.min, b.min) && vec3_approx_eq(a.max, b.max)
396 }
397
398 #[test]
399 fn test_aabb_from_min_max() {
400 let aabb = Aabb::from_min_max(Vec3::new(1.0, 2.0, 3.0), Vec3::new(4.0, 5.0, 6.0));
401 assert_eq!(aabb.min, Vec3::new(1.0, 2.0, 3.0));
402 assert_eq!(aabb.max, Vec3::new(4.0, 5.0, 6.0));
403
404 let aabb_swapped = Aabb::from_min_max(Vec3::new(4.0, 5.0, 6.0), Vec3::new(1.0, 2.0, 3.0));
406 assert_eq!(aabb_swapped.min, Vec3::new(1.0, 2.0, 3.0));
407 assert_eq!(aabb_swapped.max, Vec3::new(4.0, 5.0, 6.0));
408 }
409
410 #[test]
411 fn test_aabb_from_center_half_extents() {
412 let center = Vec3::new(10.0, 20.0, 30.0);
413 let half_extents = Vec3::new(1.0, 2.0, 3.0);
414 let aabb = Aabb::from_center_half_extents(center, half_extents);
415
416 assert_eq!(aabb.min, Vec3::new(9.0, 18.0, 27.0));
417 assert_eq!(aabb.max, Vec3::new(11.0, 22.0, 33.0));
418 assert!(aabb_approx_eq(aabb, Aabb::from_min_max(aabb.min, aabb.max)));
419 }
420
421 #[test]
422 fn test_aabb_from_point() {
423 let p = Vec3::new(5.0, 6.0, 7.0);
424 let aabb = Aabb::from_point(p);
425
426 assert_eq!(aabb.min, p);
427 assert_eq!(aabb.max, p);
428 assert!(aabb.is_valid());
429 }
430
431 #[test]
432 fn test_aabb_from_points() {
433 assert!(Aabb::from_points(&[]).is_none());
434
435 let points = [
436 Vec3::new(1.0, 5.0, -1.0),
437 Vec3::new(0.0, 2.0, 3.0),
438 Vec3::new(4.0, 8.0, 0.0),
439 ];
440 let aabb = Aabb::from_points(&points).unwrap();
441
442 assert_eq!(aabb.min, Vec3::new(0.0, 2.0, -1.0));
443 assert_eq!(aabb.max, Vec3::new(4.0, 8.0, 3.0));
444 }
445
446 #[test]
447 fn test_aabb_utils() {
448 let aabb = Aabb::from_min_max(Vec3::new(-1.0, 0.0, 1.0), Vec3::new(3.0, 2.0, 5.0));
449
450 assert!(vec3_approx_eq(aabb.center(), Vec3::new(1.0, 1.0, 3.0)));
451 assert!(vec3_approx_eq(aabb.size(), Vec3::new(4.0, 2.0, 4.0)));
452 assert!(vec3_approx_eq(
453 aabb.half_extents(),
454 Vec3::new(2.0, 1.0, 2.0)
455 ));
456 assert!(aabb.is_valid());
457 assert!(!Aabb::INVALID.is_valid());
458 assert!(Aabb::from_point(Vec3::ZERO).is_valid());
459 }
460
461 #[test]
462 fn test_aabb_contains_point() {
463 let aabb = Aabb::from_min_max(Vec3::new(0.0, 0.0, 0.0), Vec3::new(1.0, 1.0, 1.0));
464 assert!(aabb.contains_point(Vec3::new(0.5, 0.5, 0.5)));
466
467 assert!(aabb.contains_point(Vec3::new(0.0, 0.5, 0.5)));
469 assert!(aabb.contains_point(Vec3::new(1.0, 0.5, 0.5)));
470 assert!(aabb.contains_point(Vec3::new(0.5, 0.0, 0.5)));
471 assert!(aabb.contains_point(Vec3::new(0.5, 1.0, 0.5)));
472 assert!(aabb.contains_point(Vec3::new(0.5, 0.5, 0.0)));
473 assert!(aabb.contains_point(Vec3::new(0.5, 0.5, 1.0)));
474 assert!(aabb.contains_point(Vec3::new(0.0, 0.0, 0.0)));
475 assert!(aabb.contains_point(Vec3::new(1.0, 1.0, 1.0)));
476
477 assert!(!aabb.contains_point(Vec3::new(1.1, 0.5, 0.5)));
479 assert!(!aabb.contains_point(Vec3::new(-0.1, 0.5, 0.5)));
480 assert!(!aabb.contains_point(Vec3::new(0.5, 1.1, 0.5)));
481 assert!(!aabb.contains_point(Vec3::new(0.5, -0.1, 0.5)));
482 assert!(!aabb.contains_point(Vec3::new(0.5, 0.5, 1.1)));
483 assert!(!aabb.contains_point(Vec3::new(0.5, 0.5, -0.1)));
484 }
485
486 #[test]
487 fn test_aabb_intersects_aabb() {
488 let aabb1 = Aabb::from_min_max(Vec3::new(0.0, 0.0, 0.0), Vec3::new(2.0, 2.0, 2.0));
489
490 let aabb2 = Aabb::from_min_max(Vec3::new(0.0, 0.0, 0.0), Vec3::new(2.0, 2.0, 2.0));
492 assert!(aabb1.intersects_aabb(&aabb2));
493
494 let aabb3 = Aabb::from_min_max(Vec3::new(1.0, 1.0, 1.0), Vec3::new(3.0, 3.0, 3.0));
496 assert!(aabb1.intersects_aabb(&aabb3));
497 assert!(aabb3.intersects_aabb(&aabb1));
498
499 let aabb4 = Aabb::from_min_max(Vec3::new(2.0, 0.0, 0.0), Vec3::new(3.0, 2.0, 2.0));
501 assert!(aabb1.intersects_aabb(&aabb4));
502 assert!(aabb4.intersects_aabb(&aabb1));
503
504 let aabb5 = Aabb::from_min_max(Vec3::new(0.5, 0.5, 0.5), Vec3::new(1.5, 1.5, 1.5));
506 assert!(aabb1.intersects_aabb(&aabb5));
507 assert!(aabb5.intersects_aabb(&aabb1));
508
509 let aabb6 = Aabb::from_min_max(Vec3::new(2.1, 0.0, 0.0), Vec3::new(3.0, 2.0, 2.0));
511 assert!(!aabb1.intersects_aabb(&aabb6));
512 assert!(!aabb6.intersects_aabb(&aabb1));
513
514 let aabb7 = Aabb::from_min_max(Vec3::new(0.0, 2.1, 0.0), Vec3::new(2.0, 3.0, 2.0));
516 assert!(!aabb1.intersects_aabb(&aabb7));
517 assert!(!aabb7.intersects_aabb(&aabb1));
518
519 let aabb8 = Aabb::from_min_max(Vec3::new(0.0, 0.0, 2.1), Vec3::new(2.0, 2.0, 3.0));
521 assert!(!aabb1.intersects_aabb(&aabb8));
522 assert!(!aabb8.intersects_aabb(&aabb1));
523 }
524
525 #[test]
526 fn test_aabb_contains_aabb() {
527 let aabb1 = Aabb::from_min_max(Vec3::new(0.0, 0.0, 0.0), Vec3::new(10.0, 10.0, 10.0));
528
529 let aabb2 = Aabb::from_min_max(Vec3::new(2.0, 2.0, 2.0), Vec3::new(8.0, 8.0, 8.0));
531 assert!(aabb1.contains_aabb(&aabb2));
532 assert!(!aabb2.contains_aabb(&aabb1));
533
534 let aabb3 = Aabb::from_min_max(Vec3::new(5.0, 5.0, 5.0), Vec3::new(15.0, 15.0, 15.0));
536 assert!(aabb1.intersects_aabb(&aabb3));
537 assert!(!aabb1.contains_aabb(&aabb3));
538
539 assert!(aabb1.contains_aabb(&aabb1));
541 }
542
543 #[test]
544 fn test_aabb_merge() {
545 let aabb1 = Aabb::from_min_max(Vec3::new(0.0, 0.0, 0.0), Vec3::new(1.0, 1.0, 1.0));
546 let aabb2 = Aabb::from_min_max(Vec3::new(0.5, 0.5, 0.5), Vec3::new(1.5, 1.5, 1.5));
547 let merged_aabb = aabb1.merge(&aabb2);
548
549 assert_eq!(merged_aabb.min, Vec3::new(0.0, 0.0, 0.0));
550 assert_eq!(merged_aabb.max, Vec3::new(1.5, 1.5, 1.5));
551
552 let point = Vec3::new(-1.0, 0.5, 2.0);
553 let merged_point = aabb1.merged_with_point(point);
554
555 assert_eq!(merged_point.min, Vec3::new(-1.0, 0.0, 0.0));
556 assert_eq!(merged_point.max, Vec3::new(1.0, 1.0, 2.0));
557
558 let merged_with_invalid = Aabb::INVALID.merge(&aabb1);
560 assert!(aabb_approx_eq(merged_with_invalid, aabb1));
561
562 let merged_with_invalid_pt = Aabb::INVALID.merged_with_point(point);
563 assert!(aabb_approx_eq(
564 merged_with_invalid_pt,
565 Aabb::from_point(point)
566 ));
567 }
568
569 #[test]
570 fn test_aabb_transform() {
571 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);
574 let expected_aabb =
575 Aabb::from_min_max(Vec3::new(9.0, -1.0, -1.0), Vec3::new(11.0, 1.0, 1.0));
576
577 assert!(aabb_approx_eq(transformed_aabb, expected_aabb));
578
579 let matrix_rot = Mat4::from_rotation_y(PI / 4.0); let transformed_rot_aabb = aabb.transform(&matrix_rot);
582
583 let sqrt2 = 2.0f32.sqrt();
586
587 assert!(approx_eq(transformed_rot_aabb.min.x, -sqrt2));
588 assert!(approx_eq(transformed_rot_aabb.max.x, sqrt2));
589 assert!(approx_eq(transformed_rot_aabb.min.y, -1.0)); assert!(approx_eq(transformed_rot_aabb.max.y, 1.0));
591 assert!(approx_eq(transformed_rot_aabb.min.z, -sqrt2));
592 assert!(approx_eq(transformed_rot_aabb.max.z, sqrt2));
593
594 let matrix_scale = Mat4::from_scale(Vec3::new(2.0, 1.0, 0.5));
596 let transformed_scale_aabb = aabb.transform(&matrix_scale);
597 let expected_scale_aabb =
598 Aabb::from_min_max(Vec3::new(-2.0, -1.0, -0.5), Vec3::new(2.0, 1.0, 0.5));
599
600 assert!(aabb_approx_eq(transformed_scale_aabb, expected_scale_aabb));
601 }
602}