1use crate::{
4 bounding::{Bounded2d, BoundingCircle},
5 primitives::{
6 BoxedPolyline3d, Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, InfinitePlane3d,
7 Line3d, Polyline3d, Segment3d, Sphere, Torus, Triangle2d, Triangle3d,
8 },
9 Dir3, Mat3, Quat, Vec2, Vec3,
10};
11
12use super::{Aabb3d, Bounded3d, BoundingSphere};
13
14impl Bounded3d for Sphere {
15 fn aabb_3d(&self, translation: Vec3, _rotation: Quat) -> Aabb3d {
16 Aabb3d::new(translation, Vec3::splat(self.radius))
17 }
18
19 fn bounding_sphere(&self, translation: Vec3, _rotation: Quat) -> BoundingSphere {
20 BoundingSphere::new(translation, self.radius)
21 }
22}
23
24impl Bounded3d for InfinitePlane3d {
25 fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d {
26 let normal = rotation * *self.normal;
27 let facing_x = normal == Vec3::X || normal == Vec3::NEG_X;
28 let facing_y = normal == Vec3::Y || normal == Vec3::NEG_Y;
29 let facing_z = normal == Vec3::Z || normal == Vec3::NEG_Z;
30
31 let half_width = if facing_x { 0.0 } else { f32::MAX / 2.0 };
34 let half_height = if facing_y { 0.0 } else { f32::MAX / 2.0 };
35 let half_depth = if facing_z { 0.0 } else { f32::MAX / 2.0 };
36 let half_size = Vec3::new(half_width, half_height, half_depth);
37
38 Aabb3d::new(translation, half_size)
39 }
40
41 fn bounding_sphere(&self, translation: Vec3, _rotation: Quat) -> BoundingSphere {
42 BoundingSphere::new(translation, f32::MAX / 2.0)
43 }
44}
45
46impl Bounded3d for Line3d {
47 fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d {
48 let direction = rotation * *self.direction;
49
50 let max = f32::MAX / 2.0;
53 let half_width = if direction.x == 0.0 { 0.0 } else { max };
54 let half_height = if direction.y == 0.0 { 0.0 } else { max };
55 let half_depth = if direction.z == 0.0 { 0.0 } else { max };
56 let half_size = Vec3::new(half_width, half_height, half_depth);
57
58 Aabb3d::new(translation, half_size)
59 }
60
61 fn bounding_sphere(&self, translation: Vec3, _rotation: Quat) -> BoundingSphere {
62 BoundingSphere::new(translation, f32::MAX / 2.0)
63 }
64}
65
66impl Bounded3d for Segment3d {
67 fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d {
68 let direction = rotation * *self.direction;
70 let half_size = (self.half_length * direction).abs();
71
72 Aabb3d::new(translation, half_size)
73 }
74
75 fn bounding_sphere(&self, translation: Vec3, _rotation: Quat) -> BoundingSphere {
76 BoundingSphere::new(translation, self.half_length)
77 }
78}
79
80impl<const N: usize> Bounded3d for Polyline3d<N> {
81 fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d {
82 Aabb3d::from_point_cloud(translation, rotation, self.vertices.iter().copied())
83 }
84
85 fn bounding_sphere(&self, translation: Vec3, rotation: Quat) -> BoundingSphere {
86 BoundingSphere::from_point_cloud(translation, rotation, &self.vertices)
87 }
88}
89
90impl Bounded3d for BoxedPolyline3d {
91 fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d {
92 Aabb3d::from_point_cloud(translation, rotation, self.vertices.iter().copied())
93 }
94
95 fn bounding_sphere(&self, translation: Vec3, rotation: Quat) -> BoundingSphere {
96 BoundingSphere::from_point_cloud(translation, rotation, &self.vertices)
97 }
98}
99
100impl Bounded3d for Cuboid {
101 fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d {
102 let rot_mat = Mat3::from_quat(rotation);
105 let abs_rot_mat = Mat3::from_cols(
106 rot_mat.x_axis.abs(),
107 rot_mat.y_axis.abs(),
108 rot_mat.z_axis.abs(),
109 );
110 let half_size = abs_rot_mat * self.half_size;
111
112 Aabb3d::new(translation, half_size)
113 }
114
115 fn bounding_sphere(&self, translation: Vec3, _rotation: Quat) -> BoundingSphere {
116 BoundingSphere::new(translation, self.half_size.length())
117 }
118}
119
120impl Bounded3d for Cylinder {
121 fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d {
122 let segment_dir = rotation * Vec3::Y;
125 let top = segment_dir * self.half_height;
126 let bottom = -top;
127
128 let e = (Vec3::ONE - segment_dir * segment_dir).max(Vec3::ZERO);
129 let half_size = self.radius * Vec3::new(e.x.sqrt(), e.y.sqrt(), e.z.sqrt());
130
131 Aabb3d {
132 min: (translation + (top - half_size).min(bottom - half_size)).into(),
133 max: (translation + (top + half_size).max(bottom + half_size)).into(),
134 }
135 }
136
137 fn bounding_sphere(&self, translation: Vec3, _rotation: Quat) -> BoundingSphere {
138 let radius = self.radius.hypot(self.half_height);
139 BoundingSphere::new(translation, radius)
140 }
141}
142
143impl Bounded3d for Capsule3d {
144 fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d {
145 let segment = Segment3d {
147 direction: rotation * Dir3::Y,
149 half_length: self.half_length,
150 };
151 let (a, b) = (segment.point1(), segment.point2());
152
153 let min = a.min(b) - Vec3::splat(self.radius);
155 let max = a.max(b) + Vec3::splat(self.radius);
156
157 Aabb3d {
158 min: (min + translation).into(),
159 max: (max + translation).into(),
160 }
161 }
162
163 fn bounding_sphere(&self, translation: Vec3, _rotation: Quat) -> BoundingSphere {
164 BoundingSphere::new(translation, self.radius + self.half_length)
165 }
166}
167
168impl Bounded3d for Cone {
169 fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d {
170 let segment_dir = rotation * Vec3::Y;
173 let top = segment_dir * 0.5 * self.height;
174 let bottom = -top;
175
176 let e = (Vec3::ONE - segment_dir * segment_dir).max(Vec3::ZERO);
177 let half_extents = Vec3::new(e.x.sqrt(), e.y.sqrt(), e.z.sqrt());
178
179 Aabb3d {
180 min: (translation + top.min(bottom - self.radius * half_extents)).into(),
181 max: (translation + top.max(bottom + self.radius * half_extents)).into(),
182 }
183 }
184
185 fn bounding_sphere(&self, translation: Vec3, rotation: Quat) -> BoundingSphere {
186 let half_height = 0.5 * self.height;
188 let triangle = Triangle2d::new(
189 half_height * Vec2::Y,
190 Vec2::new(-self.radius, -half_height),
191 Vec2::new(self.radius, -half_height),
192 );
193
194 let BoundingCircle { circle, center } = triangle.bounding_circle(Vec2::ZERO, 0.0);
197
198 BoundingSphere::new(rotation * center.extend(0.0) + translation, circle.radius)
199 }
200}
201
202impl Bounded3d for ConicalFrustum {
203 fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d {
204 let segment_dir = rotation * Vec3::Y;
207 let top = segment_dir * 0.5 * self.height;
208 let bottom = -top;
209
210 let e = (Vec3::ONE - segment_dir * segment_dir).max(Vec3::ZERO);
211 let half_extents = Vec3::new(e.x.sqrt(), e.y.sqrt(), e.z.sqrt());
212
213 Aabb3d {
214 min: (translation
215 + (top - self.radius_top * half_extents)
216 .min(bottom - self.radius_bottom * half_extents))
217 .into(),
218 max: (translation
219 + (top + self.radius_top * half_extents)
220 .max(bottom + self.radius_bottom * half_extents))
221 .into(),
222 }
223 }
224
225 fn bounding_sphere(&self, translation: Vec3, rotation: Quat) -> BoundingSphere {
226 let half_height = 0.5 * self.height;
227
228 let a = Vec2::new(-self.radius_top, half_height);
248 let b = Vec2::new(-self.radius_bottom, -half_height);
249 let ab = a - b;
250 let ab_midpoint = b + 0.5 * ab;
251 let bisector = ab.perp();
252
253 let circumcenter_y = -ab_midpoint.x / bisector.x * bisector.y;
267
268 let (center, radius) = if circumcenter_y <= -half_height {
271 (Vec2::new(0.0, -half_height), self.radius_bottom)
272 } else if circumcenter_y >= half_height {
273 (Vec2::new(0.0, half_height), self.radius_top)
274 } else {
275 let circumcenter = Vec2::new(0.0, circumcenter_y);
276 (circumcenter, a.distance(circumcenter))
278 };
279
280 BoundingSphere::new(translation + rotation * center.extend(0.0), radius)
281 }
282}
283
284impl Bounded3d for Torus {
285 fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d {
286 let normal = rotation * Vec3::Y;
289 let e = (Vec3::ONE - normal * normal).max(Vec3::ZERO);
290 let disc_half_size = self.major_radius * Vec3::new(e.x.sqrt(), e.y.sqrt(), e.z.sqrt());
291
292 let half_size = disc_half_size + Vec3::splat(self.minor_radius);
294
295 Aabb3d::new(translation, half_size)
296 }
297
298 fn bounding_sphere(&self, translation: Vec3, _rotation: Quat) -> BoundingSphere {
299 BoundingSphere::new(translation, self.outer_radius())
300 }
301}
302
303impl Bounded3d for Triangle3d {
304 fn aabb_3d(&self, translation: Vec3, rotation: Quat) -> Aabb3d {
306 let [a, b, c] = self.vertices;
307
308 let a = rotation * a;
309 let b = rotation * b;
310 let c = rotation * c;
311
312 let min = a.min(b).min(c);
313 let max = a.max(b).max(c);
314
315 let bounding_center = (max + min) / 2.0 + translation;
316 let half_extents = (max - min) / 2.0;
317
318 Aabb3d::new(bounding_center, half_extents)
319 }
320
321 fn bounding_sphere(&self, translation: Vec3, _rotation: Quat) -> BoundingSphere {
327 if self.is_degenerate() || self.is_obtuse() {
328 let (p1, p2) = self.largest_side();
329 let mid_point = (p1 + p2) / 2.0;
330 let radius = mid_point.distance(p1);
331 BoundingSphere::new(mid_point + translation, radius)
332 } else {
333 let [a, _, _] = self.vertices;
334
335 let circumcenter = self.circumcenter();
336 let radius = circumcenter.distance(a);
337 BoundingSphere::new(circumcenter + translation, radius)
338 }
339 }
340}
341
342#[cfg(test)]
343mod tests {
344 use crate::bounding::BoundingVolume;
345 use glam::{Quat, Vec3, Vec3A};
346
347 use crate::{
348 bounding::Bounded3d,
349 primitives::{
350 Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, InfinitePlane3d, Line3d, Polyline3d,
351 Segment3d, Sphere, Torus, Triangle3d,
352 },
353 Dir3,
354 };
355
356 #[test]
357 fn sphere() {
358 let sphere = Sphere { radius: 1.0 };
359 let translation = Vec3::new(2.0, 1.0, 0.0);
360
361 let aabb = sphere.aabb_3d(translation, Quat::IDENTITY);
362 assert_eq!(aabb.min, Vec3A::new(1.0, 0.0, -1.0));
363 assert_eq!(aabb.max, Vec3A::new(3.0, 2.0, 1.0));
364
365 let bounding_sphere = sphere.bounding_sphere(translation, Quat::IDENTITY);
366 assert_eq!(bounding_sphere.center, translation.into());
367 assert_eq!(bounding_sphere.radius(), 1.0);
368 }
369
370 #[test]
371 fn plane() {
372 let translation = Vec3::new(2.0, 1.0, 0.0);
373
374 let aabb1 = InfinitePlane3d::new(Vec3::X).aabb_3d(translation, Quat::IDENTITY);
375 assert_eq!(aabb1.min, Vec3A::new(2.0, -f32::MAX / 2.0, -f32::MAX / 2.0));
376 assert_eq!(aabb1.max, Vec3A::new(2.0, f32::MAX / 2.0, f32::MAX / 2.0));
377
378 let aabb2 = InfinitePlane3d::new(Vec3::Y).aabb_3d(translation, Quat::IDENTITY);
379 assert_eq!(aabb2.min, Vec3A::new(-f32::MAX / 2.0, 1.0, -f32::MAX / 2.0));
380 assert_eq!(aabb2.max, Vec3A::new(f32::MAX / 2.0, 1.0, f32::MAX / 2.0));
381
382 let aabb3 = InfinitePlane3d::new(Vec3::Z).aabb_3d(translation, Quat::IDENTITY);
383 assert_eq!(aabb3.min, Vec3A::new(-f32::MAX / 2.0, -f32::MAX / 2.0, 0.0));
384 assert_eq!(aabb3.max, Vec3A::new(f32::MAX / 2.0, f32::MAX / 2.0, 0.0));
385
386 let aabb4 = InfinitePlane3d::new(Vec3::ONE).aabb_3d(translation, Quat::IDENTITY);
387 assert_eq!(aabb4.min, Vec3A::splat(-f32::MAX / 2.0));
388 assert_eq!(aabb4.max, Vec3A::splat(f32::MAX / 2.0));
389
390 let bounding_sphere =
391 InfinitePlane3d::new(Vec3::Y).bounding_sphere(translation, Quat::IDENTITY);
392 assert_eq!(bounding_sphere.center, translation.into());
393 assert_eq!(bounding_sphere.radius(), f32::MAX / 2.0);
394 }
395
396 #[test]
397 fn line() {
398 let translation = Vec3::new(2.0, 1.0, 0.0);
399
400 let aabb1 = Line3d { direction: Dir3::Y }.aabb_3d(translation, Quat::IDENTITY);
401 assert_eq!(aabb1.min, Vec3A::new(2.0, -f32::MAX / 2.0, 0.0));
402 assert_eq!(aabb1.max, Vec3A::new(2.0, f32::MAX / 2.0, 0.0));
403
404 let aabb2 = Line3d { direction: Dir3::X }.aabb_3d(translation, Quat::IDENTITY);
405 assert_eq!(aabb2.min, Vec3A::new(-f32::MAX / 2.0, 1.0, 0.0));
406 assert_eq!(aabb2.max, Vec3A::new(f32::MAX / 2.0, 1.0, 0.0));
407
408 let aabb3 = Line3d { direction: Dir3::Z }.aabb_3d(translation, Quat::IDENTITY);
409 assert_eq!(aabb3.min, Vec3A::new(2.0, 1.0, -f32::MAX / 2.0));
410 assert_eq!(aabb3.max, Vec3A::new(2.0, 1.0, f32::MAX / 2.0));
411
412 let aabb4 = Line3d {
413 direction: Dir3::from_xyz(1.0, 1.0, 1.0).unwrap(),
414 }
415 .aabb_3d(translation, Quat::IDENTITY);
416 assert_eq!(aabb4.min, Vec3A::splat(-f32::MAX / 2.0));
417 assert_eq!(aabb4.max, Vec3A::splat(f32::MAX / 2.0));
418
419 let bounding_sphere =
420 Line3d { direction: Dir3::Y }.bounding_sphere(translation, Quat::IDENTITY);
421 assert_eq!(bounding_sphere.center, translation.into());
422 assert_eq!(bounding_sphere.radius(), f32::MAX / 2.0);
423 }
424
425 #[test]
426 fn segment() {
427 let translation = Vec3::new(2.0, 1.0, 0.0);
428 let segment =
429 Segment3d::from_points(Vec3::new(-1.0, -0.5, 0.0), Vec3::new(1.0, 0.5, 0.0)).0;
430
431 let aabb = segment.aabb_3d(translation, Quat::IDENTITY);
432 assert_eq!(aabb.min, Vec3A::new(1.0, 0.5, 0.0));
433 assert_eq!(aabb.max, Vec3A::new(3.0, 1.5, 0.0));
434
435 let bounding_sphere = segment.bounding_sphere(translation, Quat::IDENTITY);
436 assert_eq!(bounding_sphere.center, translation.into());
437 assert_eq!(bounding_sphere.radius(), 1.0_f32.hypot(0.5));
438 }
439
440 #[test]
441 fn polyline() {
442 let polyline = Polyline3d::<4>::new([
443 Vec3::ONE,
444 Vec3::new(-1.0, 1.0, 1.0),
445 Vec3::NEG_ONE,
446 Vec3::new(1.0, -1.0, -1.0),
447 ]);
448 let translation = Vec3::new(2.0, 1.0, 0.0);
449
450 let aabb = polyline.aabb_3d(translation, Quat::IDENTITY);
451 assert_eq!(aabb.min, Vec3A::new(1.0, 0.0, -1.0));
452 assert_eq!(aabb.max, Vec3A::new(3.0, 2.0, 1.0));
453
454 let bounding_sphere = polyline.bounding_sphere(translation, Quat::IDENTITY);
455 assert_eq!(bounding_sphere.center, translation.into());
456 assert_eq!(bounding_sphere.radius(), 1.0_f32.hypot(1.0).hypot(1.0));
457 }
458
459 #[test]
460 fn cuboid() {
461 let cuboid = Cuboid::new(2.0, 1.0, 1.0);
462 let translation = Vec3::new(2.0, 1.0, 0.0);
463
464 let aabb = cuboid.aabb_3d(
465 translation,
466 Quat::from_rotation_z(std::f32::consts::FRAC_PI_4),
467 );
468 let expected_half_size = Vec3A::new(1.0606601, 1.0606601, 0.5);
469 assert_eq!(aabb.min, Vec3A::from(translation) - expected_half_size);
470 assert_eq!(aabb.max, Vec3A::from(translation) + expected_half_size);
471
472 let bounding_sphere = cuboid.bounding_sphere(translation, Quat::IDENTITY);
473 assert_eq!(bounding_sphere.center, translation.into());
474 assert_eq!(bounding_sphere.radius(), 1.0_f32.hypot(0.5).hypot(0.5));
475 }
476
477 #[test]
478 fn cylinder() {
479 let cylinder = Cylinder::new(0.5, 2.0);
480 let translation = Vec3::new(2.0, 1.0, 0.0);
481
482 let aabb = cylinder.aabb_3d(translation, Quat::IDENTITY);
483 assert_eq!(
484 aabb.min,
485 Vec3A::from(translation) - Vec3A::new(0.5, 1.0, 0.5)
486 );
487 assert_eq!(
488 aabb.max,
489 Vec3A::from(translation) + Vec3A::new(0.5, 1.0, 0.5)
490 );
491
492 let bounding_sphere = cylinder.bounding_sphere(translation, Quat::IDENTITY);
493 assert_eq!(bounding_sphere.center, translation.into());
494 assert_eq!(bounding_sphere.radius(), 1.0_f32.hypot(0.5));
495 }
496
497 #[test]
498 fn capsule() {
499 let capsule = Capsule3d::new(0.5, 2.0);
500 let translation = Vec3::new(2.0, 1.0, 0.0);
501
502 let aabb = capsule.aabb_3d(translation, Quat::IDENTITY);
503 assert_eq!(
504 aabb.min,
505 Vec3A::from(translation) - Vec3A::new(0.5, 1.5, 0.5)
506 );
507 assert_eq!(
508 aabb.max,
509 Vec3A::from(translation) + Vec3A::new(0.5, 1.5, 0.5)
510 );
511
512 let bounding_sphere = capsule.bounding_sphere(translation, Quat::IDENTITY);
513 assert_eq!(bounding_sphere.center, translation.into());
514 assert_eq!(bounding_sphere.radius(), 1.5);
515 }
516
517 #[test]
518 fn cone() {
519 let cone = Cone {
520 radius: 1.0,
521 height: 2.0,
522 };
523 let translation = Vec3::new(2.0, 1.0, 0.0);
524
525 let aabb = cone.aabb_3d(translation, Quat::IDENTITY);
526 assert_eq!(aabb.min, Vec3A::new(1.0, 0.0, -1.0));
527 assert_eq!(aabb.max, Vec3A::new(3.0, 2.0, 1.0));
528
529 let bounding_sphere = cone.bounding_sphere(translation, Quat::IDENTITY);
530 assert_eq!(
531 bounding_sphere.center,
532 Vec3A::from(translation) + Vec3A::NEG_Y * 0.25
533 );
534 assert_eq!(bounding_sphere.radius(), 1.25);
535 }
536
537 #[test]
538 fn conical_frustum() {
539 let conical_frustum = ConicalFrustum {
540 radius_top: 0.5,
541 radius_bottom: 1.0,
542 height: 2.0,
543 };
544 let translation = Vec3::new(2.0, 1.0, 0.0);
545
546 let aabb = conical_frustum.aabb_3d(translation, Quat::IDENTITY);
547 assert_eq!(aabb.min, Vec3A::new(1.0, 0.0, -1.0));
548 assert_eq!(aabb.max, Vec3A::new(3.0, 2.0, 1.0));
549
550 let bounding_sphere = conical_frustum.bounding_sphere(translation, Quat::IDENTITY);
551 assert_eq!(
552 bounding_sphere.center,
553 Vec3A::from(translation) + Vec3A::NEG_Y * 0.1875
554 );
555 assert_eq!(bounding_sphere.radius(), 1.2884705);
556 }
557
558 #[test]
559 fn wide_conical_frustum() {
560 let conical_frustum = ConicalFrustum {
561 radius_top: 0.5,
562 radius_bottom: 5.0,
563 height: 1.0,
564 };
565 let translation = Vec3::new(2.0, 1.0, 0.0);
566
567 let aabb = conical_frustum.aabb_3d(translation, Quat::IDENTITY);
568 assert_eq!(aabb.min, Vec3A::new(-3.0, 0.5, -5.0));
569 assert_eq!(aabb.max, Vec3A::new(7.0, 1.5, 5.0));
570
571 let bounding_sphere = conical_frustum.bounding_sphere(translation, Quat::IDENTITY);
574 assert_eq!(
575 bounding_sphere.center,
576 Vec3A::from(translation) + Vec3A::NEG_Y * 0.5
577 );
578 assert_eq!(bounding_sphere.radius(), 5.0);
579 }
580
581 #[test]
582 fn torus() {
583 let torus = Torus {
584 minor_radius: 0.5,
585 major_radius: 1.0,
586 };
587 let translation = Vec3::new(2.0, 1.0, 0.0);
588
589 let aabb = torus.aabb_3d(translation, Quat::IDENTITY);
590 assert_eq!(aabb.min, Vec3A::new(0.5, 0.5, -1.5));
591 assert_eq!(aabb.max, Vec3A::new(3.5, 1.5, 1.5));
592
593 let bounding_sphere = torus.bounding_sphere(translation, Quat::IDENTITY);
594 assert_eq!(bounding_sphere.center, translation.into());
595 assert_eq!(bounding_sphere.radius(), 1.5);
596 }
597
598 #[test]
599 fn triangle3d() {
600 let zero_degenerate_triangle = Triangle3d::new(Vec3::ZERO, Vec3::ZERO, Vec3::ZERO);
601
602 let br = zero_degenerate_triangle.aabb_3d(Vec3::ZERO, Quat::IDENTITY);
603 assert_eq!(
604 br.center(),
605 Vec3::ZERO.into(),
606 "incorrect bounding box center"
607 );
608 assert_eq!(
609 br.half_size(),
610 Vec3::ZERO.into(),
611 "incorrect bounding box half extents"
612 );
613
614 let bs = zero_degenerate_triangle.bounding_sphere(Vec3::ZERO, Quat::IDENTITY);
615 assert_eq!(
616 bs.center,
617 Vec3::ZERO.into(),
618 "incorrect bounding sphere center"
619 );
620 assert_eq!(bs.sphere.radius, 0.0, "incorrect bounding sphere radius");
621
622 let dup_degenerate_triangle = Triangle3d::new(Vec3::ZERO, Vec3::X, Vec3::X);
623 let bs = dup_degenerate_triangle.bounding_sphere(Vec3::ZERO, Quat::IDENTITY);
624 assert_eq!(
625 bs.center,
626 Vec3::new(0.5, 0.0, 0.0).into(),
627 "incorrect bounding sphere center"
628 );
629 assert_eq!(bs.sphere.radius, 0.5, "incorrect bounding sphere radius");
630 let br = dup_degenerate_triangle.aabb_3d(Vec3::ZERO, Quat::IDENTITY);
631 assert_eq!(
632 br.center(),
633 Vec3::new(0.5, 0.0, 0.0).into(),
634 "incorrect bounding box center"
635 );
636 assert_eq!(
637 br.half_size(),
638 Vec3::new(0.5, 0.0, 0.0).into(),
639 "incorrect bounding box half extents"
640 );
641
642 let collinear_degenerate_triangle = Triangle3d::new(Vec3::NEG_X, Vec3::ZERO, Vec3::X);
643 let bs = collinear_degenerate_triangle.bounding_sphere(Vec3::ZERO, Quat::IDENTITY);
644 assert_eq!(
645 bs.center,
646 Vec3::ZERO.into(),
647 "incorrect bounding sphere center"
648 );
649 assert_eq!(bs.sphere.radius, 1.0, "incorrect bounding sphere radius");
650 let br = collinear_degenerate_triangle.aabb_3d(Vec3::ZERO, Quat::IDENTITY);
651 assert_eq!(
652 br.center(),
653 Vec3::ZERO.into(),
654 "incorrect bounding box center"
655 );
656 assert_eq!(
657 br.half_size(),
658 Vec3::new(1.0, 0.0, 0.0).into(),
659 "incorrect bounding box half extents"
660 );
661 }
662}