1use super::{AngularInertiaTensor, ComputeMassProperties3d, MassProperties3d};
2use bevy_math::{
3 ops,
4 prelude::Tetrahedron,
5 primitives::{
6 BoxedPolyline3d, Capsule3d, Cone, ConicalFrustum, Cuboid, Cylinder, Line3d, Measured3d,
7 Plane3d, Polyline3d, Segment3d, Sphere, Torus,
8 },
9 FloatPow, Mat3, Quat, Vec3,
10};
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_mat3(Mat3::from_cols_array(&[
552 a0, -b1, -c1, -b1, b0, -a1, -c1, -a1, c0,
553 ]))
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);
628impl_zero_mass_properties_3d!(BoxedPolyline3d);
629
630impl<const N: usize> ComputeMassProperties3d for Polyline3d<N> {
631 #[inline]
632 fn mass(&self, _density: f32) -> f32 {
633 0.0
634 }
635
636 #[inline]
637 fn unit_principal_angular_inertia(&self) -> Vec3 {
638 Vec3::ZERO
639 }
640
641 #[inline]
642 fn principal_angular_inertia(&self, _mass: f32) -> Vec3 {
643 Vec3::ZERO
644 }
645
646 #[inline]
647 fn local_inertial_frame(&self) -> bevy_math::Quat {
648 Quat::IDENTITY
649 }
650
651 #[inline]
652 fn unit_angular_inertia_tensor(&self) -> AngularInertiaTensor {
653 AngularInertiaTensor::ZERO
654 }
655
656 #[inline]
657 fn angular_inertia_tensor(&self, _mass: f32) -> AngularInertiaTensor {
658 AngularInertiaTensor::ZERO
659 }
660
661 #[inline]
662 fn center_of_mass(&self) -> Vec3 {
663 Vec3::ZERO
664 }
665
666 #[inline]
667 fn mass_properties(&self, _density: f32) -> MassProperties3d {
668 MassProperties3d::ZERO
669 }
670}
671
672#[cfg(test)]
673mod tests {
674 use alloc::vec::Vec;
675
676 use approx::assert_relative_eq;
677 use bevy_math::{
678 bounding::{Bounded3d, BoundingVolume},
679 Isometry3d, ShapeSample, Vec3Swizzles,
680 };
681 use rand::SeedableRng;
682
683 use super::*;
684
685 macro_rules! test_shape {
686 ($test_name:tt, $shape:expr, $point_generator:expr) => {
687 #[test]
688 fn $test_name() {
689 let shape = $shape;
690
691 let points: Vec<Vec3> = $point_generator(&shape);
693
694 let density = 2.0;
696 let mass = shape.mass(density);
697 let principal_angular_inertia = shape.principal_angular_inertia(mass);
698 let local_inertial_frame = shape.local_inertial_frame();
699 let angular_inertia_tensor = shape.angular_inertia_tensor(mass);
700 let center_of_mass = shape.center_of_mass();
701
702 let mass_props = shape.mass_properties(density);
704 assert_relative_eq!(mass, mass_props.mass);
705 assert_relative_eq!(
706 principal_angular_inertia,
707 mass_props.principal_angular_inertia
708 );
709 assert_relative_eq!(center_of_mass, mass_props.center_of_mass);
710
711 let expected =
715 MassProperties3d::from_point_cloud(&points, mass, local_inertial_frame);
716
717 assert_relative_eq!(mass, expected.mass);
718 assert_relative_eq!(
719 principal_angular_inertia,
720 expected.principal_angular_inertia,
721 epsilon = 0.1
722 );
723 assert_relative_eq!(center_of_mass, expected.center_of_mass, epsilon = 0.01);
724
725 assert_relative_eq!(
728 angular_inertia_tensor,
729 AngularInertiaTensor::new_with_local_frame(
730 principal_angular_inertia,
731 local_inertial_frame
732 ),
733 epsilon = 1e-5
734 );
735
736 let (principal, frame) =
743 angular_inertia_tensor.principal_angular_inertia_with_local_frame();
744 assert_relative_eq!(
745 AngularInertiaTensor::new_with_local_frame(principal, frame),
746 angular_inertia_tensor,
747 epsilon = 1e-5
748 );
749 }
750 };
751 }
752
753 fn sample_shape<S: ShapeSample<Output = Vec3>>(shape: &S) -> Vec<S::Output> {
754 let mut rng = rand_chacha::ChaCha8Rng::from_seed(Default::default());
755 (0..2_000_000)
756 .map(|_| shape.sample_interior(&mut rng))
757 .collect::<Vec<_>>()
758 }
759
760 fn rejection_sample_shape<S: Bounded3d>(
761 func: impl Fn(&S, Vec3) -> bool,
762 shape: &S,
763 ) -> Vec<Vec3> {
764 let mut rng = rand_chacha::ChaCha8Rng::from_seed(Default::default());
765 let mut points = Vec::new();
766 let aabb = shape.aabb_3d(Isometry3d::IDENTITY);
767 let aabb_center: Vec3 = aabb.center().into();
768 let cuboid = Cuboid {
769 half_size: aabb.half_size().into(),
770 };
771 while points.len() < 2_000_000 {
772 let point = aabb_center + cuboid.sample_interior(&mut rng);
773 if func(shape, point) {
774 points.push(point);
775 }
776 }
777 points
778 }
779
780 test_shape!(sphere, Sphere::new(2.0), sample_shape);
781 test_shape!(cuboid, Cuboid::new(1.0, 2.0, 3.0), sample_shape);
782 test_shape!(cylinder, Cylinder::new(1.0, 4.0), sample_shape);
783 test_shape!(capsule, Capsule3d::new(1.0, 2.0), sample_shape);
784 test_shape!(
785 cone,
786 Cone {
787 radius: 1.0,
788 height: 2.0,
789 },
790 |shape| rejection_sample_shape(cone_contains_point, shape)
791 );
792 test_shape!(
793 conical_frustum,
794 ConicalFrustum {
795 radius_top: 0.5,
796 radius_bottom: 1.0,
797 height: 1.0
798 },
799 |shape| rejection_sample_shape(conical_frustum_contains_point, shape)
800 );
801 test_shape!(torus, Torus::new(1.0, 2.0), |shape| rejection_sample_shape(
802 torus_contains_point,
803 shape,
804 ));
805 test_shape!(
806 tetrahedron,
807 Tetrahedron::new(
808 Vec3::new(0.0, 0.0, 0.0),
809 Vec3::new(1.0, 0.0, 0.0),
810 Vec3::new(0.0, 1.0, 0.0),
811 Vec3::new(0.0, 0.0, 1.0),
812 ),
813 sample_shape
814 );
815
816 fn cone_contains_point(cone: &Cone, point: Vec3) -> bool {
818 let half_height = cone.height * 0.5;
819
820 if point.y < -half_height || point.y > half_height {
821 return false;
822 }
823
824 let pb_dot_ba = -cone.height * (point.y + half_height);
827
828 let delta_radius = pb_dot_ba * cone.radius / cone.height.squared();
842 let radius = cone.radius + delta_radius;
843
844 let ortho_distance_squared = point.xz().length_squared();
846
847 ortho_distance_squared < radius * radius
848 }
849
850 fn conical_frustum_contains_point(frustum: &ConicalFrustum, point: Vec3) -> bool {
852 let half_height = frustum.height * 0.5;
853
854 if point.y < -half_height || point.y > half_height {
855 return false;
856 }
857
858 let pb_dot_ba = -frustum.height * (point.y + half_height);
861
862 let delta_radius =
876 pb_dot_ba * (frustum.radius_bottom - frustum.radius_top) / frustum.height.squared();
877 let radius = frustum.radius_bottom + delta_radius;
878
879 let ortho_distance_squared = point.xz().length_squared();
881
882 ortho_distance_squared < radius * radius
883 }
884
885 fn torus_contains_point(torus: &Torus, point: Vec3) -> bool {
887 let minor_radius_squared = torus.minor_radius * torus.minor_radius;
888 (torus.major_radius - point.xz().length()).squared() + point.y.squared()
889 < minor_radius_squared
890 }
891}