geop_core_math/primitives/
ray.rs1use serde::{Deserialize, Serialize};
5
6use super::CoordinateSystem;
7use crate::{
8 geop_error::{GeopError, GeopResult},
9 scalars::Scalar,
10 vector::{Vector2, Vector3},
11};
12
13#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
16#[serde(try_from = "RayData<S>", bound = "S: Scalar")]
17pub struct Ray<S: Scalar> {
18 origin: Vector3<S>,
19 dir: Vector3<S>,
20}
21
22#[derive(Deserialize)]
24#[serde(bound(deserialize = "S: Scalar"))]
25struct RayData<S: Scalar> {
26 origin: Vector3<S>,
27 dir: Vector3<S>,
28}
29
30impl<S: Scalar> TryFrom<RayData<S>> for Ray<S> {
31 type Error = GeopError;
32
33 fn try_from(data: RayData<S>) -> GeopResult<Self> {
34 Ray::try_new(data.origin, data.dir)
35 }
36}
37
38fn ahead<S: Scalar>(t: S) -> S {
41 if t.definitely_less(S::ZERO) {
42 S::ZERO
43 } else {
44 t
45 }
46}
47
48impl<S: Scalar> Ray<S> {
49 pub fn try_new(origin: Vector3<S>, dir: Vector3<S>) -> GeopResult<Self> {
52 let dir = dir
53 .normalize()
54 .map_err(|e| e.with_context(format!("Ray::try_new(origin={origin:?}, dir={dir:?})")))?;
55 Ok(Self { origin, dir })
56 }
57
58 pub fn origin(&self) -> &Vector3<S> {
59 &self.origin
60 }
61
62 pub fn dir(&self) -> &Vector3<S> {
64 &self.dir
65 }
66
67 pub fn at(&self, t: S) -> Vector3<S> {
69 self.origin.add(&self.dir.prod_scalar(t))
70 }
71
72 pub fn closest_to_point(&self, p: &Vector3<S>) -> S {
75 ahead(p.sub(&self.origin).prod_dot(&self.dir))
76 }
77
78 pub fn distance_to_point(&self, p: &Vector3<S>) -> (S, S) {
80 let t = self.closest_to_point(p);
81 (self.at(t).sub(p).norm(), t)
82 }
83
84 pub fn distance_to_segment(&self, a: &Vector3<S>, b: &Vector3<S>) -> (S, S) {
87 let d = b.sub(a);
88 let r = self.origin.sub(a);
89 let e = d.norm_sq();
90 if e.could_be_equal(S::ZERO) {
91 return self.distance_to_point(a);
92 }
93 let b_ = self.dir.prod_dot(&d);
94 let c = self.dir.prod_dot(&r);
95 let f = d.prod_dot(&r);
96 let denom = e.sub(b_.mul(b_));
99 let t = if denom.could_be_equal(S::ZERO) {
100 S::ZERO
101 } else {
102 ahead(b_.mul(f).sub(c.mul(e)).div(denom).unwrap_or(S::ZERO))
103 };
104 let u = b_.mul(t).add(f).div(e).unwrap_or(S::ZERO);
105 let (u, t) = if u.definitely_less(S::ZERO) {
106 (S::ZERO, self.closest_to_point(a))
107 } else if u.definitely_greater(S::ONE) {
108 (S::ONE, self.closest_to_point(b))
109 } else {
110 (u, t)
111 };
112 let on_segment = a.add(&d.prod_scalar(u));
113 (self.at(t).sub(&on_segment).norm(), t)
114 }
115
116 pub fn intersect_triangle(&self, a: &Vector3<S>, b: &Vector3<S>, c: &Vector3<S>) -> Option<S> {
119 let e1 = b.sub(a);
120 let e2 = c.sub(a);
121 let h = self.dir.prod_cross(&e2);
122 let det = e1.prod_dot(&h);
123 if det.could_be_equal(S::ZERO) {
124 return None;
125 }
126 let s = self.origin.sub(a);
127 let q = s.prod_cross(&e1);
128 let u = s.prod_dot(&h).div(det).ok()?;
129 let v = self.dir.prod_dot(&q).div(det).ok()?;
130 let t = e2.prod_dot(&q).div(det).ok()?;
131 let outside = u.definitely_less(S::ZERO)
132 || v.definitely_less(S::ZERO)
133 || u.add(v).definitely_greater(S::ONE)
134 || t.definitely_less(S::ZERO);
135 (!outside).then_some(t)
136 }
137
138 pub fn intersect_plane(
142 &self,
143 origin: &Vector3<S>,
144 normal: &Vector3<S>,
145 ) -> Option<(S, Vector3<S>)> {
146 let denom = self.dir.prod_dot(normal);
147 if denom.could_be_equal(S::ZERO) {
148 return None;
149 }
150 let t = origin.sub(&self.origin).prod_dot(normal).div(denom).ok()?;
151 (!t.definitely_less(S::ZERO)).then(|| (t, self.at(t)))
152 }
153
154 pub fn intersect_uv_plane(&self, frame: &CoordinateSystem<S>) -> Option<(S, Vector2<S>)> {
158 let (t, p) = self.intersect_plane(frame.origin(), frame.w())?;
159 Some((t, frame.to_uvw(&p).head()))
160 }
161
162 pub fn line_parameter(&self, at: &Vector3<S>, direction: &Vector3<S>) -> Option<S> {
166 let w0 = at.sub(&self.origin);
167 let b = direction.prod_dot(&self.dir);
168 let denom = direction.norm_sq().sub(b.mul(b));
169 if denom.could_be_equal(S::ZERO) {
170 return None;
171 }
172 b.mul(self.dir.prod_dot(&w0))
173 .sub(direction.prod_dot(&w0))
174 .div(denom)
175 .ok()
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182 use crate::for_all_scalars;
183
184 fn v<S: Scalar>(x: f64, y: f64, z: f64) -> Vector3<S> {
185 Vector3::from_array([x, y, z].map(S::from_f64))
186 }
187
188 fn down<S: Scalar>(x: f64, y: f64) -> Ray<S> {
190 Ray::try_new(v(x, y, 10.0), v(0.0, 0.0, -2.0)).unwrap()
191 }
192
193 fn close<S: Scalar>(a: S, b: f64) -> bool {
195 a.could_be_equal(S::from_f64(b))
196 }
197
198 fn check_points_and_segments<S: Scalar>() {
199 let (dist, t) = down::<S>(0.0, 0.3).distance_to_point(&v(0.0, 0.0, 1.0));
200 assert!(close(dist, 0.3) && close(t, 9.0));
201 let (dist, _) =
203 down::<S>(2.0, 0.0).distance_to_segment(&v(0.0, 0.0, 0.0), &v(1.0, 0.0, 0.0));
204 assert!(close(dist, 1.0));
205 let (dist, t) =
206 down::<S>(0.5, 0.2).distance_to_segment(&v(0.0, 0.0, 0.0), &v(1.0, 0.0, 0.0));
207 assert!(close(dist, 0.2) && close(t, 10.0));
208 }
209
210 #[test]
211 fn points_and_segments() {
212 for_all_scalars!(check_points_and_segments);
213 }
214
215 fn check_triangles_and_planes<S: Scalar>() {
216 let (a, b, c) = (v::<S>(0.0, 0.0, 1.0), v(1.0, 0.0, 1.0), v(0.0, 1.0, 1.0));
217 assert!(close(
218 down::<S>(0.2, 0.2).intersect_triangle(&a, &b, &c).unwrap(),
219 9.0
220 ));
221 assert!(down::<S>(0.8, 0.8).intersect_triangle(&a, &b, &c).is_none());
222 let (t, p) = down::<S>(1.0, 2.0)
223 .intersect_plane(&v(0.0, 0.0, -1.0), &v(0.0, 0.0, 1.0))
224 .unwrap();
225 assert!(close(t, 11.0) && close(p[1], 2.0));
226 assert!(
228 down::<S>(0.0, 0.0)
229 .intersect_plane(&v(0.0, 0.0, 20.0), &v(0.0, 0.0, 1.0))
230 .is_none()
231 );
232 assert!(
233 down::<S>(0.0, 0.0)
234 .intersect_plane(&v(0.0, 0.0, 0.0), &v(1.0, 0.0, 0.0))
235 .is_none()
236 );
237 }
238
239 #[test]
240 fn triangles_and_planes() {
241 for_all_scalars!(check_triangles_and_planes);
242 }
243
244 fn check_line_parameters<S: Scalar>() {
248 let side = Ray::try_new(v::<S>(5.0, 0.0, 0.7), v(-1.0, 0.0, 0.0)).unwrap();
249 let s = side
250 .line_parameter(&v(0.0, 0.0, 0.0), &v(0.0, 0.0, 2.0))
251 .unwrap();
252 assert!(close(s, 0.35));
253 assert!(
254 down::<S>(0.0, 0.0)
255 .line_parameter(&v(0.0, 0.0, 0.0), &v(0.0, 0.0, 1.0))
256 .is_none()
257 );
258 }
259
260 #[test]
261 fn line_parameters() {
262 for_all_scalars!(check_line_parameters);
263 }
264}