1use mini_math::{Point, Vector3};
2
3use crate::{Capsule, Distance, Line, LineSegment, Plane, Ray, Sphere, Triangle};
4
5pub trait ClosestPoint<Other> {
7 fn closest_point(&self, other: &Other) -> Point;
9}
10
11impl ClosestPoint<Point> for Sphere {
12 fn closest_point(&self, other: &Point) -> Point {
13 self.center + (*other - self.center).normalized() * self.radius
14 }
15}
16
17impl ClosestPoint<Sphere> for Sphere {
18 fn closest_point(&self, other: &Sphere) -> Point {
19 self.closest_point(&other.center)
20 }
21}
22
23impl ClosestPoint<Point> for Line {
24 fn closest_point(&self, other: &Point) -> Point {
25 let dot = self.direction.dot(*other - self.point);
26 self.point + self.direction * dot
27 }
28}
29
30impl ClosestPoint<Line> for Line {
31 fn closest_point(&self, other: &Line) -> Point {
32 let w = self.point - other.point;
33 let b = self.direction.dot(other.direction);
34 let d = self.direction.dot(w);
35 let e = other.direction.dot(w);
36 let d_p = 1.0 - b * b;
37
38 if d_p < std::f32::EPSILON {
39 return self.point;
40 }
41
42 let sc = (b * e - d) / d_p;
43
44 self.point + self.direction * sc
45 }
46}
47
48impl ClosestPoint<Point> for Ray {
49 fn closest_point(&self, other: &Point) -> Point {
50 let dot = (*other - self.origin).dot(self.direction);
51
52 if dot <= 0.0 {
53 self.origin
54 } else {
55 self.origin + self.direction * dot
56 }
57 }
58}
59
60impl ClosestPoint<Sphere> for Ray {
61 fn closest_point(&self, other: &Sphere) -> Point {
62 self.closest_point(&other.center)
63 }
64}
65
66impl ClosestPoint<Ray> for Sphere {
67 fn closest_point(&self, other: &Ray) -> Point {
68 let p = other.closest_point(&self.center);
69 let diff = p - self.center;
70 let l = diff.magnitude();
71 self.center + (diff / l) * self.radius.min(l)
72 }
73}
74
75impl ClosestPoint<Capsule> for Ray {
76 fn closest_point(&self, other: &Capsule) -> Point {
77 self.closest_point(&other.axis)
78 }
79}
80
81impl ClosestPoint<Ray> for Capsule {
82 fn closest_point(&self, other: &Ray) -> Point {
83 let p = other.closest_point(&self.axis);
84 let q = self.axis.closest_point(other);
85 let diff = p - q;
86 let l = diff.magnitude();
87 q + (diff / l) * self.radius.min(l)
88 }
89}
90
91impl ClosestPoint<Line> for Ray {
92 fn closest_point(&self, other: &Line) -> Point {
93 let p = Line::new(self.origin, self.direction).closest_point(other);
94 self.closest_point(&p)
95 }
96}
97
98impl ClosestPoint<Ray> for Line {
99 fn closest_point(&self, other: &Ray) -> Point {
100 self.closest_point(&other.closest_point(self))
101 }
102}
103
104impl ClosestPoint<LineSegment> for Ray {
105 fn closest_point(&self, other: &LineSegment) -> Point {
106 let p = Line::new(self.origin, self.direction)
107 .closest_point(&Line::from_points(other.start, other.end));
108 let p = other.closest_point(&p);
109 self.closest_point(&p)
110 }
111}
112
113impl ClosestPoint<Ray> for LineSegment {
114 fn closest_point(&self, other: &Ray) -> Point {
115 self.closest_point(&other.closest_point(self))
116 }
117}
118
119impl ClosestPoint<Ray> for Ray {
120 fn closest_point(&self, other: &Ray) -> Point {
121 let p = Line::new(other.origin, other.direction)
122 .closest_point(&Line::new(self.origin, self.direction));
123 let p = other.closest_point(&p);
124 self.closest_point(&p)
125 }
126}
127
128impl ClosestPoint<Point> for LineSegment {
129 fn closest_point(&self, other: &Point) -> Point {
130 let mut direction = self.end - self.start;
131 let length = direction.magnitude();
132 direction /= length;
133
134 let dot = (*other - self.start).dot(direction);
135
136 if dot < 0.0 {
137 self.start
138 } else {
139 self.start + direction * dot.min(length)
140 }
141 }
142}
143
144impl ClosestPoint<Line> for LineSegment {
145 fn closest_point(&self, other: &Line) -> Point {
146 let p = other.closest_point(&Line::from_points(self.start, self.end));
147 self.closest_point(&p)
148 }
149}
150
151impl ClosestPoint<LineSegment> for LineSegment {
152 fn closest_point(&self, other: &LineSegment) -> Point {
153 let p = Line::from_points(other.start, other.end)
154 .closest_point(&Line::from_points(self.start, self.end));
155 let p = other.closest_point(&p);
156 self.closest_point(&p)
157 }
158}
159
160impl ClosestPoint<Point> for Plane {
161 fn closest_point(&self, other: &Point) -> Point {
162 let distance = self.distance(other);
163 *other - self.normal * distance
164 }
165}
166
167impl ClosestPoint<Ray> for Plane {
168 fn closest_point(&self, other: &Ray) -> Point {
169 let n_dot_r = self.normal.dot(other.direction);
170 if n_dot_r.abs() < std::f32::EPSILON {
172 return self.closest_point(&other.origin);
173 }
174
175 let e = self.normal.dot(Vector3::from(other.origin));
176 let t = (e + self.d) / n_dot_r;
177
178 other.origin + other.direction * -t
179 }
180}
181
182impl ClosestPoint<Point> for Triangle {
183 fn closest_point(&self, other: &Point) -> Point {
184 let plane = Plane::from(self);
185 let q = plane.closest_point(other);
186
187 let coordinates = self.barycentric_coordinates(q);
188 if coordinates.x >= 0.0 && coordinates.y >= 0.0 && coordinates.z >= 0.0 {
189 return q;
190 }
191
192 let p0 = LineSegment::new(self.a, self.b).closest_point(other);
193 let p1 = LineSegment::new(self.b, self.c).closest_point(other);
194 let p2 = LineSegment::new(self.c, self.a).closest_point(other);
195
196 let d0 = (p0 - *other).magnitude_squared();
197 let d1 = (p1 - *other).magnitude_squared();
198 let d2 = (p2 - *other).magnitude_squared();
199
200 if d0 < d1 && d0 < d2 {
201 p0
202 } else if d1 < d0 && d1 < d2 {
203 p1
204 } else {
205 p2
206 }
207 }
208}
209
210impl ClosestPoint<Ray> for Triangle {
211 fn closest_point(&self, other: &Ray) -> Point {
212 let plane = Plane::from(self);
213
214 let n_dot_r = plane.normal.dot(other.direction);
215 if n_dot_r.abs() < std::f32::EPSILON {
217 return self.closest_point(&other.origin);
218 }
219
220 let e = plane.normal.dot(Vector3::from(other.origin));
221 let t = (e + plane.d) / n_dot_r;
222
223 let intersection_point = other.origin + other.direction * -t;
224 self.closest_point(&intersection_point)
225 }
226}
227
228#[cfg(test)]
229mod tests {
230 use mini_math::Vector3;
231
232 use super::*;
233
234 #[test]
235 fn test_line_line() {
236 let line = Line::from_points(Point::new(0.0, 0.0, 0.0), Point::new(0.0, 0.0, 10.0));
237
238 let l = Line::from_points(Point::new(0.0, 0.0, 1.0), Point::new(0.0, 10.0, 10.0));
239 assert_eq!(line.closest_point(&l), Point::new(0.0, 0.0, 1.0));
240
241 let l = Line::from_points(Point::new(0.0, 5.0, 5.0), Point::new(0.0, 5.0, 15.0));
242 assert_eq!(line.closest_point(&l), Point::new(0.0, 0.0, 0.0));
243
244 let l = Line::from_points(Point::new(0.0, 5.0, 0.0), Point::new(25.0, 5.0, 0.0));
245 assert_eq!(line.closest_point(&l), Point::new(0.0, 0.0, 0.0));
246
247 let l = Line::from_points(Point::new(0.0, 5.0, 10.0), Point::new(25.0, 5.0, 10.0));
248 assert_eq!(line.closest_point(&l), Point::new(0.0, 0.0, 10.0));
249 }
250
251 #[test]
252 fn test_ray_point() {
253 let ray = Ray::new(Point::zero(), Vector3::new(0.0, 0.0, 1.0));
254
255 let p = Point::new(0.0, 0.0, -5.0);
256 assert_eq!(ray.closest_point(&p), Point::zero());
257
258 let p = Point::new(0.0, 5.0, 25.0);
259 assert_eq!(ray.closest_point(&p), Point::new(0.0, 0.0, 25.0));
260 }
261
262 #[test]
263 fn test_ray_line() {
264 let ray = Ray::new(Point::zero(), Vector3::new(0.0, 0.0, 1.0));
265
266 let l = Line::new(Point::new(0.0, 5.0, 0.0), Vector3::new(0.0, 0.0, 1.0));
267 assert_eq!(ray.closest_point(&l), Point::new(0.0, 0.0, 0.0));
268
269 let l = Line::new(Point::new(0.0, 0.0, -5.0), Vector3::new(0.0, 0.0, -1.0));
270 assert_eq!(ray.closest_point(&l), Point::new(0.0, 0.0, 0.0));
271
272 let l = Line::new(Point::new(0.0, 5.0, -5.0), Vector3::new(0.0, 1.0, 0.0));
273 assert_eq!(ray.closest_point(&l), Point::new(0.0, 0.0, 0.0));
274
275 let l = Line::new(Point::new(0.0, 5.0, 5.0), Vector3::new(0.0, 1.0, 0.0));
276 assert_eq!(ray.closest_point(&l), Point::new(0.0, 0.0, 5.0));
277 }
278
279 #[test]
280 fn test_plane_point() {
281 let plane = Plane::from_points(
282 Point::new(-1.0, 0.0, -1.0),
283 Point::new(1.0, 0.0, -1.0),
284 Point::new(0.0, 0.0, 1.0),
285 );
286
287 let p = Point::new(2.0, 1.0, 3.0);
288 assert_eq!(plane.closest_point(&p), Point::new(2.0, 0.0, 3.0));
289
290 let p = Point::new(-2.0, -1.0, -3.0);
291 assert_eq!(plane.closest_point(&p), Point::new(-2.0, 0.0, -3.0));
292 }
293
294 #[test]
295 fn test_triangle_point() {
296 let triangle = Triangle::new(
297 Point::new(-1.0, 0.0, -1.0),
298 Point::new(1.0, 0.0, -1.0),
299 Point::new(0.0, 0.0, 1.0),
300 );
301
302 let p = Point::new(0.0, 1.0, 0.0);
303 assert_eq!(triangle.closest_point(&p), Point::new(0.0, 0.0, 0.0));
304
305 let p = Point::new(0.0, 1.0, 2.0);
306 assert_eq!(triangle.closest_point(&p), Point::new(0.0, 0.0, 1.0));
307
308 let p = Point::new(0.0, -1.0, -2.0);
309 assert_eq!(triangle.closest_point(&p), Point::new(0.0, 0.0, -1.0));
310 }
311}