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