1use super::{AngularInertiaTensor, ComputeMassProperties3d, MassProperties3d};
2use bevy_math::{
3 ops,
4 prelude::Tetrahedron,
5 primitives::{
6 Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, Line3d, Measured3d, Plane3d, Polyline3d,
7 Segment3d, Sphere, Torus,
8 },
9 FloatPow, Quat, Vec3,
10};
11use glam_matrix_extras::SymmetricMat3;
12
13impl ComputeMassProperties3d for Sphere {
14 #[inline]
15 fn mass(&self, density: f32) -> f32 {
16 self.volume() * density
17 }
18
19 #[inline]
20 fn unit_principal_angular_inertia(&self) -> Vec3 {
21 Vec3::splat(0.4 * self.radius.squared())
22 }
23
24 #[inline]
25 fn unit_angular_inertia_tensor(&self) -> AngularInertiaTensor {
26 AngularInertiaTensor::new(self.unit_principal_angular_inertia())
27 }
28
29 #[inline]
30 fn center_of_mass(&self) -> Vec3 {
31 Vec3::ZERO
32 }
33}
34
35impl ComputeMassProperties3d for Cuboid {
36 #[inline]
37 fn mass(&self, density: f32) -> f32 {
38 self.volume() * density
39 }
40
41 #[inline]
42 fn unit_principal_angular_inertia(&self) -> Vec3 {
43 let ix = self.half_size.x.squared() / 3.0;
44 let iy = self.half_size.y.squared() / 3.0;
45 let iz = self.half_size.z.squared() / 3.0;
46 Vec3::new(iy + iz, ix + iz, ix + iy)
47 }
48
49 #[inline]
50 fn unit_angular_inertia_tensor(&self) -> AngularInertiaTensor {
51 AngularInertiaTensor::new(self.unit_principal_angular_inertia())
52 }
53
54 #[inline]
55 fn center_of_mass(&self) -> Vec3 {
56 Vec3::ZERO
57 }
58}
59
60impl ComputeMassProperties3d for Cylinder {
61 #[inline]
62 fn mass(&self, density: f32) -> f32 {
63 self.volume() * density
64 }
65
66 #[inline]
67 fn unit_principal_angular_inertia(&self) -> Vec3 {
68 let radius_squared = self.radius.squared();
69 let height_squared = self.half_height.squared() * 4.0;
70 let principal = radius_squared / 2.0;
71 let off_principal = (radius_squared * 3.0 + height_squared) / 12.0;
72 Vec3::new(off_principal, principal, off_principal)
73 }
74
75 #[inline]
76 fn unit_angular_inertia_tensor(&self) -> AngularInertiaTensor {
77 AngularInertiaTensor::new(self.unit_principal_angular_inertia())
78 }
79
80 #[inline]
81 fn center_of_mass(&self) -> Vec3 {
82 Vec3::ZERO
83 }
84}
85
86impl ComputeMassProperties3d for Capsule3d {
87 #[inline]
88 fn mass(&self, density: f32) -> f32 {
89 self.volume() * density
90 }
91
92 #[inline]
93 fn unit_principal_angular_inertia(&self) -> Vec3 {
94 let cylinder = Cylinder {
96 radius: self.radius,
97 half_height: self.half_length,
98 };
99 let cylinder_length = self.half_length * 2.0;
100 let sphere = Sphere::new(self.radius);
101
102 let cylinder_volume = cylinder.volume();
104 let sphere_volume = sphere.volume();
105
106 let density = 1.0 / (cylinder_volume + sphere_volume);
108 let cylinder_mass = cylinder_volume * density;
109 let sphere_mass = sphere_volume * density;
110
111 let cylinder_inertia = cylinder.principal_angular_inertia(cylinder_mass);
113 let sphere_inertia = sphere.principal_angular_inertia(sphere_mass);
114
115 let mut capsule_inertia = cylinder_inertia + sphere_inertia;
117
118 let extra = (cylinder_length.squared() * 0.25 + cylinder_length * self.radius * 3.0 / 8.0)
120 * sphere_mass;
121 capsule_inertia.x += extra;
122 capsule_inertia.z += extra;
123
124 capsule_inertia
125 }
126
127 #[inline]
128 fn unit_angular_inertia_tensor(&self) -> AngularInertiaTensor {
129 AngularInertiaTensor::new(self.unit_principal_angular_inertia())
130 }
131
132 #[inline]
133 fn center_of_mass(&self) -> Vec3 {
134 Vec3::ZERO
135 }
136
137 #[inline]
138 fn mass_properties(&self, density: f32) -> MassProperties3d {
139 let cylinder = Cylinder {
141 radius: self.radius,
142 half_height: self.half_length,
143 };
144 let cylinder_length = self.half_length * 2.0;
145 let sphere = Sphere::new(self.radius);
146
147 let cylinder_volume = cylinder.volume();
149 let sphere_volume = sphere.volume();
150
151 let cylinder_mass = cylinder_volume * density;
153 let sphere_mass = sphere_volume * density;
154
155 let cylinder_inertia = cylinder.principal_angular_inertia(cylinder_mass);
157 let sphere_inertia = sphere.principal_angular_inertia(sphere_mass);
158
159 let mut capsule_inertia = cylinder_inertia + sphere_inertia;
161
162 let extra = (cylinder_length.squared() * 0.25 + cylinder_length * self.radius * 3.0 / 8.0)
164 * sphere_mass;
165 capsule_inertia.x += extra;
166 capsule_inertia.z += extra;
167
168 MassProperties3d::new(cylinder_mass + sphere_mass, capsule_inertia, Vec3::ZERO)
169 }
170}
171
172impl ComputeMassProperties3d for Cone {
173 #[inline]
174 fn mass(&self, density: f32) -> f32 {
175 self.volume() * density
176 }
177
178 #[inline]
179 fn unit_principal_angular_inertia(&self) -> Vec3 {
180 let radius_squared = self.radius.squared();
181 let height_squared = self.height.squared();
182
183 let principal = 3.0 / 10.0 * radius_squared;
185
186 let off_principal = principal * 0.5 + 3.0 / 80.0 * height_squared;
188
189 Vec3::new(off_principal, principal, off_principal)
190 }
191
192 #[inline]
193 fn unit_angular_inertia_tensor(&self) -> AngularInertiaTensor {
194 AngularInertiaTensor::new(self.unit_principal_angular_inertia())
195 }
196
197 #[inline]
198 fn center_of_mass(&self) -> Vec3 {
199 Vec3::new(0.0, -self.height * 0.25, 0.0)
200 }
201}
202
203impl ComputeMassProperties3d for ConicalFrustum {
204 #[inline]
205 fn mass(&self, density: f32) -> f32 {
206 if self.radius_top == self.radius_bottom {
207 Cylinder::new(self.radius_top, self.height).mass(density)
208 } else {
209 let radii_squared = self.radius_top.squared() + self.radius_bottom.squared();
211 let volume = core::f32::consts::FRAC_PI_3
212 * self.height
213 * (radii_squared + self.radius_top * self.radius_bottom);
214 volume * density
215 }
216 }
217
218 #[inline]
219 fn unit_principal_angular_inertia(&self) -> Vec3 {
220 if self.radius_top == self.radius_bottom {
221 Cylinder::new(self.radius_top, self.height).unit_principal_angular_inertia()
222 } else {
223 let (min_radius, max_radius) = if self.radius_top < self.radius_bottom {
224 (self.radius_top, self.radius_bottom)
225 } else {
226 (self.radius_bottom, self.radius_top)
227 };
228
229 let cone_height =
241 max_radius * (self.height / ops::abs(self.radius_top - self.radius_bottom));
242 let large_cone = Cone {
243 radius: max_radius,
244 height: cone_height,
245 };
246 let small_cone = Cone {
247 radius: min_radius,
248 height: cone_height - self.height,
249 };
250
251 let large_cone_volume = large_cone.volume();
254 let small_cone_volume = small_cone.volume();
255 let volume = large_cone_volume - small_cone_volume;
256
257 let large_cone_volume_fraction = large_cone_volume / volume;
260 let small_cone_volume_fraction = small_cone_volume / volume;
261
262 let large_cone_angular_inertia =
263 large_cone.principal_angular_inertia(large_cone_volume_fraction);
264 let mut small_cone_angular_inertia =
265 small_cone.principal_angular_inertia(small_cone_volume_fraction);
266
267 let d = 0.5 * (self.height + small_cone.height) + small_cone.center_of_mass().y;
276 let extra_angular_inertia = small_cone_volume_fraction * d * d;
277 small_cone_angular_inertia.x += extra_angular_inertia;
278 small_cone_angular_inertia.z += extra_angular_inertia;
279
280 large_cone_angular_inertia.x - small_cone_angular_inertia
282 }
283 }
284
285 #[inline]
286 fn unit_angular_inertia_tensor(&self) -> AngularInertiaTensor {
287 AngularInertiaTensor::new(self.unit_principal_angular_inertia())
288 }
289
290 #[inline]
291 fn center_of_mass(&self) -> Vec3 {
292 if self.radius_top == self.radius_bottom {
293 Vec3::ZERO
294 } else {
295 let (min_radius, max_radius) = if self.radius_top < self.radius_bottom {
298 (self.radius_top, self.radius_bottom)
299 } else {
300 (self.radius_bottom, self.radius_top)
301 };
302
303 let min_radius_squared = min_radius.squared();
304 let max_radius_squared = max_radius.squared();
305 let radii_product = self.radius_top * self.radius_bottom;
306
307 let y = self.height
308 * ((max_radius_squared + 2.0 * radii_product + 3.0 * min_radius_squared)
309 / (4.0 * (max_radius_squared + radii_product + min_radius_squared))
310 - 0.5);
311
312 Vec3::new(0.0, y, 0.0)
313 }
314 }
315
316 #[inline]
317 fn mass_properties(&self, density: f32) -> MassProperties3d {
318 if self.radius_top == self.radius_bottom {
319 Cylinder::new(self.radius_top, self.height).mass_properties(density)
320 } else {
321 let (min_radius, max_radius) = if self.radius_top < self.radius_bottom {
324 (self.radius_top, self.radius_bottom)
325 } else {
326 (self.radius_bottom, self.radius_top)
327 };
328
329 let cone_height =
335 max_radius * (self.height / ops::abs(self.radius_top - self.radius_bottom));
336 let large_cone = Cone {
337 radius: max_radius,
338 height: cone_height,
339 };
340 let small_cone = Cone {
341 radius: min_radius,
342 height: cone_height - self.height,
343 };
344
345 let large_cone_volume = large_cone.volume();
348 let small_cone_volume = small_cone.volume();
349 let volume = large_cone_volume - small_cone_volume;
350
351 let mass = volume * density;
353
354 let large_cone_volume_fraction = large_cone_volume / volume;
356 let small_cone_volume_fraction = small_cone_volume / volume;
357
358 let large_cone_angular_inertia =
359 large_cone.principal_angular_inertia(large_cone_volume_fraction);
360 let mut small_cone_angular_inertia =
361 small_cone.principal_angular_inertia(small_cone_volume_fraction);
362
363 let d = 0.5 * (self.height + small_cone.height) + small_cone.center_of_mass().y;
372 let extra_angular_inertia = small_cone_volume_fraction * d * d;
373 small_cone_angular_inertia.x += extra_angular_inertia;
374 small_cone_angular_inertia.z += extra_angular_inertia;
375
376 let principal_angular_inertia =
378 mass * (large_cone_angular_inertia.x - small_cone_angular_inertia);
379
380 let min_radius_squared = min_radius.squared();
381 let max_radius_squared = max_radius.squared();
382 let radii_product = self.radius_top * self.radius_bottom;
383
384 let y = self.height
386 * ((max_radius_squared + 2.0 * radii_product + 3.0 * min_radius_squared)
387 / (4.0 * (max_radius_squared + radii_product + min_radius_squared))
388 - 0.5);
389 let center_of_mass = Vec3::new(0.0, y, 0.0);
390
391 MassProperties3d::new(mass, principal_angular_inertia, center_of_mass)
392 }
393 }
394}
395
396impl ComputeMassProperties3d for Torus {
397 #[inline]
398 fn mass(&self, density: f32) -> f32 {
399 self.volume() * density
400 }
401
402 #[inline]
403 fn unit_principal_angular_inertia(&self) -> Vec3 {
404 let major_radius_squared = self.major_radius.squared();
407 let minor_radius_squared = self.minor_radius.squared();
408
409 let principal = 0.25 * (4.0 * major_radius_squared + 3.0 * minor_radius_squared);
410 let off_principal = 1.0 / 8.0 * (4.0 * major_radius_squared + 5.0 * minor_radius_squared);
411 Vec3::new(off_principal, principal, off_principal)
412 }
413
414 #[inline]
415 fn unit_angular_inertia_tensor(&self) -> AngularInertiaTensor {
416 AngularInertiaTensor::new(self.unit_principal_angular_inertia())
417 }
418
419 #[inline]
420 fn center_of_mass(&self) -> Vec3 {
421 Vec3::ZERO
422 }
423}
424
425impl ComputeMassProperties3d for Tetrahedron {
426 #[inline]
427 fn mass(&self, density: f32) -> f32 {
428 self.volume() * density
429 }
430
431 #[inline]
432 fn unit_principal_angular_inertia(&self) -> Vec3 {
433 let tensor = self.unit_angular_inertia_tensor();
434 tensor.principal_angular_inertia_with_local_frame().0
435 }
436
437 #[inline]
438 fn local_inertial_frame(&self) -> Quat {
439 let tensor = self.unit_angular_inertia_tensor();
440 tensor.principal_angular_inertia_with_local_frame().1
441 }
442
443 #[inline]
444 fn unit_angular_inertia_tensor(&self) -> AngularInertiaTensor {
445 let [p1, p2, p3, p4] = self.vertices;
450
451 let x1 = p1.x;
453 let y1 = p1.y;
454 let z1 = p1.z;
455 let x2 = p2.x;
456 let y2 = p2.y;
457 let z2 = p2.z;
458 let x3 = p3.x;
459 let y3 = p3.y;
460 let z3 = p3.z;
461 let x4 = p4.x;
462 let y4 = p4.y;
463 let z4 = p4.z;
464
465 let diag_x = x1 * x1
466 + x1 * x2
467 + x2 * x2
468 + x1 * x3
469 + x2 * x3
470 + x3 * x3
471 + x1 * x4
472 + x2 * x4
473 + x3 * x4
474 + x4 * x4;
475 let diag_y = y1 * y1
476 + y1 * y2
477 + y2 * y2
478 + y1 * y3
479 + y2 * y3
480 + y3 * y3
481 + y1 * y4
482 + y2 * y4
483 + y3 * y4
484 + y4 * y4;
485 let diag_z = z1 * z1
486 + z1 * z2
487 + z2 * z2
488 + z1 * z3
489 + z2 * z3
490 + z3 * z3
491 + z1 * z4
492 + z2 * z4
493 + z3 * z4
494 + z4 * z4;
495
496 let a0 = (diag_y + diag_z) * 0.1;
497 let b0 = (diag_z + diag_x) * 0.1;
498 let c0 = (diag_x + diag_y) * 0.1;
499
500 let a1 = (y1 * z1 * 2.0
501 + y2 * z1
502 + y3 * z1
503 + y4 * z1
504 + y1 * z2
505 + y2 * z2 * 2.0
506 + y3 * z2
507 + y4 * z2
508 + y1 * z3
509 + y2 * z3
510 + y3 * z3 * 2.0
511 + y4 * z3
512 + y1 * z4
513 + y2 * z4
514 + y3 * z4
515 + y4 * z4 * 2.0)
516 * 0.05;
517 let b1 = (x1 * z1 * 2.0
518 + x2 * z1
519 + x3 * z1
520 + x4 * z1
521 + x1 * z2
522 + x2 * z2 * 2.0
523 + x3 * z2
524 + x4 * z2
525 + x1 * z3
526 + x2 * z3
527 + x3 * z3 * 2.0
528 + x4 * z3
529 + x1 * z4
530 + x2 * z4
531 + x3 * z4
532 + x4 * z4 * 2.0)
533 * 0.05;
534 let c1 = (x1 * y1 * 2.0
535 + x2 * y1
536 + x3 * y1
537 + x4 * y1
538 + x1 * y2
539 + x2 * y2 * 2.0
540 + x3 * y2
541 + x4 * y2
542 + x1 * y3
543 + x2 * y3
544 + x3 * y3 * 2.0
545 + x4 * y3
546 + x1 * y4
547 + x2 * y4
548 + x3 * y4
549 + x4 * y4 * 2.0)
550 * 0.05;
551
552 AngularInertiaTensor::from_symmetric_mat3(SymmetricMat3::new(a0, -b1, -c1, b0, -a1, c0))
554 }
555
556 #[inline]
557 fn center_of_mass(&self) -> Vec3 {
558 (self.vertices[0] + self.vertices[1] + self.vertices[2] + self.vertices[3]) / 4.0
559 }
560
561 #[inline]
562 fn mass_properties(&self, density: f32) -> MassProperties3d {
563 let volume = self.volume();
564 let center_of_mass = self.center_of_mass();
565
566 if volume < f32::EPSILON {
567 return MassProperties3d::new(0.0, Vec3::ZERO, center_of_mass);
568 }
569
570 let mass = volume * density;
571 let tensor = self.angular_inertia_tensor(mass);
572
573 MassProperties3d::new_with_angular_inertia_tensor(mass, tensor, center_of_mass)
574 }
575}
576
577macro_rules! impl_zero_mass_properties_3d {
578 ($($shape:ty),*) => {
579 $(
580 impl ComputeMassProperties3d for $shape {
581 #[inline]
582 fn mass(&self, _density: f32) -> f32 {
583 0.0
584 }
585
586 #[inline]
587 fn unit_principal_angular_inertia(&self) -> Vec3 {
588 Vec3::ZERO
589 }
590
591 #[inline]
592 fn principal_angular_inertia(&self, _mass: f32) -> Vec3 {
593 Vec3::ZERO
594 }
595
596 #[inline]
597 fn local_inertial_frame(&self) -> Quat {
598 Quat::IDENTITY
599 }
600
601 #[inline]
602 fn unit_angular_inertia_tensor(&self) -> AngularInertiaTensor {
603 AngularInertiaTensor::ZERO
604 }
605
606 #[inline]
607 fn angular_inertia_tensor(&self, _mass: f32) -> AngularInertiaTensor {
608 AngularInertiaTensor::ZERO
609 }
610
611 #[inline]
612 fn center_of_mass(&self) -> Vec3 {
613 Vec3::ZERO
614 }
615
616 #[inline]
617 fn mass_properties(&self, _density: f32) -> MassProperties3d {
618 MassProperties3d::ZERO
619 }
620 }
621 )*
622 };
623}
624
625impl_zero_mass_properties_3d!(Plane3d);
626impl_zero_mass_properties_3d!(Line3d);
627impl_zero_mass_properties_3d!(Segment3d);
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 bounding::{Bounded3d, BoundingVolume},
678 Isometry3d, ShapeSample, Vec3Swizzles,
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}