1mod extrusion;
2mod primitive_impls;
3
4use glam::Mat3;
5
6use super::{BoundingVolume, IntersectsVolume};
7use crate::{
8 ops::{self, FloatPow},
9 Isometry3d, Quat, Vec3A,
10};
11
12#[cfg(feature = "bevy_reflect")]
13use bevy_reflect::Reflect;
14#[cfg(all(feature = "bevy_reflect", feature = "serialize"))]
15use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
16#[cfg(feature = "serialize")]
17use serde::{Deserialize, Serialize};
18
19pub use extrusion::BoundedExtrusion;
20
21#[inline(always)]
23fn point_cloud_3d_center(points: impl Iterator<Item = impl Into<Vec3A>>) -> Vec3A {
24 let (acc, len) = points.fold((Vec3A::ZERO, 0), |(acc, len), point| {
25 (acc + point.into(), len + 1)
26 });
27
28 assert!(
29 len > 0,
30 "cannot compute the center of an empty set of points"
31 );
32 acc / len as f32
33}
34
35pub trait Bounded3d {
37 fn aabb_3d(&self, isometry: impl Into<Isometry3d>) -> Aabb3d;
39 fn bounding_sphere(&self, isometry: impl Into<Isometry3d>) -> BoundingSphere;
41}
42
43#[derive(Clone, Copy, Debug, PartialEq)]
45#[cfg_attr(
46 feature = "bevy_reflect",
47 derive(Reflect),
48 reflect(Debug, PartialEq, Clone)
49)]
50#[cfg_attr(feature = "serialize", derive(Serialize), derive(Deserialize))]
51#[cfg_attr(
52 all(feature = "serialize", feature = "bevy_reflect"),
53 reflect(Serialize, Deserialize)
54)]
55pub struct Aabb3d {
56 pub min: Vec3A,
58 pub max: Vec3A,
60}
61
62impl Aabb3d {
63 #[inline(always)]
65 pub fn new(center: impl Into<Vec3A>, half_size: impl Into<Vec3A>) -> Self {
66 let (center, half_size) = (center.into(), half_size.into());
67 debug_assert!(half_size.x >= 0.0 && half_size.y >= 0.0 && half_size.z >= 0.0);
68 Self {
69 min: center - half_size,
70 max: center + half_size,
71 }
72 }
73
74 #[inline(always)]
81 pub fn from_point_cloud(
82 isometry: impl Into<Isometry3d>,
83 points: impl Iterator<Item = impl Into<Vec3A>>,
84 ) -> Aabb3d {
85 let isometry = isometry.into();
86
87 let mut iter = points.map(|point| isometry.rotation * point.into());
89
90 let first = iter
91 .next()
92 .expect("point cloud must contain at least one point for Aabb3d construction");
93
94 let (min, max) = iter.fold((first, first), |(prev_min, prev_max), point| {
95 (point.min(prev_min), point.max(prev_max))
96 });
97
98 Aabb3d {
99 min: min + isometry.translation,
100 max: max + isometry.translation,
101 }
102 }
103
104 #[inline(always)]
106 pub fn bounding_sphere(&self) -> BoundingSphere {
107 let radius = self.min.distance(self.max) / 2.0;
108 BoundingSphere::new(self.center(), radius)
109 }
110
111 #[inline(always)]
116 pub fn closest_point(&self, point: impl Into<Vec3A>) -> Vec3A {
117 point.into().clamp(self.min, self.max)
119 }
120}
121
122impl BoundingVolume for Aabb3d {
123 type Translation = Vec3A;
124 type Rotation = Quat;
125 type HalfSize = Vec3A;
126
127 #[inline(always)]
128 fn center(&self) -> Self::Translation {
129 (self.min + self.max) / 2.
130 }
131
132 #[inline(always)]
133 fn half_size(&self) -> Self::HalfSize {
134 (self.max - self.min) / 2.
135 }
136
137 #[inline(always)]
138 fn visible_area(&self) -> f32 {
139 let b = self.max - self.min;
140 b.x * (b.y + b.z) + b.y * b.z
141 }
142
143 #[inline(always)]
144 fn contains(&self, other: &Self) -> bool {
145 other.min.cmpge(self.min).all() && other.max.cmple(self.max).all()
146 }
147
148 #[inline(always)]
149 fn merge(&self, other: &Self) -> Self {
150 Self {
151 min: self.min.min(other.min),
152 max: self.max.max(other.max),
153 }
154 }
155
156 #[inline(always)]
157 fn grow(&self, amount: impl Into<Self::HalfSize>) -> Self {
158 let amount = amount.into();
159 let b = Self {
160 min: self.min - amount,
161 max: self.max + amount,
162 };
163 debug_assert!(b.min.cmple(b.max).all());
164 b
165 }
166
167 #[inline(always)]
168 fn shrink(&self, amount: impl Into<Self::HalfSize>) -> Self {
169 let amount = amount.into();
170 let b = Self {
171 min: self.min + amount,
172 max: self.max - amount,
173 };
174 debug_assert!(b.min.cmple(b.max).all());
175 b
176 }
177
178 #[inline(always)]
179 fn scale_around_center(&self, scale: impl Into<Self::HalfSize>) -> Self {
180 let scale = scale.into();
181 let b = Self {
182 min: self.center() - (self.half_size() * scale),
183 max: self.center() + (self.half_size() * scale),
184 };
185 debug_assert!(b.min.cmple(b.max).all());
186 b
187 }
188
189 #[inline(always)]
197 fn transformed_by(
198 mut self,
199 translation: impl Into<Self::Translation>,
200 rotation: impl Into<Self::Rotation>,
201 ) -> Self {
202 self.transform_by(translation, rotation);
203 self
204 }
205
206 #[inline(always)]
214 fn transform_by(
215 &mut self,
216 translation: impl Into<Self::Translation>,
217 rotation: impl Into<Self::Rotation>,
218 ) {
219 self.rotate_by(rotation);
220 self.translate_by(translation);
221 }
222
223 #[inline(always)]
224 fn translate_by(&mut self, translation: impl Into<Self::Translation>) {
225 let translation = translation.into();
226 self.min += translation;
227 self.max += translation;
228 }
229
230 #[inline(always)]
238 fn rotated_by(mut self, rotation: impl Into<Self::Rotation>) -> Self {
239 self.rotate_by(rotation);
240 self
241 }
242
243 #[inline(always)]
251 fn rotate_by(&mut self, rotation: impl Into<Self::Rotation>) {
252 let rot_mat = Mat3::from_quat(rotation.into());
253 let abs_rot_mat = Mat3::from_cols(
254 rot_mat.x_axis.abs(),
255 rot_mat.y_axis.abs(),
256 rot_mat.z_axis.abs(),
257 );
258 let half_size = abs_rot_mat * self.half_size();
259 *self = Self::new(rot_mat * self.center(), half_size);
260 }
261}
262
263impl IntersectsVolume<Self> for Aabb3d {
264 #[inline(always)]
265 fn intersects(&self, other: &Self) -> bool {
266 self.min.cmple(other.max).all() && self.max.cmpge(other.min).all()
267 }
268}
269
270impl IntersectsVolume<BoundingSphere> for Aabb3d {
271 #[inline(always)]
272 fn intersects(&self, sphere: &BoundingSphere) -> bool {
273 let closest_point = self.closest_point(sphere.center);
274 let distance_squared = sphere.center.distance_squared(closest_point);
275 let radius_squared = sphere.radius().squared();
276 distance_squared <= radius_squared
277 }
278}
279
280#[cfg(test)]
281mod aabb3d_tests {
282 use super::Aabb3d;
283 use crate::{
284 bounding::{BoundingSphere, BoundingVolume, IntersectsVolume},
285 ops, Quat, Vec3, Vec3A,
286 };
287
288 #[test]
289 fn center() {
290 let aabb = Aabb3d {
291 min: Vec3A::new(-0.5, -1., -0.5),
292 max: Vec3A::new(1., 1., 2.),
293 };
294 assert!((aabb.center() - Vec3A::new(0.25, 0., 0.75)).length() < f32::EPSILON);
295 let aabb = Aabb3d {
296 min: Vec3A::new(5., 5., -10.),
297 max: Vec3A::new(10., 10., -5.),
298 };
299 assert!((aabb.center() - Vec3A::new(7.5, 7.5, -7.5)).length() < f32::EPSILON);
300 }
301
302 #[test]
303 fn half_size() {
304 let aabb = Aabb3d {
305 min: Vec3A::new(-0.5, -1., -0.5),
306 max: Vec3A::new(1., 1., 2.),
307 };
308 assert!((aabb.half_size() - Vec3A::new(0.75, 1., 1.25)).length() < f32::EPSILON);
309 }
310
311 #[test]
312 fn area() {
313 let aabb = Aabb3d {
314 min: Vec3A::new(-1., -1., -1.),
315 max: Vec3A::new(1., 1., 1.),
316 };
317 assert!(ops::abs(aabb.visible_area() - 12.) < f32::EPSILON);
318 let aabb = Aabb3d {
319 min: Vec3A::new(0., 0., 0.),
320 max: Vec3A::new(1., 0.5, 0.25),
321 };
322 assert!(ops::abs(aabb.visible_area() - 0.875) < f32::EPSILON);
323 }
324
325 #[test]
326 fn contains() {
327 let a = Aabb3d {
328 min: Vec3A::new(-1., -1., -1.),
329 max: Vec3A::new(1., 1., 1.),
330 };
331 let b = Aabb3d {
332 min: Vec3A::new(-2., -1., -1.),
333 max: Vec3A::new(1., 1., 1.),
334 };
335 assert!(!a.contains(&b));
336 let b = Aabb3d {
337 min: Vec3A::new(-0.25, -0.8, -0.9),
338 max: Vec3A::new(1., 1., 0.9),
339 };
340 assert!(a.contains(&b));
341 }
342
343 #[test]
344 fn merge() {
345 let a = Aabb3d {
346 min: Vec3A::new(-1., -1., -1.),
347 max: Vec3A::new(1., 0.5, 1.),
348 };
349 let b = Aabb3d {
350 min: Vec3A::new(-2., -0.5, -0.),
351 max: Vec3A::new(0.75, 1., 2.),
352 };
353 let merged = a.merge(&b);
354 assert!((merged.min - Vec3A::new(-2., -1., -1.)).length() < f32::EPSILON);
355 assert!((merged.max - Vec3A::new(1., 1., 2.)).length() < f32::EPSILON);
356 assert!(merged.contains(&a));
357 assert!(merged.contains(&b));
358 assert!(!a.contains(&merged));
359 assert!(!b.contains(&merged));
360 }
361
362 #[test]
363 fn grow() {
364 let a = Aabb3d {
365 min: Vec3A::new(-1., -1., -1.),
366 max: Vec3A::new(1., 1., 1.),
367 };
368 let padded = a.grow(Vec3A::ONE);
369 assert!((padded.min - Vec3A::new(-2., -2., -2.)).length() < f32::EPSILON);
370 assert!((padded.max - Vec3A::new(2., 2., 2.)).length() < f32::EPSILON);
371 assert!(padded.contains(&a));
372 assert!(!a.contains(&padded));
373 }
374
375 #[test]
376 fn shrink() {
377 let a = Aabb3d {
378 min: Vec3A::new(-2., -2., -2.),
379 max: Vec3A::new(2., 2., 2.),
380 };
381 let shrunk = a.shrink(Vec3A::ONE);
382 assert!((shrunk.min - Vec3A::new(-1., -1., -1.)).length() < f32::EPSILON);
383 assert!((shrunk.max - Vec3A::new(1., 1., 1.)).length() < f32::EPSILON);
384 assert!(a.contains(&shrunk));
385 assert!(!shrunk.contains(&a));
386 }
387
388 #[test]
389 fn scale_around_center() {
390 let a = Aabb3d {
391 min: Vec3A::NEG_ONE,
392 max: Vec3A::ONE,
393 };
394 let scaled = a.scale_around_center(Vec3A::splat(2.));
395 assert!((scaled.min - Vec3A::splat(-2.)).length() < f32::EPSILON);
396 assert!((scaled.max - Vec3A::splat(2.)).length() < f32::EPSILON);
397 assert!(!a.contains(&scaled));
398 assert!(scaled.contains(&a));
399 }
400
401 #[test]
402 fn transform() {
403 let a = Aabb3d {
404 min: Vec3A::new(-2.0, -2.0, -2.0),
405 max: Vec3A::new(2.0, 2.0, 2.0),
406 };
407 let transformed = a.transformed_by(
408 Vec3A::new(2.0, -2.0, 4.0),
409 Quat::from_rotation_z(core::f32::consts::FRAC_PI_4),
410 );
411 let half_length = ops::hypot(2.0, 2.0);
412 assert_eq!(
413 transformed.min,
414 Vec3A::new(2.0 - half_length, -half_length - 2.0, 2.0)
415 );
416 assert_eq!(
417 transformed.max,
418 Vec3A::new(2.0 + half_length, half_length - 2.0, 6.0)
419 );
420 }
421
422 #[test]
423 fn closest_point() {
424 let aabb = Aabb3d {
425 min: Vec3A::NEG_ONE,
426 max: Vec3A::ONE,
427 };
428 assert_eq!(aabb.closest_point(Vec3A::X * 10.0), Vec3A::X);
429 assert_eq!(aabb.closest_point(Vec3A::NEG_ONE * 10.0), Vec3A::NEG_ONE);
430 assert_eq!(
431 aabb.closest_point(Vec3A::new(0.25, 0.1, 0.3)),
432 Vec3A::new(0.25, 0.1, 0.3)
433 );
434 }
435
436 #[test]
437 fn intersect_aabb() {
438 let aabb = Aabb3d {
439 min: Vec3A::NEG_ONE,
440 max: Vec3A::ONE,
441 };
442 assert!(aabb.intersects(&aabb));
443 assert!(aabb.intersects(&Aabb3d {
444 min: Vec3A::splat(0.5),
445 max: Vec3A::splat(2.0),
446 }));
447 assert!(aabb.intersects(&Aabb3d {
448 min: Vec3A::splat(-2.0),
449 max: Vec3A::splat(-0.5),
450 }));
451 assert!(!aabb.intersects(&Aabb3d {
452 min: Vec3A::new(1.1, 0.0, 0.0),
453 max: Vec3A::new(2.0, 0.5, 0.25),
454 }));
455 }
456
457 #[test]
458 fn intersect_bounding_sphere() {
459 let aabb = Aabb3d {
460 min: Vec3A::NEG_ONE,
461 max: Vec3A::ONE,
462 };
463 assert!(aabb.intersects(&BoundingSphere::new(Vec3::ZERO, 1.0)));
464 assert!(aabb.intersects(&BoundingSphere::new(Vec3::ONE * 1.5, 1.0)));
465 assert!(aabb.intersects(&BoundingSphere::new(Vec3::NEG_ONE * 1.5, 1.0)));
466 assert!(!aabb.intersects(&BoundingSphere::new(Vec3::ONE * 1.75, 1.0)));
467 }
468}
469
470use crate::primitives::Sphere;
471
472#[derive(Clone, Copy, Debug, PartialEq)]
474#[cfg_attr(
475 feature = "bevy_reflect",
476 derive(Reflect),
477 reflect(Debug, PartialEq, Clone)
478)]
479#[cfg_attr(feature = "serialize", derive(Serialize), derive(Deserialize))]
480#[cfg_attr(
481 all(feature = "serialize", feature = "bevy_reflect"),
482 reflect(Serialize, Deserialize)
483)]
484pub struct BoundingSphere {
485 pub center: Vec3A,
487 pub sphere: Sphere,
489}
490
491impl BoundingSphere {
492 pub fn new(center: impl Into<Vec3A>, radius: f32) -> Self {
494 debug_assert!(radius >= 0.);
495 Self {
496 center: center.into(),
497 sphere: Sphere { radius },
498 }
499 }
500
501 #[inline(always)]
506 pub fn from_point_cloud(
507 isometry: impl Into<Isometry3d>,
508 points: &[impl Copy + Into<Vec3A>],
509 ) -> BoundingSphere {
510 let isometry = isometry.into();
511
512 let center = point_cloud_3d_center(points.iter().map(|v| Into::<Vec3A>::into(*v)));
513 let mut radius_squared: f32 = 0.0;
514
515 for point in points {
516 let distance_squared = Into::<Vec3A>::into(*point).distance_squared(center);
518 if distance_squared > radius_squared {
519 radius_squared = distance_squared;
520 }
521 }
522
523 BoundingSphere::new(isometry * center, ops::sqrt(radius_squared))
524 }
525
526 #[inline(always)]
528 pub fn radius(&self) -> f32 {
529 self.sphere.radius
530 }
531
532 #[inline(always)]
534 pub fn aabb_3d(&self) -> Aabb3d {
535 Aabb3d {
536 min: self.center - self.radius(),
537 max: self.center + self.radius(),
538 }
539 }
540
541 #[inline(always)]
546 pub fn closest_point(&self, point: impl Into<Vec3A>) -> Vec3A {
547 let point = point.into();
548 let radius = self.radius();
549 let distance_squared = (point - self.center).length_squared();
550
551 if distance_squared <= radius.squared() {
552 point
554 } else {
555 let dir_to_point = point / ops::sqrt(distance_squared);
558 self.center + radius * dir_to_point
559 }
560 }
561}
562
563impl BoundingVolume for BoundingSphere {
564 type Translation = Vec3A;
565 type Rotation = Quat;
566 type HalfSize = f32;
567
568 #[inline(always)]
569 fn center(&self) -> Self::Translation {
570 self.center
571 }
572
573 #[inline(always)]
574 fn half_size(&self) -> Self::HalfSize {
575 self.radius()
576 }
577
578 #[inline(always)]
579 fn visible_area(&self) -> f32 {
580 2. * core::f32::consts::PI * self.radius() * self.radius()
581 }
582
583 #[inline(always)]
584 fn contains(&self, other: &Self) -> bool {
585 let diff = self.radius() - other.radius();
586 self.center.distance_squared(other.center) <= ops::copysign(diff.squared(), diff)
587 }
588
589 #[inline(always)]
590 fn merge(&self, other: &Self) -> Self {
591 let diff = other.center - self.center;
592 let length = diff.length();
593 if self.radius() >= length + other.radius() {
594 return *self;
595 }
596 if other.radius() >= length + self.radius() {
597 return *other;
598 }
599 let dir = diff / length;
600 Self::new(
601 (self.center + other.center) / 2. + dir * ((other.radius() - self.radius()) / 2.),
602 (length + self.radius() + other.radius()) / 2.,
603 )
604 }
605
606 #[inline(always)]
607 fn grow(&self, amount: impl Into<Self::HalfSize>) -> Self {
608 let amount = amount.into();
609 debug_assert!(amount >= 0.);
610 Self {
611 center: self.center,
612 sphere: Sphere {
613 radius: self.radius() + amount,
614 },
615 }
616 }
617
618 #[inline(always)]
619 fn shrink(&self, amount: impl Into<Self::HalfSize>) -> Self {
620 let amount = amount.into();
621 debug_assert!(amount >= 0.);
622 debug_assert!(self.radius() >= amount);
623 Self {
624 center: self.center,
625 sphere: Sphere {
626 radius: self.radius() - amount,
627 },
628 }
629 }
630
631 #[inline(always)]
632 fn scale_around_center(&self, scale: impl Into<Self::HalfSize>) -> Self {
633 let scale = scale.into();
634 debug_assert!(scale >= 0.);
635 Self::new(self.center, self.radius() * scale)
636 }
637
638 #[inline(always)]
639 fn translate_by(&mut self, translation: impl Into<Self::Translation>) {
640 self.center += translation.into();
641 }
642
643 #[inline(always)]
644 fn rotate_by(&mut self, rotation: impl Into<Self::Rotation>) {
645 let rotation: Quat = rotation.into();
646 self.center = rotation * self.center;
647 }
648}
649
650impl IntersectsVolume<Self> for BoundingSphere {
651 #[inline(always)]
652 fn intersects(&self, other: &Self) -> bool {
653 let center_distance_squared = self.center.distance_squared(other.center);
654 let radius_sum_squared = (self.radius() + other.radius()).squared();
655 center_distance_squared <= radius_sum_squared
656 }
657}
658
659impl IntersectsVolume<Aabb3d> for BoundingSphere {
660 #[inline(always)]
661 fn intersects(&self, aabb: &Aabb3d) -> bool {
662 aabb.intersects(self)
663 }
664}
665
666#[cfg(test)]
667mod bounding_sphere_tests {
668 use approx::assert_relative_eq;
669
670 use super::BoundingSphere;
671 use crate::{
672 bounding::{BoundingVolume, IntersectsVolume},
673 ops, Quat, Vec3, Vec3A,
674 };
675
676 #[test]
677 fn area() {
678 let sphere = BoundingSphere::new(Vec3::ONE, 5.);
679 assert!(ops::abs(sphere.visible_area() - 157.0796) < 0.001);
681 }
682
683 #[test]
684 fn contains() {
685 let a = BoundingSphere::new(Vec3::ONE, 5.);
686 let b = BoundingSphere::new(Vec3::new(5.5, 1., 1.), 1.);
687 assert!(!a.contains(&b));
688 let b = BoundingSphere::new(Vec3::new(1., -3.5, 1.), 0.5);
689 assert!(a.contains(&b));
690 }
691
692 #[test]
693 fn contains_identical() {
694 let a = BoundingSphere::new(Vec3::ONE, 5.);
695 assert!(a.contains(&a));
696 }
697
698 #[test]
699 fn merge() {
700 let a = BoundingSphere::new(Vec3::ONE, 5.);
703 let b = BoundingSphere::new(Vec3::new(1., 1., -4.), 1.);
704 let merged = a.merge(&b);
705 assert!((merged.center - Vec3A::new(1., 1., 0.5)).length() < f32::EPSILON);
706 assert!(ops::abs(merged.radius() - 5.5) < f32::EPSILON);
707 assert!(merged.contains(&a));
708 assert!(merged.contains(&b));
709 assert!(!a.contains(&merged));
710 assert!(!b.contains(&merged));
711
712 let b = BoundingSphere::new(Vec3::ZERO, 3.);
714 assert!(a.contains(&b));
715 let merged = a.merge(&b);
716 assert_eq!(merged.center, a.center);
717 assert_eq!(merged.radius(), a.radius());
718
719 let b = BoundingSphere::new(Vec3::ONE, 6.);
721 let merged = a.merge(&b);
722 assert_eq!(merged.center, a.center);
723 assert_eq!(merged.radius(), b.radius());
724 }
725
726 #[test]
727 fn merge_identical() {
728 let a = BoundingSphere::new(Vec3::ONE, 5.);
729 let merged = a.merge(&a);
730 assert_eq!(merged.center, a.center);
731 assert_eq!(merged.radius(), a.radius());
732 }
733
734 #[test]
735 fn grow() {
736 let a = BoundingSphere::new(Vec3::ONE, 5.);
737 let padded = a.grow(1.25);
738 assert!(ops::abs(padded.radius() - 6.25) < f32::EPSILON);
739 assert!(padded.contains(&a));
740 assert!(!a.contains(&padded));
741 }
742
743 #[test]
744 fn shrink() {
745 let a = BoundingSphere::new(Vec3::ONE, 5.);
746 let shrunk = a.shrink(0.5);
747 assert!(ops::abs(shrunk.radius() - 4.5) < f32::EPSILON);
748 assert!(a.contains(&shrunk));
749 assert!(!shrunk.contains(&a));
750 }
751
752 #[test]
753 fn scale_around_center() {
754 let a = BoundingSphere::new(Vec3::ONE, 5.);
755 let scaled = a.scale_around_center(2.);
756 assert!(ops::abs(scaled.radius() - 10.) < f32::EPSILON);
757 assert!(!a.contains(&scaled));
758 assert!(scaled.contains(&a));
759 }
760
761 #[test]
762 fn transform() {
763 let a = BoundingSphere::new(Vec3::ONE, 5.0);
764 let transformed = a.transformed_by(
765 Vec3::new(2.0, -2.0, 4.0),
766 Quat::from_rotation_z(core::f32::consts::FRAC_PI_4),
767 );
768 assert_relative_eq!(
769 transformed.center,
770 Vec3A::new(2.0, core::f32::consts::SQRT_2 - 2.0, 5.0)
771 );
772 assert_eq!(transformed.radius(), 5.0);
773 }
774
775 #[test]
776 fn closest_point() {
777 let sphere = BoundingSphere::new(Vec3::ZERO, 1.0);
778 assert_eq!(sphere.closest_point(Vec3::X * 10.0), Vec3A::X);
779 assert_eq!(
780 sphere.closest_point(Vec3::NEG_ONE * 10.0),
781 Vec3A::NEG_ONE.normalize()
782 );
783 assert_eq!(
784 sphere.closest_point(Vec3::new(0.25, 0.1, 0.3)),
785 Vec3A::new(0.25, 0.1, 0.3)
786 );
787 }
788
789 #[test]
790 fn intersect_bounding_sphere() {
791 let sphere = BoundingSphere::new(Vec3::ZERO, 1.0);
792 assert!(sphere.intersects(&BoundingSphere::new(Vec3::ZERO, 1.0)));
793 assert!(sphere.intersects(&BoundingSphere::new(Vec3::ONE * 1.1, 1.0)));
794 assert!(sphere.intersects(&BoundingSphere::new(Vec3::NEG_ONE * 1.1, 1.0)));
795 assert!(!sphere.intersects(&BoundingSphere::new(Vec3::ONE * 1.2, 1.0)));
796 }
797}