parry3d/query/ray/
ray.rs

1//! Traits and structure needed to cast rays.
2
3use crate::math::{Isometry, Point, Real, Vector};
4use crate::shape::FeatureId;
5
6#[cfg(feature = "alloc")]
7use crate::partitioning::BvhLeafCost;
8#[cfg(feature = "rkyv")]
9use rkyv::{bytecheck, CheckBytes};
10
11/// A Ray.
12#[derive(Debug, Clone, Copy)]
13#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14#[cfg_attr(
15    feature = "rkyv",
16    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, CheckBytes),
17    archive(as = "Self")
18)]
19#[repr(C)]
20pub struct Ray {
21    /// Starting point of the ray.
22    pub origin: Point<Real>,
23    /// Direction of the ray.
24    pub dir: Vector<Real>,
25}
26
27impl Ray {
28    /// Creates a new ray starting from `origin` and with the direction `dir`.
29    pub fn new(origin: Point<Real>, dir: Vector<Real>) -> Ray {
30        Ray { origin, dir }
31    }
32
33    /// Transforms this ray by the given isometry.
34    #[inline]
35    pub fn transform_by(&self, m: &Isometry<Real>) -> Self {
36        Self::new(m * self.origin, m * self.dir)
37    }
38
39    /// Transforms this ray by the inverse of the given isometry.
40    #[inline]
41    pub fn inverse_transform_by(&self, m: &Isometry<Real>) -> Self {
42        Self::new(
43            m.inverse_transform_point(&self.origin),
44            m.inverse_transform_vector(&self.dir),
45        )
46    }
47
48    /// Translates this ray by the given vector. Its direction is left unchanged.
49    #[inline]
50    pub fn translate_by(&self, v: Vector<Real>) -> Self {
51        Self::new(self.origin + v, self.dir)
52    }
53
54    /// Computes the point at the given parameter on this line.
55    ///
56    /// This computes `self.origin + self.dir * t`.
57    #[inline]
58    pub fn point_at(&self, t: Real) -> Point<Real> {
59        self.origin + self.dir * t
60    }
61}
62
63/// Structure containing the result of a successful ray cast.
64#[derive(Copy, Clone, Debug)]
65#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
66#[cfg_attr(
67    feature = "rkyv",
68    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, CheckBytes),
69    archive(as = "Self")
70)]
71pub struct RayIntersection {
72    /// The time of impact of the ray with the object. The exact contact point can be computed
73    /// with: `ray.point_at(time_of_impact)` or equivalently `origin + dir * time_of_impact` where `origin` is the origin of the ray;
74    /// `dir` is its direction and `time_of_impact` is the value of this field.
75    pub time_of_impact: Real,
76
77    /// The normal at the intersection point.
78    ///
79    /// If the origin of the ray is inside the shape and the shape is not solid,
80    /// the normal will point towards the interior of the shape.
81    /// Otherwise, the normal points outward.
82    ///
83    /// If the `time_of_impact` is exactly zero, the normal might not be reliable.
84    // TODO: use a Unit<Vector> instead.
85    pub normal: Vector<Real>,
86
87    /// Feature at the intersection point.
88    pub feature: FeatureId,
89}
90
91impl RayIntersection {
92    #[inline]
93    /// Creates a new `RayIntersection`.
94    #[cfg(feature = "dim3")]
95    pub fn new(time_of_impact: Real, normal: Vector<Real>, feature: FeatureId) -> RayIntersection {
96        RayIntersection {
97            time_of_impact,
98            normal,
99            feature,
100        }
101    }
102
103    #[inline]
104    /// Creates a new `RayIntersection`.
105    #[cfg(feature = "dim2")]
106    pub fn new(time_of_impact: Real, normal: Vector<Real>, feature: FeatureId) -> RayIntersection {
107        RayIntersection {
108            time_of_impact,
109            normal,
110            feature,
111        }
112    }
113
114    #[inline]
115    pub fn transform_by(&self, transform: &Isometry<Real>) -> Self {
116        RayIntersection {
117            time_of_impact: self.time_of_impact,
118            normal: transform * self.normal,
119            feature: self.feature,
120        }
121    }
122}
123
124#[cfg(feature = "alloc")]
125impl BvhLeafCost for RayIntersection {
126    #[inline]
127    fn cost(&self) -> Real {
128        self.time_of_impact
129    }
130}
131
132/// Traits of objects which can be transformed and tested for intersection with a ray.
133pub trait RayCast {
134    /// Computes the time of impact between this transform shape and a ray.
135    fn cast_local_ray(&self, ray: &Ray, max_time_of_impact: Real, solid: bool) -> Option<Real> {
136        self.cast_local_ray_and_get_normal(ray, max_time_of_impact, solid)
137            .map(|inter| inter.time_of_impact)
138    }
139
140    /// Computes the time of impact, and normal between this transformed shape and a ray.
141    fn cast_local_ray_and_get_normal(
142        &self,
143        ray: &Ray,
144        max_time_of_impact: Real,
145        solid: bool,
146    ) -> Option<RayIntersection>;
147
148    /// Tests whether a ray intersects this transformed shape.
149    #[inline]
150    fn intersects_local_ray(&self, ray: &Ray, max_time_of_impact: Real) -> bool {
151        self.cast_local_ray(ray, max_time_of_impact, true).is_some()
152    }
153
154    /// Computes the time of impact between this transform shape and a ray.
155    fn cast_ray(
156        &self,
157        m: &Isometry<Real>,
158        ray: &Ray,
159        max_time_of_impact: Real,
160        solid: bool,
161    ) -> Option<Real> {
162        let ls_ray = ray.inverse_transform_by(m);
163        self.cast_local_ray(&ls_ray, max_time_of_impact, solid)
164    }
165
166    /// Computes the time of impact, and normal between this transformed shape and a ray.
167    fn cast_ray_and_get_normal(
168        &self,
169        m: &Isometry<Real>,
170        ray: &Ray,
171        max_time_of_impact: Real,
172        solid: bool,
173    ) -> Option<RayIntersection> {
174        let ls_ray = ray.inverse_transform_by(m);
175        self.cast_local_ray_and_get_normal(&ls_ray, max_time_of_impact, solid)
176            .map(|inter| inter.transform_by(m))
177    }
178
179    /// Tests whether a ray intersects this transformed shape.
180    #[inline]
181    fn intersects_ray(&self, m: &Isometry<Real>, ray: &Ray, max_time_of_impact: Real) -> bool {
182        let ls_ray = ray.inverse_transform_by(m);
183        self.intersects_local_ray(&ls_ray, max_time_of_impact)
184    }
185}