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