1use crate::{Capsule, ClosestPoint, Distance, LineSegment, Plane, Ray, Sphere, Triangle};
2use mini_math::Vector3;
3
4pub trait Intersection<Rhs> {
6 fn intersects(&self, rhs: &Rhs) -> bool;
8}
9
10impl Intersection<Ray> for Sphere {
11 fn intersects(&self, ray: &Ray) -> bool {
12 let p = ray.closest_point(&self.center);
13 self.distance(&p) < 0.0
14 }
15}
16
17impl Intersection<Sphere> for Ray {
18 fn intersects(&self, sphere: &Sphere) -> bool {
19 sphere.intersects(self)
20 }
21}
22
23impl Intersection<Capsule> for Ray {
24 fn intersects(&self, rhs: &Capsule) -> bool {
25 self.distance(rhs) < 0.0
26 }
27}
28
29impl Intersection<Ray> for Capsule {
30 fn intersects(&self, rhs: &Ray) -> bool {
31 rhs.intersects(self)
32 }
33}
34
35impl Intersection<Ray> for Plane {
36 fn intersects(&self, ray: &Ray) -> bool {
37 let t =
38 -(self.d + Vector3::from(ray.origin).dot(self.normal)) / ray.direction.dot(self.normal);
39 t >= 0.0
40 }
41}
42
43impl Intersection<Plane> for Ray {
44 fn intersects(&self, plane: &Plane) -> bool {
45 plane.intersects(self)
46 }
47}
48
49impl Intersection<LineSegment> for Sphere {
50 fn intersects(&self, line: &LineSegment) -> bool {
51 let p = line.closest_point(&self.center);
52 self.distance(&p) < 0.0
53 }
54}
55
56impl Intersection<Sphere> for LineSegment {
57 fn intersects(&self, sphere: &Sphere) -> bool {
58 sphere.intersects(self)
59 }
60}
61
62impl Intersection<Sphere> for Plane {
63 fn intersects(&self, sphere: &Sphere) -> bool {
64 self.distance(&sphere.center).abs() <= sphere.radius
65 }
66}
67
68impl Intersection<Plane> for Sphere {
69 fn intersects(&self, plane: &Plane) -> bool {
70 plane.intersects(self)
71 }
72}
73
74impl Intersection<Sphere> for Sphere {
75 fn intersects(&self, sphere: &Sphere) -> bool {
76 let combined_radius = self.radius + sphere.radius;
77 (self.center - sphere.center).magnitude_squared() <= combined_radius * combined_radius
78 }
79}
80
81impl Intersection<Sphere> for Triangle {
82 fn intersects(&self, sphere: &Sphere) -> bool {
83 let plane = Plane::from(self);
84
85 let p = plane.closest_point(&sphere.center);
86 let distance_from_plane_squared = (p - sphere.center).magnitude_squared();
87
88 if distance_from_plane_squared > sphere.radius * sphere.radius {
89 return false;
90 }
91
92 let radius_on_plane = (sphere.radius * sphere.radius - distance_from_plane_squared).sqrt();
93 let coordinates = self.barycentric_coordinates(p);
94
95 coordinates.x > -radius_on_plane
96 && coordinates.y > -radius_on_plane
97 && coordinates.z > -radius_on_plane
98 }
99}
100
101impl Intersection<Triangle> for Sphere {
102 fn intersects(&self, triangle: &Triangle) -> bool {
103 triangle.intersects(self)
104 }
105}
106
107impl Intersection<Ray> for Triangle {
108 fn intersects(&self, ray: &Ray) -> bool {
109 let plane = Plane::from(self);
110
111 let n_dot_r = plane.normal.dot(ray.direction);
112 if n_dot_r.abs() < std::f32::EPSILON {
114 return false;
115 }
116
117 let d = plane.normal.dot(ray.origin - self.a);
118 let t = -d / n_dot_r;
119
120 if t < 0.0 {
122 return false;
123 }
124
125 let intersection_point = ray.origin + ray.direction * t;
126 self.coplanar_point_inside(intersection_point)
127 }
128}
129
130impl Intersection<Triangle> for Ray {
131 fn intersects(&self, triangle: &Triangle) -> bool {
132 triangle.intersects(self)
133 }
134}
135
136impl Intersection<LineSegment> for Triangle {
137 fn intersects(&self, line: &LineSegment) -> bool {
138 let plane = Plane::from(self);
139
140 let mut direction = line.end - line.start;
141 let length = direction.magnitude();
142 direction /= length;
143
144 let n_dot_r = plane.normal.dot(direction);
145 if n_dot_r.abs() < std::f32::EPSILON {
147 return false;
148 }
149
150 let d = plane.normal.dot(line.start - self.a);
151 let t = -d / n_dot_r;
152
153 if t < 0.0 || t > length {
155 return false;
156 }
157
158 let intersection_point = line.start + direction * t;
159 self.coplanar_point_inside(intersection_point)
160 }
161}
162
163impl Intersection<Triangle> for LineSegment {
164 fn intersects(&self, triangle: &Triangle) -> bool {
165 triangle.intersects(self)
166 }
167}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
172 use mini_math::{Point, Vector3};
173
174 #[test]
175 fn test_ray_sphere_intersects() {
176 let sphere = Sphere::new(Point::new(0.0, 20.0, 0.0), 10.0);
177
178 let ray = Ray::new(Point::new(-20.0, 0.0, 0.0), Vector3::new(1.0, 0.0, 0.0));
179 assert!(!sphere.intersects(&ray));
180 assert!(!ray.intersects(&sphere));
181
182 let ray = Ray::new(Point::new(-20.0, 20.0, 0.0), Vector3::new(1.0, 0.0, 0.0));
183 assert!(sphere.intersects(&ray));
184 assert!(ray.intersects(&sphere));
185 }
186
187 #[test]
188 fn test_segment_sphere_intersects() {
189 let sphere = Sphere::new(Point::new(0.0, 20.0, 0.0), 10.0);
190
191 let segment = LineSegment::new(Point::new(-20.0, 0.0, 0.0), Point::new(-10.0, 0.0, 0.0));
192 assert!(!sphere.intersects(&segment));
193 assert!(!segment.intersects(&sphere));
194
195 let segment = LineSegment::new(Point::new(10.0, 0.0, 0.0), Point::new(20.0, 0.0, 0.0));
196 assert!(!sphere.intersects(&segment));
197 assert!(!segment.intersects(&sphere));
198
199 let segment = LineSegment::new(Point::new(-20.0, 20.0, 0.0), Point::new(20.0, 0.0, 0.0));
200 assert!(sphere.intersects(&segment));
201 assert!(segment.intersects(&sphere));
202 }
203
204 #[test]
205 fn test_ray_plane_intersects() {
206 let plane = Plane::from_points(
207 Point::new(-1.0, 0.0, -1.0),
208 Point::new(1.0, 0.0, -1.0),
209 Point::new(0.0, 0.0, 1.0),
210 );
211
212 let ray = Ray::new(Point::new(0.0, 1.0, 0.0), Vector3::new(0.0, 1.0, 0.0));
213 assert!(!plane.intersects(&ray));
214 assert!(!ray.intersects(&plane));
215
216 let ray = Ray::new(Point::new(0.0, -1.0, 0.0), Vector3::new(0.0, 1.0, 0.0));
217 assert!(plane.intersects(&ray));
218 assert!(ray.intersects(&plane));
219 }
220
221 #[test]
222 fn test_sphere_plane_intersects() {
223 let plane = Plane::from_points(
224 Point::new(-1.0, 0.0, -1.0),
225 Point::new(1.0, 0.0, -1.0),
226 Point::new(0.0, 0.0, 1.0),
227 );
228
229 let sphere = Sphere::new(Point::new(0.0, 10.0, 0.0), 5.0);
230 assert!(!plane.intersects(&sphere));
231 assert!(!sphere.intersects(&plane));
232
233 let sphere = Sphere::new(Point::new(0.0, -10.0, 0.0), 5.0);
234 assert!(!plane.intersects(&sphere));
235 assert!(!sphere.intersects(&plane));
236
237 let sphere = Sphere::new(Point::new(0.0, 2.0, 0.0), 5.0);
238 assert!(plane.intersects(&sphere));
239 assert!(sphere.intersects(&plane));
240 }
241
242 #[test]
243 fn test_sphere_sphere_intersects() {
244 let sphere1 = Sphere::new(Point::new(10.0, 0.0, 0.0), 5.0);
245
246 let sphere2 = Sphere::new(Point::new(-10.0, 00.0, 0.0), 5.0);
247 assert!(!sphere1.intersects(&sphere2));
248 assert!(!sphere2.intersects(&sphere1));
249
250 let sphere2 = Sphere::new(Point::new(0.0, 0.0, 0.0), 7.0);
251 assert!(sphere1.intersects(&sphere2));
252 assert!(sphere2.intersects(&sphere1));
253 }
254
255 #[test]
256 fn test_triangle_sphere_intersects() {
257 let triangle = Triangle::new(
258 Point::new(-1.0, 0.0, 0.0),
259 Point::new(1.0, 0.0, 0.0),
260 Point::new(0.0, 0.0, 1.0),
261 );
262
263 let sphere = Sphere::new(Point::new(0.0, 1.0, 0.0), 0.5);
265 assert!(!triangle.intersects(&sphere));
266 assert!(!sphere.intersects(&triangle));
267
268 let sphere = Sphere::new(Point::new(0.0, -1.0, 0.0), 0.5);
270 assert!(!triangle.intersects(&sphere));
271 assert!(!sphere.intersects(&triangle));
272
273 let sphere = Sphere::new(Point::new(0.0, 0.0, -3.0), 0.5);
275 assert!(!triangle.intersects(&sphere));
276 assert!(!sphere.intersects(&triangle));
277
278 let sphere = Sphere::new(Point::new(3.0, 0.0, 3.0), 0.5);
279 assert!(!triangle.intersects(&sphere));
280 assert!(!sphere.intersects(&triangle));
281
282 let sphere = Sphere::new(Point::new(-3.0, 0.0, 3.0), 0.5);
283 assert!(!triangle.intersects(&sphere));
284 assert!(!sphere.intersects(&triangle));
285
286 let sphere = Sphere::new(Point::new(0.0, 0.3, -0.3), 0.5);
288 assert!(triangle.intersects(&sphere));
289 assert!(sphere.intersects(&triangle));
290
291 let sphere = Sphere::new(Point::new(0.0, 0.0, 0.0), 0.5);
293 assert!(triangle.intersects(&sphere));
294 assert!(sphere.intersects(&triangle));
295 }
296
297 #[test]
298 fn test_triangle_ray_intersects() {
299 let triangle = Triangle::new(
300 Point::new(-1.0, 0.0, 0.0),
301 Point::new(1.0, 0.0, 0.0),
302 Point::new(0.0, 0.0, 1.0),
303 );
304
305 let ray = Ray::new(Point::new(0.0, 1.0, 0.0), Vector3::new(0.0, 0.0, 1.0));
307 assert!(!triangle.intersects(&ray));
308
309 let ray = Ray::new(Point::new(0.0, 1.0, 0.0), Vector3::new(0.0, 1.0, 0.0));
311 assert!(!triangle.intersects(&ray));
312
313 let ray = Ray::new(Point::new(0.0, -1.0, 0.0), Vector3::new(0.0, -1.0, 0.0));
315 assert!(!triangle.intersects(&ray));
316
317 let ray = Ray::new(Point::new(3.0, 1.0, 3.0), Vector3::new(0.0, -1.0, 0.0));
319 assert!(!triangle.intersects(&ray));
320
321 let ray = Ray::new(Point::new(0.0, 1.0, 0.0), Vector3::new(0.0, -1.0, 0.0));
323 assert!(triangle.intersects(&ray));
324
325 let ray = Ray::new(
327 Point::new(-0.5, -1.0, 0.0),
328 Vector3::new(0.5, 1.0, 0.0).normalized(),
329 );
330 assert!(triangle.intersects(&ray));
331 }
332
333 #[test]
334 fn test_triangle_ray_intersects_2() {
335 let ray = Ray::new(
336 Point::new(0.0, 2.0, -6.0),
337 Vector3::new(0.0, 0.0, 1.0).normalized(),
338 );
339 let triangle = Triangle::new(
340 Point::new(2.0, 0.0, -1.25),
341 Point::new(0.0, 3.0, 2.5),
342 Point::new(-2.0, 0.0, -1.25),
343 );
344
345 assert!(triangle.intersects(&ray));
346 }
347
348 #[test]
349 fn test_triangle_line_segment_intersects() {
350 let triangle = Triangle::new(
351 Point::new(-1.0, 0.0, 0.0),
352 Point::new(1.0, 0.0, 0.0),
353 Point::new(0.0, 0.0, 1.0),
354 );
355
356 let line = LineSegment::new(Point::new(0.0, 1.0, 0.0), Point::new(0.0, 1.0, 1.0));
358 assert!(!triangle.intersects(&line));
359
360 let line = LineSegment::new(Point::new(0.0, 1.0, 0.0), Point::new(0.0, 4.0, 0.0));
362 assert!(!triangle.intersects(&line));
363
364 let line = LineSegment::new(Point::new(0.0, -8.0, 0.0), Point::new(0.0, -1.0, 0.0));
366 assert!(!triangle.intersects(&line));
367
368 let line = LineSegment::new(Point::new(3.0, 1.0, 3.0), Point::new(3.0, -1.0, 3.0));
370 assert!(!triangle.intersects(&line));
371
372 let line = LineSegment::new(Point::new(0.0, 1.0, 0.0), Point::new(0.0, -1.0, 0.0));
374 assert!(triangle.intersects(&line));
375
376 let line = LineSegment::new(Point::new(-0.5, -2.0, 0.0), Point::new(0.5, 2.0, 0.0));
378 assert!(triangle.intersects(&line));
379 }
380}