1use crate::primitives::*;
38use crate::scalar::*;
39use crate::vector::*;
40use num_traits::{One, Zero};
41
42pub trait Distance<T, Other> {
46 fn distance(&self, other: &Other) -> Option<T>;
48}
49
50pub trait Intersect<T> {
52 fn intersect(&self, other: &T) -> bool;
57}
58
59pub trait Intersection<T, Other> {
61 fn intersection(&self, other: &Other) -> Option<T>;
63}
64
65impl<T: FloatScalar> Distance<T, Vector3<T>> for Plane<T> {
69 fn distance(&self, other: &Vector3<T>) -> Option<T> {
70 let n = self.normal();
71 let nom = Vector3::dot(other, &n) + self.constant();
72 let denom = Vector3::dot(&n, &n);
73 if denom <= T::epsilon() {
74 return None;
75 }
76 Some(T::tabs(nom) / denom)
77 }
78}
79
80impl<T: FloatScalar> Distance<T, Vector3<T>> for Segment<T, Vector3<T>> {
81 fn distance(&self, other: &Vector3<T>) -> Option<T> {
82 Segment::distance(self, other, T::epsilon())
83 }
84}
85
86fn update_slab_interval<T: FloatScalar>(
99 slab_min: T,
100 slab_max: T,
101 ray_start: T,
102 ray_dir: T,
103 tmin: &mut T,
104 tmax: &mut T,
105) -> bool {
106 if ray_dir == <T as Zero>::zero() {
107 return ray_start >= slab_min && ray_start <= slab_max;
108 }
109
110 let inv_dir = <T as One>::one() / ray_dir;
111 let mut t0 = (slab_min - ray_start) * inv_dir;
112 let mut t1 = (slab_max - ray_start) * inv_dir;
113 if t0 > t1 {
114 core::mem::swap(&mut t0, &mut t1);
115 }
116
117 *tmin = T::max(*tmin, t0);
118 *tmax = T::min(*tmax, t1);
119 *tmin <= *tmax
120}
121
122impl<T: FloatScalar> Intersect<Ray<T, Vector3<T>>> for Box3<T> {
123 fn intersect(&self, other: &Ray<T, Vector3<T>>) -> bool {
124 let mut tmin = -T::infinity();
135 let mut tmax = T::infinity();
136
137 if !update_slab_interval(
138 self.min.x,
139 self.max.x,
140 other.start.x,
141 other.direction.x,
142 &mut tmin,
143 &mut tmax,
144 ) {
145 return false;
146 }
147 if !update_slab_interval(
148 self.min.y,
149 self.max.y,
150 other.start.y,
151 other.direction.y,
152 &mut tmin,
153 &mut tmax,
154 ) {
155 return false;
156 }
157 if !update_slab_interval(
158 self.min.z,
159 self.max.z,
160 other.start.z,
161 other.direction.z,
162 &mut tmin,
163 &mut tmax,
164 ) {
165 return false;
166 }
167
168 tmax >= T::max(tmin, <T as Zero>::zero())
171 }
172}
173
174impl<T: FloatScalar> Intersect<Sphere3<T>> for Box3<T> {
175 fn intersect(&self, other: &Sphere3<T>) -> bool {
176 let mut dist = <T as Zero>::zero();
177
178 let c = other.center;
179 let r = other.radius;
180
181 if c.x < self.min.x {
182 dist += T::squared(c.x - self.min.x)
183 } else if c.x > self.max.x {
184 dist += T::squared(c.x - self.max.x)
185 }
186
187 if c.y < self.min.y {
188 dist += T::squared(c.y - self.min.y)
189 } else if c.y > self.max.y {
190 dist += T::squared(c.y - self.max.y)
191 }
192
193 if c.z < self.min.z {
194 dist += T::squared(c.z - self.min.z)
195 } else if c.z > self.max.z {
196 dist += T::squared(c.z - self.max.z)
197 }
198
199 r * r >= dist
200 }
201}
202
203fn is_in_0_to_1_range<T: FloatScalar>(x: T) -> bool {
204 x >= <T as Zero>::zero() && x <= <T as One>::one()
205}
206
207impl<T: FloatScalar> Intersect<Tri3<T>> for Sphere3<T> {
208 fn intersect(&self, tri: &Tri3<T>) -> bool {
209 let uvw = tri.barycentric_coordinates(&self.center);
210 let verts = tri.vertices();
211 let v0 = verts[0];
212 let v1 = verts[1];
213 let v2 = verts[2];
214 if is_in_0_to_1_range(uvw.x) && is_in_0_to_1_range(uvw.y) && is_in_0_to_1_range(uvw.z) {
215 Plane::try_from_tri(&v0, &v1, &v2, T::epsilon()).is_some_and(|p| {
216 p.distance(&self.center)
217 .is_some_and(|dist| dist <= self.radius)
218 })
219 } else {
220 let d0 = Segment::new(&v0, &v1).distance(&self.center, T::epsilon());
221 let d1 = Segment::new(&v1, &v2).distance(&self.center, T::epsilon());
222 let d2 = Segment::new(&v2, &v0).distance(&self.center, T::epsilon());
223
224 match (d0, d1, d2) {
225 (Some(d0), Some(d1), Some(d2)) => {
226 let m = T::min(d0, T::min(d1, d2));
227 m <= self.radius
228 }
229 _ => false,
230 }
231 }
232 }
233}
234
235enum TriangleIntersectionKind {
243 Ray,
244 Line,
245}
246
247fn triangle_intersection_from_point_dir<T: FloatScalar>(
248 start: &Vector3<T>,
249 direction: &Vector3<T>,
250 tri: &Tri3<T>,
251 epsilon: T,
252 kind: TriangleIntersectionKind,
253) -> Option<(T, Vector3<T>)> {
254 let verts = tri.vertices();
255 let v0 = verts[0];
256 let v1 = verts[1];
257 let v2 = verts[2];
258 let edge1 = v1 - v0;
259 let edge2 = v2 - v0;
260
261 let pvec = Vector3::cross(direction, &edge2);
262 let det = Vector3::dot(&edge1, &pvec);
263 if det > -epsilon && det < epsilon {
264 return None;
265 }
266
267 let tvec = *start - v0;
268 let qvec = Vector3::cross(&tvec, &edge1);
269
270 let u = Vector3::dot(&tvec, &pvec) / det;
271 if u < -epsilon || u > <T as One>::one() + epsilon {
272 return None;
273 }
274
275 let v = Vector3::dot(direction, &qvec) / det;
276 if v < -epsilon || u + v > <T as One>::one() + T::two() * epsilon {
277 return None;
278 }
279
280 let t = Vector3::dot(&edge2, &qvec) / det;
281 if matches!(kind, TriangleIntersectionKind::Ray) && t < -epsilon {
282 return None;
283 }
284
285 Some((t, *start + (*direction * t)))
286}
287
288impl<T: FloatScalar> Intersection<(T, Vector3<T>), Tri3<T>> for Ray<T, Vector3<T>> {
289 fn intersection(&self, tri: &Tri3<T>) -> Option<(T, Vector3<T>)> {
290 triangle_intersection_from_point_dir(
291 &self.start,
292 &self.direction,
293 tri,
294 T::epsilon(),
295 TriangleIntersectionKind::Ray,
296 )
297 }
298}
299
300impl<T: FloatScalar> Intersection<(T, Vector3<T>), Tri3<T>> for Line<T, Vector3<T>> {
301 fn intersection(&self, tri: &Tri3<T>) -> Option<(T, Vector3<T>)> {
302 triangle_intersection_from_point_dir(
303 &self.p,
304 &self.d,
305 tri,
306 T::epsilon(),
307 TriangleIntersectionKind::Line,
308 )
309 }
310}
311
312impl<T: FloatScalar> Intersection<(T, Vector3<T>), Ray<T, Vector3<T>>> for Tri3<T> {
313 fn intersection(&self, ray: &Ray<T, Vector3<T>>) -> Option<(T, Vector3<T>)> {
314 ray.intersection(self)
315 }
316}
317
318impl<T: FloatScalar> Intersection<(T, Vector3<T>), Line<T, Vector3<T>>> for Tri3<T> {
319 fn intersection(&self, line: &Line<T, Vector3<T>>) -> Option<(T, Vector3<T>)> {
320 line.intersection(self)
321 }
322}
323
324pub fn try_basis_from_unit<T: FloatScalar>(
359 unit: &Vector3<T>,
360 epsilon: T,
361) -> Option<[Vector3<T>; 3]> {
362 let u = unit.try_normalize(epsilon)?;
363 let x = u.x;
364 let y = u.y;
365 let z = u.z;
366
367 let v = if x.tabs() <= y.tabs() && x.tabs() <= z.tabs() {
368 Vector3::new(<T as Zero>::zero(), -z, y)
369 } else if y.tabs() <= x.tabs() && y.tabs() <= z.tabs() {
370 Vector3::new(-z, <T as Zero>::zero(), x)
371 } else {
372 Vector3::new(-y, x, <T as Zero>::zero())
374 };
375 let v = Vector3::normalize(&v);
376 let w = Vector3::normalize(&Vector3::cross(&u, &v));
377 Some([u, w, v])
378}
379
380pub fn basis_from_unit<T: FloatScalar>(unit: &Vector3<T>) -> [Vector3<T>; 3] {
386 try_basis_from_unit(unit, T::epsilon()).expect("basis direction must be non-zero")
387}
388
389#[cfg(test)]
390mod tests {
391 use super::{Intersect, Intersection};
392 use crate::primitives::{Box3, Line, Ray, Sphere3, Tri3};
393 use crate::vector::{FloatVector, Vector, Vector3};
394 use crate::EPS_F32;
395
396 fn assert_orthonormal_basis(basis: [Vector3<f32>; 3]) {
397 let [u, w, v] = basis;
398 assert!((u.length() - 1.0).abs() < 0.001);
399 assert!((v.length() - 1.0).abs() < 0.001);
400 assert!((w.length() - 1.0).abs() < 0.001);
401 assert!(Vector3::dot(&u, &v).abs() < 0.001);
402 assert!(Vector3::dot(&u, &w).abs() < 0.001);
403 assert!(Vector3::dot(&v, &w).abs() < 0.001);
404 }
405
406 #[test]
407 fn test_box3_sphere_intersect_z_axis() {
408 let b = Box3::new(
409 &Vector3::new(0.0f32, 0.0, 0.0),
410 &Vector3::new(1.0, 1.0, 1.0),
411 );
412
413 let outside_z = Sphere3::new(Vector3::new(0.5, 0.5, 2.0), 0.25);
414 assert!(!b.intersect(&outside_z));
415
416 let touching_z = Sphere3::new(Vector3::new(0.5, 0.5, 2.0), 1.0);
417 assert!(b.intersect(&touching_z));
418 }
419
420 #[test]
421 fn test_box3_sphere_intersect_on_edge() {
422 let b = Box3::new(
423 &Vector3::new(0.0f32, 0.0, 0.0),
424 &Vector3::new(1.0, 1.0, 1.0),
425 );
426
427 let touching = Sphere3::new(Vector3::new(2.0, 0.5, 0.5), 1.0);
428 assert!(b.intersect(&touching));
429
430 let just_inside = Sphere3::new(Vector3::new(2.0, 0.5, 0.5), 1.0001);
431 assert!(b.intersect(&just_inside));
432 }
433
434 #[test]
435 fn test_box3_ray_intersect_touching_boundary() {
436 let b = Box3::new(
437 &Vector3::new(0.0f32, 0.0, 0.0),
438 &Vector3::new(1.0, 1.0, 1.0),
439 );
440 let ray = Ray::new(
441 &Vector3::new(1.0f32, 0.5, 0.5),
442 &Vector3::new(1.0, 0.0, 0.0),
443 EPS_F32,
444 )
445 .expect("ray should be valid");
446 assert!(b.intersect(&ray));
447 }
448
449 #[test]
450 fn test_box3_ray_intersect_parallel_on_face() {
451 let b = Box3::new(
452 &Vector3::new(0.0f32, 0.0, 0.0),
453 &Vector3::new(1.0, 1.0, 1.0),
454 );
455 let ray = Ray::new(
456 &Vector3::new(0.0f32, 0.5, 0.5),
457 &Vector3::new(0.0, 1.0, 0.0),
458 EPS_F32,
459 )
460 .expect("ray should be valid");
461 assert!(b.intersect(&ray));
462 }
463
464 #[test]
465 fn test_box3_ray_intersect_parallel_outside_slab() {
466 let b = Box3::new(
467 &Vector3::new(0.0f32, 0.0, 0.0),
468 &Vector3::new(1.0, 1.0, 1.0),
469 );
470 let ray = Ray::new(
471 &Vector3::new(2.0f32, 0.5, 0.5),
472 &Vector3::new(0.0, 1.0, 0.0),
473 EPS_F32,
474 )
475 .expect("ray should be valid");
476 assert!(!b.intersect(&ray));
477 }
478
479 #[test]
480 fn test_try_basis_from_unit_zero_none() {
481 let zero = Vector3::new(0.0f32, 0.0, 0.0);
482 assert!(super::try_basis_from_unit(&zero, EPS_F32).is_none());
483 }
484
485 #[test]
486 fn test_try_basis_from_unit_orthonormal() {
487 let basis0 = super::try_basis_from_unit(&Vector3::new(1.0f32, 2.0, 3.0), EPS_F32)
488 .expect("basis should exist");
489 let basis1 = super::try_basis_from_unit(&Vector3::new(0.1f32, 4.0, 5.0), EPS_F32)
490 .expect("basis should exist");
491 let basis2 = super::try_basis_from_unit(&Vector3::new(4.0f32, 0.1, 5.0), EPS_F32)
492 .expect("basis should exist");
493 let basis3 = super::try_basis_from_unit(&Vector3::new(4.0f32, 5.0, 0.1), EPS_F32)
494 .expect("basis should exist");
495
496 assert_orthonormal_basis(basis0);
497 assert_orthonormal_basis(basis1);
498 assert_orthonormal_basis(basis2);
499 assert_orthonormal_basis(basis3);
500 }
501
502 #[test]
503 fn test_try_basis_from_unit_axes_and_ties() {
504 let basis_x =
505 super::try_basis_from_unit(&Vector3::new(1.0f32, 0.0, 0.0), EPS_F32).expect("basis");
506 let basis_y =
507 super::try_basis_from_unit(&Vector3::new(0.0f32, 1.0, 0.0), EPS_F32).expect("basis");
508 let basis_z =
509 super::try_basis_from_unit(&Vector3::new(0.0f32, 0.0, 1.0), EPS_F32).expect("basis");
510 let basis_neg =
511 super::try_basis_from_unit(&Vector3::new(-1.0f32, -1.0, -1.0), EPS_F32).expect("basis");
512
513 assert_orthonormal_basis(basis_x);
514 assert_orthonormal_basis(basis_y);
515 assert_orthonormal_basis(basis_z);
516 assert_orthonormal_basis(basis_neg);
517 }
518
519 #[test]
520 fn test_ray_triangle_intersection_rejects_hits_behind_ray() {
521 let tri = Tri3::new([
522 Vector3::new(0.0f32, 0.0, 0.0),
523 Vector3::new(1.0, 0.0, 0.0),
524 Vector3::new(0.0, 1.0, 0.0),
525 ]);
526 let ray = Ray::new(
527 &Vector3::new(0.25f32, 0.25, 1.0),
528 &Vector3::new(0.0, 0.0, 1.0),
529 EPS_F32,
530 )
531 .expect("ray should be valid");
532
533 assert!(ray.intersection(&tri).is_none());
534 assert!(tri.intersection(&ray).is_none());
535 }
536
537 #[test]
538 fn test_ray_triangle_intersection_forward_hit() {
539 let tri = Tri3::new([
540 Vector3::new(0.0f32, 0.0, 0.0),
541 Vector3::new(1.0, 0.0, 0.0),
542 Vector3::new(0.0, 1.0, 0.0),
543 ]);
544 let ray = Ray::new(
545 &Vector3::new(0.25f32, 0.25, 1.0),
546 &Vector3::new(0.0, 0.0, -1.0),
547 EPS_F32,
548 )
549 .expect("ray should be valid");
550
551 let (t0, p0) = ray.intersection(&tri).expect("ray should hit triangle");
552 let (t1, p1) = tri.intersection(&ray).expect("triangle should hit ray");
553
554 assert!((t0 - 1.0).abs() < 0.001);
555 assert!((p0.x - 0.25).abs() < 0.001);
556 assert!((p0.y - 0.25).abs() < 0.001);
557 assert!(p0.z.abs() < 0.001);
558
559 assert!((t1 - t0).abs() < 0.001);
560 assert!((p1.x - p0.x).abs() < 0.001);
561 assert!((p1.y - p0.y).abs() < 0.001);
562 assert!((p1.z - p0.z).abs() < 0.001);
563 }
564
565 #[test]
566 fn test_line_triangle_intersection_accepts_hits_on_both_sides() {
567 let tri = Tri3::new([
568 Vector3::new(0.0f32, 0.0, 0.0),
569 Vector3::new(1.0, 0.0, 0.0),
570 Vector3::new(0.0, 1.0, 0.0),
571 ]);
572 let line = Line::new(
573 &Vector3::new(0.25f32, 0.25, 1.0),
574 &Vector3::new(0.0, 0.0, 1.0),
575 EPS_F32,
576 )
577 .expect("line should be valid");
578
579 let (t0, p0) = line.intersection(&tri).expect("line should hit triangle");
580 let (t1, p1) = tri.intersection(&line).expect("triangle should hit line");
581
582 assert!((t0 + 1.0).abs() < 0.001);
583 assert!((p0.x - 0.25).abs() < 0.001);
584 assert!((p0.y - 0.25).abs() < 0.001);
585 assert!(p0.z.abs() < 0.001);
586
587 assert!((t1 - t0).abs() < 0.001);
588 assert!((p1.x - p0.x).abs() < 0.001);
589 assert!((p1.y - p0.y).abs() < 0.001);
590 assert!((p1.z - p0.z).abs() < 0.001);
591 }
592
593 #[test]
594 fn test_box3_sphere_intersect_negative_radius_canonicalized() {
595 let b = Box3::new(
596 &Vector3::new(0.0f32, 0.0, 0.0),
597 &Vector3::new(1.0, 1.0, 1.0),
598 );
599 let touching = Sphere3::new(Vector3::new(2.0f32, 0.5, 0.5), -1.0);
600 assert!(b.intersect(&touching));
601 }
602}