1use super::{AngularInertiaTensor, ComputeMassProperties3d, MassProperties3d};
2use bevy_math::{
3 FloatPow, Quat, Vec3, ops,
4 prelude::Tetrahedron,
5 primitives::{
6 Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, Line3d, Measured3d, Plane3d, Polyline3d,
7 Segment3d, Sphere, Torus, Triangle3d,
8 },
9};
10use glam_matrix_extras::SymmetricMat3;
11
12impl ComputeMassProperties3d for Sphere {
13 #[inline]
14 fn mass(&self, density: f32) -> f32 {
15 self.volume() * density
16 }
17
18 #[inline]
19 fn unit_principal_angular_inertia(&self) -> Vec3 {
20 Vec3::splat(0.4 * self.radius.squared())
21 }
22
23 #[inline]
24 fn unit_angular_inertia_tensor(&self) -> AngularInertiaTensor {
25 AngularInertiaTensor::new(self.unit_principal_angular_inertia())
26 }
27
28 #[inline]
29 fn center_of_mass(&self) -> Vec3 {
30 Vec3::ZERO
31 }
32}
33
34impl ComputeMassProperties3d for Cuboid {
35 #[inline]
36 fn mass(&self, density: f32) -> f32 {
37 self.volume() * density
38 }
39
40 #[inline]
41 fn unit_principal_angular_inertia(&self) -> Vec3 {
42 let ix = self.half_size.x.squared() / 3.0;
43 let iy = self.half_size.y.squared() / 3.0;
44 let iz = self.half_size.z.squared() / 3.0;
45 Vec3::new(iy + iz, ix + iz, ix + iy)
46 }
47
48 #[inline]
49 fn unit_angular_inertia_tensor(&self) -> AngularInertiaTensor {
50 AngularInertiaTensor::new(self.unit_principal_angular_inertia())
51 }
52
53 #[inline]
54 fn center_of_mass(&self) -> Vec3 {
55 Vec3::ZERO
56 }
57}
58
59impl ComputeMassProperties3d for Cylinder {
60 #[inline]
61 fn mass(&self, density: f32) -> f32 {
62 self.volume() * density
63 }
64
65 #[inline]
66 fn unit_principal_angular_inertia(&self) -> Vec3 {
67 let radius_squared = self.radius.squared();
68 let height_squared = self.half_height.squared() * 4.0;
69 let principal = radius_squared / 2.0;
70 let off_principal = (radius_squared * 3.0 + height_squared) / 12.0;
71 Vec3::new(off_principal, principal, off_principal)
72 }
73
74 #[inline]
75 fn unit_angular_inertia_tensor(&self) -> AngularInertiaTensor {
76 AngularInertiaTensor::new(self.unit_principal_angular_inertia())
77 }
78
79 #[inline]
80 fn center_of_mass(&self) -> Vec3 {
81 Vec3::ZERO
82 }
83}
84
85impl ComputeMassProperties3d for Capsule3d {
86 #[inline]
87 fn mass(&self, density: f32) -> f32 {
88 self.volume() * density
89 }
90
91 #[inline]
92 fn unit_principal_angular_inertia(&self) -> Vec3 {
93 let cylinder = Cylinder {
95 radius: self.radius,
96 half_height: self.half_length,
97 };
98 let cylinder_length = self.half_length * 2.0;
99 let sphere = Sphere::new(self.radius);
100
101 let cylinder_volume = cylinder.volume();
103 let sphere_volume = sphere.volume();
104
105 let density = 1.0 / (cylinder_volume + sphere_volume);
107 let cylinder_mass = cylinder_volume * density;
108 let sphere_mass = sphere_volume * density;
109
110 let cylinder_inertia = cylinder.principal_angular_inertia(cylinder_mass);
112 let sphere_inertia = sphere.principal_angular_inertia(sphere_mass);
113
114 let mut capsule_inertia = cylinder_inertia + sphere_inertia;
116
117 let extra = (cylinder_length.squared() * 0.25 + cylinder_length * self.radius * 3.0 / 8.0)
119 * sphere_mass;
120 capsule_inertia.x += extra;
121 capsule_inertia.z += extra;
122
123 capsule_inertia
124 }
125
126 #[inline]
127 fn unit_angular_inertia_tensor(&self) -> AngularInertiaTensor {
128 AngularInertiaTensor::new(self.unit_principal_angular_inertia())
129 }
130
131 #[inline]
132 fn center_of_mass(&self) -> Vec3 {
133 Vec3::ZERO
134 }
135
136 #[inline]
137 fn mass_properties(&self, density: f32) -> MassProperties3d {
138 let cylinder = Cylinder {
140 radius: self.radius,
141 half_height: self.half_length,
142 };
143 let cylinder_length = self.half_length * 2.0;
144 let sphere = Sphere::new(self.radius);
145
146 let cylinder_volume = cylinder.volume();
148 let sphere_volume = sphere.volume();
149
150 let cylinder_mass = cylinder_volume * density;
152 let sphere_mass = sphere_volume * density;
153
154 let cylinder_inertia = cylinder.principal_angular_inertia(cylinder_mass);
156 let sphere_inertia = sphere.principal_angular_inertia(sphere_mass);
157
158 let mut capsule_inertia = cylinder_inertia + sphere_inertia;
160
161 let extra = (cylinder_length.squared() * 0.25 + cylinder_length * self.radius * 3.0 / 8.0)
163 * sphere_mass;
164 capsule_inertia.x += extra;
165 capsule_inertia.z += extra;
166
167 MassProperties3d::new(cylinder_mass + sphere_mass, capsule_inertia, Vec3::ZERO)
168 }
169}
170
171impl ComputeMassProperties3d for Cone {
172 #[inline]
173 fn mass(&self, density: f32) -> f32 {
174 self.volume() * density
175 }
176
177 #[inline]
178 fn unit_principal_angular_inertia(&self) -> Vec3 {
179 let radius_squared = self.radius.squared();
180 let height_squared = self.height.squared();
181
182 let principal = 3.0 / 10.0 * radius_squared;
184
185 let off_principal = principal * 0.5 + 3.0 / 80.0 * height_squared;
187
188 Vec3::new(off_principal, principal, off_principal)
189 }
190
191 #[inline]
192 fn unit_angular_inertia_tensor(&self) -> AngularInertiaTensor {
193 AngularInertiaTensor::new(self.unit_principal_angular_inertia())
194 }
195
196 #[inline]
197 fn center_of_mass(&self) -> Vec3 {
198 Vec3::new(0.0, -self.height * 0.25, 0.0)
199 }
200}
201
202impl ComputeMassProperties3d for ConicalFrustum {
203 #[inline]
204 fn mass(&self, density: f32) -> f32 {
205 if self.radius_top == self.radius_bottom {
206 Cylinder::new(self.radius_top, self.height).mass(density)
207 } else {
208 let radii_squared = self.radius_top.squared() + self.radius_bottom.squared();
210 let volume = core::f32::consts::FRAC_PI_3
211 * self.height
212 * (radii_squared + self.radius_top * self.radius_bottom);
213 volume * density
214 }
215 }
216
217 #[inline]
218 fn unit_principal_angular_inertia(&self) -> Vec3 {
219 if self.radius_top == self.radius_bottom {
220 Cylinder::new(self.radius_top, self.height).unit_principal_angular_inertia()
221 } else {
222 let (min_radius, max_radius) = if self.radius_top < self.radius_bottom {
223 (self.radius_top, self.radius_bottom)
224 } else {
225 (self.radius_bottom, self.radius_top)
226 };
227
228 let cone_height =
240 max_radius * (self.height / ops::abs(self.radius_top - self.radius_bottom));
241 let large_cone = Cone {
242 radius: max_radius,
243 height: cone_height,
244 };
245 let small_cone = Cone {
246 radius: min_radius,
247 height: cone_height - self.height,
248 };
249
250 let large_cone_volume = large_cone.volume();
253 let small_cone_volume = small_cone.volume();
254 let volume = large_cone_volume - small_cone_volume;
255
256 let large_cone_volume_fraction = large_cone_volume / volume;
259 let small_cone_volume_fraction = small_cone_volume / volume;
260
261 let large_cone_angular_inertia =
262 large_cone.principal_angular_inertia(large_cone_volume_fraction);
263 let mut small_cone_angular_inertia =
264 small_cone.principal_angular_inertia(small_cone_volume_fraction);
265
266 let d = 0.5 * (self.height + small_cone.height) + small_cone.center_of_mass().y;
275 let extra_angular_inertia = small_cone_volume_fraction * d * d;
276 small_cone_angular_inertia.x += extra_angular_inertia;
277 small_cone_angular_inertia.z += extra_angular_inertia;
278
279 large_cone_angular_inertia.x - small_cone_angular_inertia
281 }
282 }
283
284 #[inline]
285 fn unit_angular_inertia_tensor(&self) -> AngularInertiaTensor {
286 AngularInertiaTensor::new(self.unit_principal_angular_inertia())
287 }
288
289 #[inline]
290 fn center_of_mass(&self) -> Vec3 {
291 if self.radius_top == self.radius_bottom {
292 Vec3::ZERO
293 } else {
294 let (min_radius, max_radius) = if self.radius_top < self.radius_bottom {
297 (self.radius_top, self.radius_bottom)
298 } else {
299 (self.radius_bottom, self.radius_top)
300 };
301
302 let min_radius_squared = min_radius.squared();
303 let max_radius_squared = max_radius.squared();
304 let radii_product = self.radius_top * self.radius_bottom;
305
306 let y = self.height
307 * ((max_radius_squared + 2.0 * radii_product + 3.0 * min_radius_squared)
308 / (4.0 * (max_radius_squared + radii_product + min_radius_squared))
309 - 0.5);
310
311 Vec3::new(0.0, y, 0.0)
312 }
313 }
314
315 #[inline]
316 fn mass_properties(&self, density: f32) -> MassProperties3d {
317 if self.radius_top == self.radius_bottom {
318 Cylinder::new(self.radius_top, self.height).mass_properties(density)
319 } else {
320 let (min_radius, max_radius) = if self.radius_top < self.radius_bottom {
323 (self.radius_top, self.radius_bottom)
324 } else {
325 (self.radius_bottom, self.radius_top)
326 };
327
328 let cone_height =
334 max_radius * (self.height / ops::abs(self.radius_top - self.radius_bottom));
335 let large_cone = Cone {
336 radius: max_radius,
337 height: cone_height,
338 };
339 let small_cone = Cone {
340 radius: min_radius,
341 height: cone_height - self.height,
342 };
343
344 let large_cone_volume = large_cone.volume();
347 let small_cone_volume = small_cone.volume();
348 let volume = large_cone_volume - small_cone_volume;
349
350 let mass = volume * density;
352
353 let large_cone_volume_fraction = large_cone_volume / volume;
355 let small_cone_volume_fraction = small_cone_volume / volume;
356
357 let large_cone_angular_inertia =
358 large_cone.principal_angular_inertia(large_cone_volume_fraction);
359 let mut small_cone_angular_inertia =
360 small_cone.principal_angular_inertia(small_cone_volume_fraction);
361
362 let d = 0.5 * (self.height + small_cone.height) + small_cone.center_of_mass().y;
371 let extra_angular_inertia = small_cone_volume_fraction * d * d;
372 small_cone_angular_inertia.x += extra_angular_inertia;
373 small_cone_angular_inertia.z += extra_angular_inertia;
374
375 let principal_angular_inertia =
377 mass * (large_cone_angular_inertia.x - small_cone_angular_inertia);
378
379 let min_radius_squared = min_radius.squared();
380 let max_radius_squared = max_radius.squared();
381 let radii_product = self.radius_top * self.radius_bottom;
382
383 let y = self.height
385 * ((max_radius_squared + 2.0 * radii_product + 3.0 * min_radius_squared)
386 / (4.0 * (max_radius_squared + radii_product + min_radius_squared))
387 - 0.5);
388 let center_of_mass = Vec3::new(0.0, y, 0.0);
389
390 MassProperties3d::new(mass, principal_angular_inertia, center_of_mass)
391 }
392 }
393}
394
395impl ComputeMassProperties3d for Torus {
396 #[inline]
397 fn mass(&self, density: f32) -> f32 {
398 self.volume() * density
399 }
400
401 #[inline]
402 fn unit_principal_angular_inertia(&self) -> Vec3 {
403 let major_radius_squared = self.major_radius.squared();
406 let minor_radius_squared = self.minor_radius.squared();
407
408 let principal = 0.25 * (4.0 * major_radius_squared + 3.0 * minor_radius_squared);
409 let off_principal = 1.0 / 8.0 * (4.0 * major_radius_squared + 5.0 * minor_radius_squared);
410 Vec3::new(off_principal, principal, off_principal)
411 }
412
413 #[inline]
414 fn unit_angular_inertia_tensor(&self) -> AngularInertiaTensor {
415 AngularInertiaTensor::new(self.unit_principal_angular_inertia())
416 }
417
418 #[inline]
419 fn center_of_mass(&self) -> Vec3 {
420 Vec3::ZERO
421 }
422}
423
424impl ComputeMassProperties3d for Tetrahedron {
425 #[inline]
426 fn mass(&self, density: f32) -> f32 {
427 self.volume() * density
428 }
429
430 #[inline]
431 fn unit_principal_angular_inertia(&self) -> Vec3 {
432 let tensor = self.unit_angular_inertia_tensor();
433 tensor.principal_angular_inertia_with_local_frame().0
434 }
435
436 #[inline]
437 fn local_inertial_frame(&self) -> Quat {
438 let tensor = self.unit_angular_inertia_tensor();
439 tensor.principal_angular_inertia_with_local_frame().1
440 }
441
442 #[inline]
443 fn unit_angular_inertia_tensor(&self) -> AngularInertiaTensor {
444 let [p1, p2, p3, p4] = self.vertices;
449
450 let x1 = p1.x;
452 let y1 = p1.y;
453 let z1 = p1.z;
454 let x2 = p2.x;
455 let y2 = p2.y;
456 let z2 = p2.z;
457 let x3 = p3.x;
458 let y3 = p3.y;
459 let z3 = p3.z;
460 let x4 = p4.x;
461 let y4 = p4.y;
462 let z4 = p4.z;
463
464 let diag_x = x1 * x1
465 + x1 * x2
466 + x2 * x2
467 + x1 * x3
468 + x2 * x3
469 + x3 * x3
470 + x1 * x4
471 + x2 * x4
472 + x3 * x4
473 + x4 * x4;
474 let diag_y = y1 * y1
475 + y1 * y2
476 + y2 * y2
477 + y1 * y3
478 + y2 * y3
479 + y3 * y3
480 + y1 * y4
481 + y2 * y4
482 + y3 * y4
483 + y4 * y4;
484 let diag_z = z1 * z1
485 + z1 * z2
486 + z2 * z2
487 + z1 * z3
488 + z2 * z3
489 + z3 * z3
490 + z1 * z4
491 + z2 * z4
492 + z3 * z4
493 + z4 * z4;
494
495 let a0 = (diag_y + diag_z) * 0.1;
496 let b0 = (diag_z + diag_x) * 0.1;
497 let c0 = (diag_x + diag_y) * 0.1;
498
499 let a1 = (y1 * z1 * 2.0
500 + y2 * z1
501 + y3 * z1
502 + y4 * z1
503 + y1 * z2
504 + y2 * z2 * 2.0
505 + y3 * z2
506 + y4 * z2
507 + y1 * z3
508 + y2 * z3
509 + y3 * z3 * 2.0
510 + y4 * z3
511 + y1 * z4
512 + y2 * z4
513 + y3 * z4
514 + y4 * z4 * 2.0)
515 * 0.05;
516 let b1 = (x1 * z1 * 2.0
517 + x2 * z1
518 + x3 * z1
519 + x4 * z1
520 + x1 * z2
521 + x2 * z2 * 2.0
522 + x3 * z2
523 + x4 * z2
524 + x1 * z3
525 + x2 * z3
526 + x3 * z3 * 2.0
527 + x4 * z3
528 + x1 * z4
529 + x2 * z4
530 + x3 * z4
531 + x4 * z4 * 2.0)
532 * 0.05;
533 let c1 = (x1 * y1 * 2.0
534 + x2 * y1
535 + x3 * y1
536 + x4 * y1
537 + x1 * y2
538 + x2 * y2 * 2.0
539 + x3 * y2
540 + x4 * y2
541 + x1 * y3
542 + x2 * y3
543 + x3 * y3 * 2.0
544 + x4 * y3
545 + x1 * y4
546 + x2 * y4
547 + x3 * y4
548 + x4 * y4 * 2.0)
549 * 0.05;
550
551 AngularInertiaTensor::from_symmetric_mat3(SymmetricMat3::new(a0, -b1, -c1, b0, -a1, c0))
553 }
554
555 #[inline]
556 fn center_of_mass(&self) -> Vec3 {
557 (self.vertices[0] + self.vertices[1] + self.vertices[2] + self.vertices[3]) / 4.0
558 }
559
560 #[inline]
561 fn mass_properties(&self, density: f32) -> MassProperties3d {
562 let volume = self.volume();
563 let center_of_mass = self.center_of_mass();
564
565 if volume < f32::EPSILON {
566 return MassProperties3d::new(0.0, Vec3::ZERO, center_of_mass);
567 }
568
569 let mass = volume * density;
570 let tensor = self.angular_inertia_tensor(mass);
571
572 MassProperties3d::new_with_angular_inertia_tensor(mass, tensor, center_of_mass)
573 }
574}
575
576macro_rules! impl_zero_mass_properties_3d {
577 ($($shape:ty),*) => {
578 $(
579 impl ComputeMassProperties3d for $shape {
580 #[inline]
581 fn mass(&self, _density: f32) -> f32 {
582 0.0
583 }
584
585 #[inline]
586 fn unit_principal_angular_inertia(&self) -> Vec3 {
587 Vec3::ZERO
588 }
589
590 #[inline]
591 fn principal_angular_inertia(&self, _mass: f32) -> Vec3 {
592 Vec3::ZERO
593 }
594
595 #[inline]
596 fn local_inertial_frame(&self) -> Quat {
597 Quat::IDENTITY
598 }
599
600 #[inline]
601 fn unit_angular_inertia_tensor(&self) -> AngularInertiaTensor {
602 AngularInertiaTensor::ZERO
603 }
604
605 #[inline]
606 fn angular_inertia_tensor(&self, _mass: f32) -> AngularInertiaTensor {
607 AngularInertiaTensor::ZERO
608 }
609
610 #[inline]
611 fn center_of_mass(&self) -> Vec3 {
612 Vec3::ZERO
613 }
614
615 #[inline]
616 fn mass_properties(&self, _density: f32) -> MassProperties3d {
617 MassProperties3d::ZERO
618 }
619 }
620 )*
621 };
622}
623
624impl_zero_mass_properties_3d!(Plane3d);
625impl_zero_mass_properties_3d!(Line3d);
626impl_zero_mass_properties_3d!(Segment3d);
627impl_zero_mass_properties_3d!(Triangle3d);
628
629impl ComputeMassProperties3d for Polyline3d {
630 #[inline]
631 fn mass(&self, _density: f32) -> f32 {
632 0.0
633 }
634
635 #[inline]
636 fn unit_principal_angular_inertia(&self) -> Vec3 {
637 Vec3::ZERO
638 }
639
640 #[inline]
641 fn principal_angular_inertia(&self, _mass: f32) -> Vec3 {
642 Vec3::ZERO
643 }
644
645 #[inline]
646 fn local_inertial_frame(&self) -> bevy_math::Quat {
647 Quat::IDENTITY
648 }
649
650 #[inline]
651 fn unit_angular_inertia_tensor(&self) -> AngularInertiaTensor {
652 AngularInertiaTensor::ZERO
653 }
654
655 #[inline]
656 fn angular_inertia_tensor(&self, _mass: f32) -> AngularInertiaTensor {
657 AngularInertiaTensor::ZERO
658 }
659
660 #[inline]
661 fn center_of_mass(&self) -> Vec3 {
662 Vec3::ZERO
663 }
664
665 #[inline]
666 fn mass_properties(&self, _density: f32) -> MassProperties3d {
667 MassProperties3d::ZERO
668 }
669}
670
671#[cfg(test)]
672mod tests {
673 use alloc::vec::Vec;
674
675 use approx::assert_relative_eq;
676 use bevy_math::{
677 Isometry3d, ShapeSample, Vec3Swizzles,
678 bounding::{Bounded3d, BoundingVolume},
679 };
680 use rand::SeedableRng;
681
682 use super::*;
683
684 macro_rules! test_shape {
685 ($test_name:tt, $shape:expr, $point_generator:expr) => {
686 #[test]
687 fn $test_name() {
688 let shape = $shape;
689
690 let points: Vec<Vec3> = $point_generator(&shape);
692
693 let density = 2.0;
695 let mass = shape.mass(density);
696 let principal_angular_inertia = shape.principal_angular_inertia(mass);
697 let local_inertial_frame = shape.local_inertial_frame();
698 let angular_inertia_tensor = shape.angular_inertia_tensor(mass);
699 let center_of_mass = shape.center_of_mass();
700
701 let mass_props = shape.mass_properties(density);
703 assert_relative_eq!(mass, mass_props.mass);
704 assert_relative_eq!(
705 principal_angular_inertia,
706 mass_props.principal_angular_inertia
707 );
708 assert_relative_eq!(center_of_mass, mass_props.center_of_mass);
709
710 let expected =
714 MassProperties3d::from_point_cloud(&points, mass, local_inertial_frame);
715
716 assert_relative_eq!(mass, expected.mass);
717 assert_relative_eq!(
718 principal_angular_inertia,
719 expected.principal_angular_inertia,
720 epsilon = 0.1
721 );
722 assert_relative_eq!(center_of_mass, expected.center_of_mass, epsilon = 0.01);
723
724 assert_relative_eq!(
727 angular_inertia_tensor,
728 AngularInertiaTensor::new_with_local_frame(
729 principal_angular_inertia,
730 local_inertial_frame
731 ),
732 epsilon = 1e-5
733 );
734
735 let (principal, frame) =
742 angular_inertia_tensor.principal_angular_inertia_with_local_frame();
743 assert_relative_eq!(
744 AngularInertiaTensor::new_with_local_frame(principal, frame),
745 angular_inertia_tensor,
746 epsilon = 1e-5
747 );
748 }
749 };
750 }
751
752 fn sample_shape<S: ShapeSample<Output = Vec3>>(shape: &S) -> Vec<S::Output> {
753 let mut rng = rand_chacha::ChaCha8Rng::from_seed(Default::default());
754 (0..2_000_000)
755 .map(|_| shape.sample_interior(&mut rng))
756 .collect::<Vec<_>>()
757 }
758
759 fn rejection_sample_shape<S: Bounded3d>(
760 func: impl Fn(&S, Vec3) -> bool,
761 shape: &S,
762 ) -> Vec<Vec3> {
763 let mut rng = rand_chacha::ChaCha8Rng::from_seed(Default::default());
764 let mut points = Vec::new();
765 let aabb = shape.aabb_3d(Isometry3d::IDENTITY);
766 let aabb_center: Vec3 = aabb.center().into();
767 let cuboid = Cuboid {
768 half_size: aabb.half_size().into(),
769 };
770 while points.len() < 2_000_000 {
771 let point = aabb_center + cuboid.sample_interior(&mut rng);
772 if func(shape, point) {
773 points.push(point);
774 }
775 }
776 points
777 }
778
779 test_shape!(sphere, Sphere::new(2.0), sample_shape);
780 test_shape!(cuboid, Cuboid::new(1.0, 2.0, 3.0), sample_shape);
781 test_shape!(cylinder, Cylinder::new(1.0, 4.0), sample_shape);
782 test_shape!(capsule, Capsule3d::new(1.0, 2.0), sample_shape);
783 test_shape!(
784 cone,
785 Cone {
786 radius: 1.0,
787 height: 2.0,
788 },
789 |shape| rejection_sample_shape(cone_contains_point, shape)
790 );
791 test_shape!(
792 conical_frustum,
793 ConicalFrustum {
794 radius_top: 0.5,
795 radius_bottom: 1.0,
796 height: 1.0
797 },
798 |shape| rejection_sample_shape(conical_frustum_contains_point, shape)
799 );
800 test_shape!(torus, Torus::new(1.0, 2.0), |shape| rejection_sample_shape(
801 torus_contains_point,
802 shape,
803 ));
804 test_shape!(
805 tetrahedron,
806 Tetrahedron::new(
807 Vec3::new(0.0, 0.0, 0.0),
808 Vec3::new(1.0, 0.0, 0.0),
809 Vec3::new(0.0, 1.0, 0.0),
810 Vec3::new(0.0, 0.0, 1.0),
811 ),
812 sample_shape
813 );
814
815 fn cone_contains_point(cone: &Cone, point: Vec3) -> bool {
817 let half_height = cone.height * 0.5;
818
819 if point.y < -half_height || point.y > half_height {
820 return false;
821 }
822
823 let pb_dot_ba = -cone.height * (point.y + half_height);
826
827 let delta_radius = pb_dot_ba * cone.radius / cone.height.squared();
841 let radius = cone.radius + delta_radius;
842
843 let ortho_distance_squared = point.xz().length_squared();
845
846 ortho_distance_squared < radius * radius
847 }
848
849 fn conical_frustum_contains_point(frustum: &ConicalFrustum, point: Vec3) -> bool {
851 let half_height = frustum.height * 0.5;
852
853 if point.y < -half_height || point.y > half_height {
854 return false;
855 }
856
857 let pb_dot_ba = -frustum.height * (point.y + half_height);
860
861 let delta_radius =
875 pb_dot_ba * (frustum.radius_bottom - frustum.radius_top) / frustum.height.squared();
876 let radius = frustum.radius_bottom + delta_radius;
877
878 let ortho_distance_squared = point.xz().length_squared();
880
881 ortho_distance_squared < radius * radius
882 }
883
884 fn torus_contains_point(torus: &Torus, point: Vec3) -> bool {
886 let minor_radius_squared = torus.minor_radius * torus.minor_radius;
887 (torus.major_radius - point.xz().length()).squared() + point.y.squared()
888 < minor_radius_squared
889 }
890}