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