parry3d/query/ray/
ray_ball.rs1use na::{self, ComplexField};
2
3use crate::math::{Point, Real};
4use crate::query::{Ray, RayCast, RayIntersection};
5use crate::shape::{Ball, FeatureId};
6use num::Zero;
7
8impl RayCast for Ball {
9 #[inline]
10 fn cast_local_ray(&self, ray: &Ray, max_time_of_impact: Real, solid: bool) -> Option<Real> {
11 ray_toi_with_ball(&Point::origin(), self.radius, ray, solid)
12 .1
13 .filter(|time_of_impact| *time_of_impact <= max_time_of_impact)
14 }
15
16 #[inline]
17 fn cast_local_ray_and_get_normal(
18 &self,
19 ray: &Ray,
20 max_time_of_impact: Real,
21 solid: bool,
22 ) -> Option<RayIntersection> {
23 ray_toi_and_normal_with_ball(&Point::origin(), self.radius, ray, solid)
24 .1
25 .filter(|int| int.time_of_impact <= max_time_of_impact)
26 }
27}
28
29#[inline]
33pub fn ray_toi_with_ball(
34 center: &Point<Real>,
35 radius: Real,
36 ray: &Ray,
37 solid: bool,
38) -> (bool, Option<Real>) {
39 let dcenter = ray.origin - *center;
40
41 let a = ray.dir.norm_squared();
42 let b = dcenter.dot(&ray.dir);
43 let c = dcenter.norm_squared() - radius * radius;
44
45 if a.is_zero() {
47 if c > 0.0 {
48 return (false, None);
49 } else {
50 return (true, Some(0.0));
51 }
52 }
53
54 if c > 0.0 && b > 0.0 {
55 (false, None)
56 } else {
57 let delta = b * b - a * c;
58
59 if delta < 0.0 {
60 (false, None)
62 } else {
63 let t = (-b - ComplexField::sqrt(delta)) / a;
64
65 if t <= 0.0 {
66 if solid {
68 (true, Some(0.0))
69 } else {
70 (true, Some((-b + delta.sqrt()) / a))
71 }
72 } else {
73 (false, Some(t))
74 }
75 }
76 }
77}
78
79#[inline]
81pub fn ray_toi_and_normal_with_ball(
82 center: &Point<Real>,
83 radius: Real,
84 ray: &Ray,
85 solid: bool,
86) -> (bool, Option<RayIntersection>) {
87 let (inside, inter) = ray_toi_with_ball(center, radius, ray, solid);
88
89 (
90 inside,
91 inter.map(|n| {
92 let pos = ray.origin + ray.dir * n - center;
93 let normal = pos.normalize();
94
95 RayIntersection::new(n, if inside { -normal } else { normal }, FeatureId::Face(0))
96 }),
97 )
98}