1#![expect(clippy::module_name_repetitions)]
4
5use approx;
6
7use crate::*;
8use super::*;
9
10pub mod integer;
11
12#[inline]
24pub fn intersect_interval <S> (a : Interval <S>, b : Interval <S>)
25 -> Option <Interval <S>>
26where S : MinMax + Copy + PartialOrd + std::fmt::Debug {
27 if overlaps_interval (a, b) {
28 Some (Interval::with_minmax_unchecked (
29 S::max (a.min(), b.min()), S::min (a.max(), b.max()))
30 )
31 } else {
32 None
33 }
34}
35
36#[inline]
48pub fn intersect_aabb2_aabb2 <S> (a : Aabb2 <S>, b : Aabb2 <S>) -> Option <Aabb2 <S>>
49 where S : Copy + PartialOrd + std::fmt::Debug
50{
51 if overlaps_aabb2_aabb2 (a, b) {
52 Some (Aabb2::with_minmax_unchecked (
53 point2_max (a.min(), b.min()), point2_min (a.max(), b.max()))
54 )
55 } else {
56 None
57 }
58}
59
60#[inline]
74pub fn intersect_aabb3_aabb3 <S> (a : Aabb3 <S>, b : Aabb3 <S>) -> Option <Aabb3 <S>>
75 where S : Copy + PartialOrd + std::fmt::Debug
76{
77 if overlaps_aabb3_aabb3 (a, b) {
78 Some (
79 Aabb3::with_minmax_unchecked (
80 point3_max (a.min(), b.min()), point3_min (a.max(), b.max()))
81 )
82 } else {
83 None
84 }
85}
86
87pub fn intersect_line2_aabb2 <S> (line : Line2 <S>, aabb : Aabb2 <S>)
117 -> Option <(Line2Point <S>, Line2Point <S>)>
118where S : OrderedRing + std::fmt::Debug {
119 let aabb_min = aabb.min();
120 let aabb_max = aabb.max();
121 if line.direction.x == S::zero() {
122 if aabb_min.0.x < line.base.0.x && line.base.0.x < aabb_max.0.x {
124 let out = if line.direction.y > S::zero() {
125 let (t0, t1) = (aabb_min.0.y - line.base.0.y, aabb_max.0.y - line.base.0.y);
126 ( (t0, [line.base.0.x, aabb_min.0.y].into()),
127 (t1, [line.base.0.x, aabb_max.0.y].into())
128 )
129 } else {
130 let (t0, t1) = (line.base.0.y - aabb_max.0.y, line.base.0.y - aabb_min.0.y);
131 ( (t0, [line.base.0.x, aabb_max.0.y].into()),
132 (t1, [line.base.0.x, aabb_min.0.y].into())
133 )
134 };
135 Some (out)
136 } else {
137 None
138 }
139 } else if line.direction.y == S::zero() {
140 if aabb_min.0.y < line.base.0.y && line.base.0.y < aabb_max.0.y {
142 let out = if line.direction.x > S::zero() {
143 let (t0, t1) = (aabb_min.0.x - line.base.0.x, aabb_max.0.x - line.base.0.x);
144 ( (t0, [aabb_min.0.x, line.base.0.y].into()),
145 (t1, [aabb_max.0.x, line.base.0.y].into())
146 )
147 } else {
148 let (t0, t1) = (line.base.0.x - aabb_max.0.x, line.base.0.x - aabb_min.0.x);
149 ( (t0, [aabb_max.0.x, line.base.0.y].into()),
150 (t1, [aabb_min.0.x, line.base.0.y].into())
151 )
152 };
153 Some (out)
154 } else {
155 None
156 }
157 } else {
158 let dir_reciprocal = line.direction.map (|s| S::one() / s);
159 let interval_x = line_aabb_slab_1d (
160 line.base.0.x, line.direction.x, aabb_min.0.x, aabb_max.0.x, dir_reciprocal.x);
161 let interval_y = line_aabb_slab_1d (
162 line.base.0.y, line.direction.y, aabb_min.0.y, aabb_max.0.y, dir_reciprocal.y);
163 intersect_interval (interval_x, interval_y).map (|interval|{
164 let start = line.point (interval.min());
165 let end = line.point (interval.max());
166 ( (interval.min(), start), (interval.max(), end) )
167 })
168 }
169}
170
171pub fn intersect_ray2_aabb2 <S> (_ray : Ray2 <S>, _aabb : Aabb2 <S>)
172 -> Option <(Ray2Point <S>, Ray2Point <S>)>
173{
174 unimplemented!("TODO: ray2 aabb2 intersect")
175}
176
177pub fn intersect_line2_sphere2 <S> (line : Line2 <S>, sphere : Sphere2 <S>)
207 -> Option <(Line2Point <S>, Line2Point <S>)>
208where S : OrderedField + Sqrt {
209 line_sphere_roots (line.base, *line.direction, sphere.center, sphere.radius)
211}
212
213pub fn intersect_ray2_sphere2 <S> (_ray : Ray2 <S>, _sphere : Sphere2 <S>)
214 -> Option <(Ray2Point <S>, Ray2Point <S>)>
215{
216 unimplemented!("TODO: ray2 sphere2 intersect")
217}
218
219pub fn intersect_line3_plane3 <S> (line : Line3 <S>, plane : Plane3 <S>)
234 -> Option <Line3Point <S>>
235where S : OrderedField + approx::RelativeEq {
236 let normal_dot_direction = plane.normal.dot (*line.direction);
237 if approx::relative_eq!(normal_dot_direction, S::zero()) {
238 None
239 } else {
240 let plane_to_line = line.base - plane.base;
241 let t = -plane.normal.dot (plane_to_line) / normal_dot_direction;
242 let point = line.point (t);
243 Some ((t, point))
244 }
245}
246
247pub fn intersect_ray3_plane3 <S> (_ray : Ray3 <S>, _plane : Plane3 <S>)
248 -> Option <Ray3Point <S>>
249{
250 unimplemented!("TODO: ray3 plane3 intersect")
251}
252
253pub fn intersect_line3_triangle3 <S> (line : Line3 <S>, triangle : Triangle3 <S>)
255 -> Option <Line3Point <S>>
256where
257 S : OrderedField + num::real::Real + approx::AbsDiffEq <Epsilon = S>
258{
259 line_triangle (line.affine_line(), triangle)
260}
261
262pub fn intersect_ray3_triangle3 <S> (ray : Ray3 <S>, triangle : Triangle3 <S>)
264 -> Option <Ray3Point <S>>
265where
266 S : Real + num::real::Real + approx::AbsDiffEq <Epsilon = S>
267{
268 line_triangle (ray.affine_line(), triangle)
269 .and_then (|(s, p)| NonNegative::new (s).map (|s| (s, p)))
270}
271
272pub fn intersect_line3_aabb3 <S> (line : Line3 <S>, aabb : Aabb3 <S>)
304 -> Option <(Line3Point <S>, Line3Point <S>)>
305where S : OrderedRing + num::real::Real + approx::RelativeEq <Epsilon=S> {
306 let aabb_min = aabb.min();
307 let aabb_max = aabb.max();
308 if line.direction.x == S::zero() {
309 if aabb_min.0.x < line.base.0.x && line.base.0.x < aabb_max.0.x {
310 let line2 = Line2::new (
311 [line.base.0.y, line.base.0.z].into(),
312 Unit2::unchecked_approx ([line.direction.y, line.direction.z].into()));
313 let aabb2 = Aabb2::with_minmax_unchecked (
314 [aabb_min.0.y, aabb_min.0.z].into(),
315 [aabb_max.0.y, aabb_max.0.z].into());
316 intersect_line2_aabb2 (line2, aabb2).map (|((t0, p0), (t1, p1))|
317 ( (t0, [line.base.0.x, p0.0.x, p0.0.y].into()),
318 (t1, [line.base.0.x, p1.0.x, p1.0.y].into())
319 )
320 )
321 } else {
322 None
323 }
324 } else if line.direction.y == S::zero() {
325 if aabb_min.0.y < line.base.0.y && line.base.0.y < aabb_max.0.y {
326 let line2 = Line2::new (
327 [line.base.0.x, line.base.0.z].into(),
328 Unit2::unchecked_approx ([line.direction.x, line.direction.z].into()));
329 let aabb2 = Aabb2::with_minmax_unchecked (
330 [aabb_min.0.x, aabb_min.0.z].into(),
331 [aabb_max.0.x, aabb_max.0.z].into());
332 intersect_line2_aabb2 (line2, aabb2).map (|((t0, p0), (t1, p1))|
333 ( (t0, [p0.0.x, line.base.0.y, p0.0.y].into()),
334 (t1, [p1.0.x, line.base.0.y, p1.0.y].into())
335 )
336 )
337 } else {
338 None
339 }
340 } else if line.direction.z == S::zero() {
341 if aabb_min.0.z < line.base.0.z && line.base.0.z < aabb_max.0.z {
342 let line2 = Line2::new (
343 [line.base.0.x, line.base.0.y].into(),
344 Unit2::unchecked_approx ([line.direction.x, line.direction.y].into()));
345 let aabb2 = Aabb2::with_minmax_unchecked (
346 [aabb_min.0.x, aabb_min.0.y].into(),
347 [aabb_max.0.x, aabb_max.0.y].into());
348 intersect_line2_aabb2 (line2, aabb2).map (|((t0, p0), (t1, p1))|
349 ( (t0, [p0.0.x, p0.0.y, line.base.0.z].into()),
350 (t1, [p1.0.x, p1.0.y, line.base.0.z].into())
351 )
352 )
353 } else {
354 None
355 }
356 } else {
357 let dir_reciprocal = line.direction.map (|s| S::one() / s);
358 let interval_x = line_aabb_slab_1d (
359 line.base.0.x, line.direction.x, aabb_min.0.x, aabb_max.0.x, dir_reciprocal.x);
360 let interval_y = line_aabb_slab_1d (
361 line.base.0.y, line.direction.y, aabb_min.0.y, aabb_max.0.y, dir_reciprocal.y);
362 intersect_interval (interval_x, interval_y).and_then (|interval| {
363 let interval_z = line_aabb_slab_1d (
364 line.base.0.z, line.direction.z, aabb_min.0.z, aabb_max.0.z, dir_reciprocal.z);
365 intersect_interval (interval, interval_z).map (|interval|{
366 let start = line.point (interval.min());
367 let end = line.point (interval.max());
368 ( (interval.min(), start), (interval.max(), end) )
369 })
370 })
371 }
372}
373
374pub fn intersect_ray3_aabb3 <S> (_ray : Ray3 <S>, _aabb : Aabb3 <S>)
375 -> Option <(Ray3Point <S>, Ray3Point <S>)>
376{
377 unimplemented!("TODO: ray3 aabb3 intersect")
378}
379
380pub fn intersect_line3_sphere3 <S> (line : Line3 <S>, sphere : Sphere3 <S>)
410 -> Option <(Line3Point <S>, Line3Point <S>)>
411where S : OrderedField + Sqrt {
412 line_sphere_roots (line.base, *line.direction, sphere.center, sphere.radius)
413}
414
415pub fn intersect_ray3_sphere3 <S> (_ray : Ray3 <S>, _sphere : Sphere3 <S>)
416 -> Option <(Ray3Point <S>, Ray3Point <S>)>
417{
418 unimplemented!("TODO: ray3 sphere3 intersect")
419}
420
421pub fn intersect_segment2_aabb2 <S> (segment : Segment2 <S>, aabb : Aabb2 <S>)
428 -> Option <(Segment2Point <S>, Segment2Point <S>)>
429where S : Real {
430 let vector = segment.point_b() - segment.point_a();
431 let length = *vector.norm();
432 let line = Line2::new (segment.point_a(), Unit2::unchecked (vector / length));
433 if let Some (((t0, _p0), (t1, _p1))) = intersect_line2_aabb2 (line, aabb) {
434 clip_line_query_to_segment (length, t0, t1, |t| line.point (t))
435 } else {
436 None
437 }
438}
439
440pub fn intersect_segment2_sphere2 <S> (segment : Segment2 <S>, sphere : Sphere2 <S>)
448 -> Option <(Segment2Point <S>, Segment2Point <S>)>
449where S : Real {
450 let vector = segment.point_b() - segment.point_a();
451 let length = *vector.norm();
452 let line = Line2::new (segment.point_a(), Unit2::unchecked (vector / length));
453 if let Some (((t0, _p0), (t1, _p1))) = intersect_line2_sphere2 (line, sphere) {
454 clip_line_query_to_segment (length, t0, t1, |t| line.point (t))
455 } else {
456 None
457 }
458}
459
460pub fn intersect_segment3_aabb3 <S> (segment : Segment3 <S>, aabb : Aabb3 <S>)
467 -> Option <(Segment3Point <S>, Segment3Point <S>)>
468where S : Real + num::Float + approx::RelativeEq <Epsilon=S> {
469 let vector = segment.point_b() - segment.point_a();
470 let length = *vector.norm();
471 let line = Line3::new (segment.point_a(), Unit3::unchecked_approx (vector / length));
472 if let Some (((t0, _p0), (t1, _p1))) = intersect_line3_aabb3 (line, aabb) {
473 clip_line_query_to_segment (length, t0, t1, |t| line.point (t))
474 } else {
475 None
476 }
477}
478
479pub fn intersect_segment3_sphere3 <S> (segment : Segment3 <S>, sphere : Sphere3 <S>)
487 -> Option <(Segment3Point <S>, Segment3Point <S>)>
488where S : OrderedField + Sqrt {
489 let two = S::two();
490 let four = S::four();
491 let p1 = segment.point_a();
499 let p2 = segment.point_b();
500 let p3 = sphere.center;
501 let r = *sphere.radius;
502 let p1p2 = p2 - p1;
503 let p3p1 = p1 - p3;
504 let a = *p1p2.norm_squared();
505 let b = two * p1p2.dot (p3p1);
506 let c = *p3p1.norm_squared() - r * r;
507 let discriminant = b * b - four * a * c;
510 if discriminant <= S::zero() {
511 None
512 } else {
513 let discriminant_sqrt = discriminant.sqrt();
514 let frac_2a = S::one() / (two * a);
515 let t1 = S::max ((-b - discriminant_sqrt) * frac_2a, S::zero());
516 let t2 = S::min ((-b + discriminant_sqrt) * frac_2a, S::one());
517 if t2 <= S::zero() || S::one() <= t1 {
518 None
519 } else {
520 let first = p1 + p1p2 * t1;
521 let second = p1 + p1p2 * t2;
522 Some ((
523 (Normalized::unchecked (t1), first),
524 (Normalized::unchecked (t2), second)
525 ))
526 }
527 }
528}
529
530pub fn intersect_segment3_cylinder3 <S : Real> (
538 segment : Segment3 <S>, cylinder : Cylinder3 <S>
539) -> Option <(Segment3Point <S>, Segment3Point <S>)> {
540 let segment_aabb = segment.aabb3();
541 let cylinder_aabb = cylinder.aabb3();
542 if !overlaps_aabb3_aabb3 (segment_aabb, cylinder_aabb) {
543 None
544 } else {
545 let p1 = segment.point_a();
546 let p2 = segment.point_b();
547 let p3 = cylinder.center;
548 let r = cylinder.radius;
549 let r2 = r * r;
550 let p1p2 = p2 - p1;
551 let p1_xy = Point2::from ([p1.0.x, p1.0.y]);
552 let p2_xy = Point2::from ([p2.0.x, p2.0.y]);
553 let p3_xy = Point2::from ([p3.0.x, p3.0.y]);
554 let p3_z_max = cylinder_aabb.max().0.z;
555 let p3_z_min = cylinder_aabb.min().0.z;
556 if p1_xy == p2_xy { let d2 = (p1_xy - p3_xy).norm_squared();
558 if *d2 >= *r2 {
559 None
560 } else {
561 let (t1, begin_z) = if p1.0.z >= p3_z_max {
562 ((p3_z_max - p1.0.z) / p1p2.z, p3_z_max)
563 } else if p1.0.z <= p3_z_min {
564 ((p3_z_min - p1.0.z) / p1p2.z, p3_z_min)
565 } else {
566 (S::zero(), p1.0.z)
567 };
568 let (t2, end_z) = if p2.0.z >= p3_z_max {
569 ((p3_z_max - p1.0.z) / p1p2.z, p3_z_max)
570 } else if p2.0.z <= p3_z_min {
571 ((p3_z_min - p1.0.z) / p1p2.z, p3_z_min)
572 } else {
573 (S::one(), p2.0.z)
574 };
575 let begin = [p1_xy.0.x, p1_xy.0.y, begin_z].into();
576 let end = [p1_xy.0.x, p1_xy.0.y, end_z ].into();
577 Some ((
578 (Normalized::unchecked (t1), begin),
579 (Normalized::unchecked (t2), end)
580 ))
581 }
582 } else { let two = S::two();
585 let four = S::four();
586 let p1p2_xy = p1p2.xy();
587 let p3p1_xy = p1_xy - p3_xy;
588 let a = p1p2_xy.norm_squared();
589 let b = two * p1p2_xy.dot (p3p1_xy);
590 let c = *p3p1_xy.norm_squared() - *r2;
591 let discriminant = b * b - four * *a * c;
594 if discriminant <= S::zero() {
595 None
596 } else {
597 let discriminant_sqrt = discriminant.sqrt();
598 let frac_2a = S::one() / (two * *a);
599 let t1_xy = S::max ((-b - discriminant_sqrt) * frac_2a, S::zero());
600 let t2_xy = S::min ((-b + discriminant_sqrt) * frac_2a, S::one());
601 if t2_xy <= S::zero() || S::one() <= t1_xy {
602 None
603 } else if let Some ((t1, t2)) = if p1.0.z == p2.0.z {
604 Some ((t1_xy, t2_xy))
606 } else {
607 let p1p3_z_max = p3_z_max - p1.0.z;
610 let p1p3_z_min = p3_z_min - p1.0.z;
611 let t_z_max = S::max (S::min (p1p3_z_max / p1p2.z, S::one()), S::zero());
612 let t_z_min = S::max (S::min (p1p3_z_min / p1p2.z, S::one()), S::zero());
613 let t1_z = S::min (t_z_max, t_z_min);
614 let t2_z = S::max (t_z_max, t_z_min);
615 let aabb_xy = Interval::with_minmax_unchecked (t1_xy, t2_xy);
616 let aabb_z = Interval::with_minmax_unchecked (t1_z, t2_z);
617 if !aabb_xy.intersects (aabb_z) {
618 None
619 } else {
620 Some ((S::max (t1_xy, t1_z), S::min (t2_xy, t2_z)))
621 }
622 } {
623 debug_assert!(t1 < t2);
624 debug_assert!(t1 >= S::zero());
625 debug_assert!(t1 < S::one());
626 debug_assert!(t2 > S::zero());
627 debug_assert!(t2 <= S::one());
628 let first = p1 + p1p2 * t1;
629 let second = p1 + p1p2 * t2;
630 Some ((
631 (Normalized::unchecked (t1), first),
632 (Normalized::unchecked (t2), second)
633 ))
634 } else {
635 None
636 }
637 }
638 }
639 }
640}
641
642pub fn intersect_segment3_capsule3 <S : Real> (
650 segment : Segment3 <S>, capsule : Capsule3 <S>
651) -> Option <(Segment3Point <S>, Segment3Point <S>)> {
652 let segment_aabb = segment.aabb3();
653 let capsule_aabb = capsule.aabb3();
654 if !overlaps_aabb3_aabb3 (segment_aabb, capsule_aabb) {
655 None
656 } else {
657 let (upper_sphere, cylinder, lower_sphere) = capsule.decompose();
659 let cylinder_result = cylinder
660 .and_then (|cylinder| segment.intersect_cylinder (cylinder));
661 let upper_result = segment.intersect_sphere (upper_sphere);
662 let lower_result = segment.intersect_sphere (lower_sphere);
663 match (upper_result, cylinder_result, lower_result) {
664 (None, None, None) => None,
665 (one, None, None) | (None, one, None) | (None, None, one) => one,
666 (Some (((t1,p1), (t2,p2))), Some (((u1,q1), (u2,q2))), None) |
667 (Some (((t1,p1), (t2,p2))), None, Some (((u1,q1), (u2,q2)))) |
668 (None, Some (((t1,p1), (t2,p2))), Some (((u1,q1), (u2,q2)))) => {
669 let first = if t1 < u1 {
670 (t1,p1)
671 } else {
672 (u1,q1)
673 };
674 let second = if t2 > u2 {
675 (t2,p2)
676 } else {
677 (u2,q2)
678 };
679 Some ((first, second))
680 }
681 ( Some (((t1,p1), (t2,p2))), Some (((u1,q1), (u2,q2))), Some (((v1,r1), (v2,r2)))
682 ) => {
683 let min1 = Normalized::min (Normalized::min (t1, u1), v1);
684 let max2 = Normalized::max (Normalized::max (t2, u2), v2);
685 let first = if min1 == t1 {
686 (t1,p1)
687 } else if min1 == u1 {
688 (u1,q1)
689 } else {
690 debug_assert_eq!(min1, v1);
691 (v1,r1)
692 };
693 let second = if max2 == t2 {
694 (t2,p2)
695 } else if max2 == u2 {
696 (u2,q2)
697 } else {
698 debug_assert_eq!(max2, v2);
699 (v2,r2)
700 };
701 Some ((first, second))
702 }
703 }
704 }
705}
706
707pub fn intersect_segment3_hull3 <S> (
715 segment : Segment3 <S>, hull : &Hull3 <S>, mesh : &mesh::VertexEdgeTriangleMesh
716) -> Option <(Segment3Point <S>, Segment3Point <S>)> where
717 S : OrderedField + Sqrt + approx::RelativeEq <Epsilon = S>
718{
719 use num::One;
720 let line = segment.affine_line();
721 let mut start = None;
722 let mut end = None;
723 for triangle in mesh.triangles().values() {
724 let face = hull.triangle (triangle);
725 if let Some ((t, p)) = line_triangle (line, face) {
726 if let Some ((s, q)) = start {
727 if t < s {
728 end = Some ((s, q));
729 start = Some ((t, p));
730 } else {
731 if let Some ((s, _q)) = end {
732 if t > s {
733 end = Some ((t, p));
734 }
735 } else {
736 end = Some ((t, p));
737 }
738 }
739 if t != s {
742 break
743 }
744 } else {
745 start = Some ((t, p));
746 }
747 }
748 }
749 match (start, end) {
750 (Some ((t, p)), Some ((s, q))) => {
751 debug_assert!(t < s, "t: {t:?}, s: {s:?}");
752 if t < S::zero() && s > S::one() {
753 Some ((
754 (Normalized::zero(), segment.point_a()),
755 (Normalized::one(), segment.point_b())
756 ))
757 } else if t >= S::one() {
758 debug_assert!(s > S::one());
759 None
760 } else if s <= S::zero() {
761 None
762 } else {
763 let (t, p) = if t < S::zero() {
764 (Normalized::zero(), segment.point_a())
765 } else {
766 (Normalized::unchecked (t), p)
767 };
768 let (s, q) = if s > S::one() {
769 (Normalized::one(), segment.point_b())
770 } else {
771 (Normalized::unchecked (s), q)
772 };
773 Some (((t, p), (s, q)))
774 }
775 }
776 (Some(_), None) | (None, Some(_)) => unreachable!(),
777 (None, None) => None
778 }
779}
780
781pub fn intersect_segment3_triangle3 <S> (
783 segment : Segment3 <S>, triangle : Triangle3 <S>
784) -> Option <Segment3Point <S>> where
785 S : OrderedField + approx::AbsDiffEq <Epsilon = S>
786{
787 line_triangle (segment.affine_line(), triangle)
788 .and_then (|(s, p)| Normalized::new (s).map (|s| (s, p)))
789}
790
791#[inline]
803pub fn overlaps_interval <S> (a : Interval <S>, b : Interval <S>) -> bool where
804 S : Copy + PartialOrd
805{
806 let (min_a, max_a) = (a.min(), a.max());
807 let (min_b, max_b) = (b.min(), b.max());
808 max_a > min_b && min_a < max_b
810}
811
812#[inline]
824pub fn overlaps_aabb2_aabb2 <S> (a : Aabb2 <S>, b : Aabb2 <S>) -> bool where
825 S : Copy + PartialOrd
826{
827 let (min_a, max_a) = (a.min(), a.max());
828 let (min_b, max_b) = (b.min(), b.max());
829 max_a.0.x > min_b.0.x && min_a.0.x < max_b.0.x &&
831 max_a.0.y > min_b.0.y && min_a.0.y < max_b.0.y
832}
833
834#[inline]
848pub fn overlaps_aabb3_aabb3 <S> (a : Aabb3 <S>, b : Aabb3 <S>) -> bool where
849 S : Copy + PartialOrd
850{
851 let (min_a, max_a) = (a.min(), a.max());
852 let (min_b, max_b) = (b.min(), b.max());
853 max_a.0.x > min_b.0.x && min_a.0.x < max_b.0.x &&
855 max_a.0.y > min_b.0.y && min_a.0.y < max_b.0.y &&
856 max_a.0.z > min_b.0.z && min_a.0.z < max_b.0.z
857}
858
859#[inline]
861pub fn overlaps_triangle3_segment3 <S> (
862 triangle : Triangle3 <S>, segment : Segment3 <S>
863) -> bool where
864 S : OrderedField + num::real::Real + approx::AbsDiffEq <Epsilon = S>
865{
866 intersect_segment3_triangle3 (segment, triangle).is_some()
867}
868
869pub fn overlaps_triangle3_triangle3 <S> (
871 triangle_a : Triangle3 <S>, triangle_b : Triangle3 <S>
872) -> bool where
873 S : Real + num::real::Real + approx::AbsDiffEq <Epsilon = S>
874{
875 for edge in triangle_a.edges() {
877 if overlaps_triangle3_segment3 (triangle_b, edge) {
878 return true
879 }
880 }
881 for edge in triangle_b.edges() {
882 if overlaps_triangle3_segment3 (triangle_a, edge) {
883 return true
884 }
885 }
886 false
887}
888
889#[inline]
904pub fn overlaps_sphere2_sphere2 <S : OrderedRing> (a : Sphere2 <S>, b : Sphere2 <S>)
905 -> bool
906{
907 let r_ab = a.radius + b.radius;
908 (b.center - a.center).self_dot() < (r_ab * r_ab).into()
909}
910
911#[inline]
926pub fn overlaps_sphere3_sphere3 <S : OrderedRing> (a : Sphere3 <S>, b : Sphere3 <S>)
927 -> bool
928{
929 let r_ab = a.radius + b.radius;
930 (b.center - a.center).self_dot() < (r_ab * r_ab).into()
931}
932
933pub fn line_triangle <S> (line : frame::Line3 <S>, triangle : Triangle3 <S>)
956 -> Option <Line3Point <S>>
957where S : OrderedField + approx::AbsDiffEq <Epsilon = S> {
958 let r = line.basis;
960 let edge1 = triangle.point_b() - triangle.point_a();
961 let edge2 = triangle.point_c() - triangle.point_a();
962 let p = r.cross (edge2);
964 let a = edge1.dot (p);
965 if approx::abs_diff_eq!(a, S::zero()) {
966 return None
967 }
968 let f = S::one() / a;
970 let s = line.origin - triangle.point_a();
971 let u = f * s.dot (p);
972 if u < S::zero() || u > S::one() {
973 return None
974 }
975 let q = s.cross (edge1);
977 let v = f * r.dot (q);
978 if v < S::zero() || u + v > S::one() {
979 return None
980 }
981 let t = f * edge2.dot (q);
983 Some ((t, line.origin + *r * t))
984}
985
986fn clip_line_query_to_segment <S, P, F> (
992 length : S, t0 : S, t1 : S, line_get_point : F
993) -> Option <((Normalized <S>, P), (Normalized <S>, P))> where
994 S : Field + MinMax + Copy + PartialOrd + std::fmt::Debug,
995 F : Fn(S) -> P
996{
997 let interval = Interval::with_minmax_unchecked (S::zero(), length);
998 intersect_interval (interval, Interval::with_minmax_unchecked (t0, t1))
999 .map (|interval|(
1000 ( Normalized::unchecked (interval.min() / length),
1001 line_get_point (interval.min())
1002 ),
1003 ( Normalized::unchecked (interval.max() / length),
1004 line_get_point (interval.max())
1005 )
1006 ))
1007}
1008
1009fn line_aabb_slab_1d <S : OrderedRing> (
1010 line_base : S,
1011 line_direction : S,
1012 aabb_min : S,
1013 aabb_max : S,
1014 dir_reciprocal : S
1015) -> Interval <S> {
1016 let (near, far) = if line_direction.is_positive() {
1017 (aabb_min, aabb_max)
1018 } else {
1019 (aabb_max, aabb_min)
1020 };
1021 Interval::with_minmax_unchecked (
1022 (near - line_base) * dir_reciprocal,
1023 (far - line_base) * dir_reciprocal)
1024}
1025
1026#[expect(clippy::doc_markdown)]
1034fn line_sphere_roots <S, P, V> (
1035 line_base : P,
1036 line_direction : V,
1037 sphere_center : P,
1038 sphere_radius : Positive <S>
1039) -> Option <((S, P), (S, P))> where
1040 S : OrderedField + Sqrt,
1041 P : Point <V> + std::ops::Add <V, Output=P> + Copy,
1042 V : NormedVectorSpace <S> + GroupAction <Addition, P>
1043{
1044 let two = S::two();
1045 let four = S::four();
1046 let p1 = line_base;
1047 #[expect(clippy::no_effect_underscore_binding)]
1048 let _p2 = line_base + line_direction;
1049 let p3 = sphere_center;
1050 let r = *sphere_radius;
1051 let p1p2 = line_direction;
1052 let p3p1 = p1 - p3;
1053 let a = S::one();
1054 let b = two * p1p2.dot (p3p1);
1055 let c = *p3p1.norm_squared() - r * r;
1056 let discriminant = b * b - four * a * c;
1059 if discriminant <= S::zero() {
1060 None
1061 } else {
1062 let discriminant_sqrt = discriminant.sqrt();
1063 let frac_2a = S::one() / (two * a);
1064 let t1 = (-b - discriminant_sqrt) * frac_2a;
1065 let t2 = (-b + discriminant_sqrt) * frac_2a;
1066 let first = p1 + p1p2 * t1;
1067 let second = p1 + p1p2 * t2;
1068 Some (((t1, first), (t2, second)))
1069 }
1070}
1071
1072#[cfg(test)]
1077mod tests {
1078 extern crate test;
1079
1080 use super::*;
1081
1082 #[expect(clippy::cast_possible_truncation)]
1083 static RAY_TRIANGLE_BENCH_SEED : std::sync::LazyLock <u64> = std::sync::LazyLock::new (
1084 || std::time::SystemTime::UNIX_EPOCH.elapsed().unwrap().as_nanos() as u64);
1085
1086 #[test]
1087 fn line2_aabb2() {
1088 use std::f64::consts::SQRT_2;
1089 let aabb = Aabb2::with_minmax_unchecked ([-1.0, -1.0].into(), [1.0, 1.0].into());
1090 let line = Line2::new ([0.0, 0.0].into(), Unit2::axis_y());
1091 assert_eq!(
1092 intersect_line2_aabb2 (line, aabb).unwrap(),
1093 ((-1.0, [ 0.0, -1.0].into()), (1.0, [ 0.0, 1.0].into())));
1094 let line = Line2::new ([0.0, 0.0].into(), Unit2::axis_x());
1095 assert_eq!(
1096 intersect_line2_aabb2 (line, aabb).unwrap(),
1097 ((-1.0, [-1.0, 0.0].into()), (1.0, [ 1.0, 0.0].into())));
1098 let line = Line2::new ([0.0, 0.0].into(), Unit2::normalize ([1.0, 1.0].into()));
1099 assert_eq!(
1100 intersect_line2_aabb2 (line, aabb).unwrap(),
1101 ((-SQRT_2, [-1.0, -1.0].into()), (SQRT_2, [ 1.0, 1.0].into())));
1102 let line = Line2::new ([0.0, 0.0].into(), Unit2::normalize ([-1.0, -1.0].into()));
1103 assert_eq!(
1104 intersect_line2_aabb2 (line, aabb).unwrap(),
1105 ((-SQRT_2, [1.0, 1.0].into()), (SQRT_2, [-1.0, -1.0].into())));
1106 let line = Line2::new ([0.0, 3.0].into(), Unit2::normalize ([-1.0, -1.0].into()));
1107 assert!(intersect_line2_aabb2 (line, aabb).is_none());
1108 let line = Line2::new ([0.0, -3.0].into(), Unit2::normalize ([1.0, 1.0].into()));
1109 assert!(intersect_line2_aabb2 (line, aabb).is_none());
1110 }
1111
1112 #[test]
1113 fn line3_aabb3() {
1114 use approx::assert_ulps_eq;
1115 let aabb = Aabb3::with_minmax_unchecked (
1116 [-1.0, -1.0, -1.0].into(), [1.0, 1.0, 1.0].into());
1117 let line = Line3::new ([0.0, 0.0, 0.0].into(), Unit3::axis_z());
1118 assert_eq!(
1119 intersect_line3_aabb3 (line, aabb).unwrap(),
1120 ((-1.0, [0.0, 0.0, -1.0].into()), (1.0, [0.0, 0.0, 1.0].into())));
1121 let line = Line3::new ([0.0, 0.0, 0.0].into(), Unit3::axis_y());
1122 assert_eq!(
1123 intersect_line3_aabb3 (line, aabb).unwrap(),
1124 ((-1.0, [0.0, -1.0, 0.0].into()), (1.0, [0.0, 1.0, 0.0].into())));
1125 {
1126 let line = Line3::new (
1127 [0.0, 0.0, 0.0].into(),
1128 Unit3::normalize ([1.0, 1.0, 1.0].into()));
1129 let result = intersect_line3_aabb3 (line, aabb).unwrap();
1130 assert_ulps_eq!((result.0).0, -f64::sqrt_3());
1131 assert_ulps_eq!((result.1).0, f64::sqrt_3());
1132 assert_eq!((result.0).1, [-1.0, -1.0, -1.0].into());
1133 assert_eq!((result.1).1, [ 1.0, 1.0, 1.0].into());
1134 }
1135 {
1136 let line = Line3::new (
1137 [0.0, 0.0, 0.0].into(),
1138 Unit3::normalize ([-1.0, -1.0, -1.0].into()));
1139 let result = intersect_line3_aabb3 (line, aabb).unwrap();
1140 assert_ulps_eq!((result.0).0, -f64::sqrt_3());
1141 assert_ulps_eq!((result.1).0, f64::sqrt_3());
1142 assert_eq!((result.0).1, [ 1.0, 1.0, 1.0].into());
1143 assert_eq!((result.1).1, [-1.0, -1.0, -1.0].into());
1144 }
1145 let line = Line3::new (
1146 [0.0, 0.0, 3.0].into(),
1147 Unit3::normalize ([-1.0, -1.0, -1.0].into()));
1148 assert!(intersect_line3_aabb3 (line, aabb).is_none());
1149 let line = Line3::new (
1150 [0.0, 0.0, -3.0].into(),
1151 Unit3::normalize ([1.0, 1.0, 1.0].into()));
1152 assert!(intersect_line3_aabb3 (line, aabb).is_none());
1153 }
1154
1155 #[test]
1156 fn segment3_sphere3() {
1157 let sphere = shape::Sphere::unit().sphere3 (Point3::origin());
1158 let segment = Segment3::noisy ([-2.0, 0.0, 0.0].into(), [2.0, 0.0, 0.0].into());
1159 assert_eq!(
1160 intersect_segment3_sphere3 (segment, sphere)
1161 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1162 ((0.25, [-1.0, 0.0, 0.0].into()), (0.75, [1.0, 0.0, 0.0].into())));
1163 let segment = Segment3::noisy ([2.0, 0.0, 0.0].into(), [-2.0, 0.0, 0.0].into());
1164 assert_eq!(
1165 intersect_segment3_sphere3 (segment, sphere)
1166 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1167 ((0.25, [1.0, 0.0, 0.0].into()), (0.75, [-1.0, 0.0, 0.0].into())));
1168 let segment = Segment3::noisy ([0.0, 0.0, 0.0].into(), [0.0, 0.0, 2.0].into());
1169 assert_eq!(
1170 intersect_segment3_sphere3 (segment, sphere)
1171 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1172 ((0.0, [0.0, 0.0, 0.0].into()), (0.5, [0.0, 0.0, 1.0].into())));
1173 let segment = Segment3::noisy ([0.0, 0.0, -2.0].into(), [0.0, 0.0, 0.0].into());
1174 assert_eq!(
1175 intersect_segment3_sphere3 (segment, sphere)
1176 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1177 ((0.5, [0.0, 0.0, -1.0].into()), (1.0, [0.0, 0.0, 0.0].into())));
1178 }
1179
1180 #[test]
1181 fn segment3_cylinder3() {
1182 let cylinder = shape::Cylinder::unit().cylinder3 (Point3::origin());
1183 let segment = Segment3::noisy ([-2.0, 0.0, 0.0].into(), [2.0, 0.0, 0.0].into());
1184 assert_eq!(
1185 intersect_segment3_cylinder3 (segment, cylinder)
1186 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1187 ((0.25, [-1.0, 0.0, 0.0].into()), (0.75, [1.0, 0.0, 0.0].into())));
1188 let segment = Segment3::noisy ([2.0, 0.0, 0.0].into(), [-2.0, 0.0, 0.0].into());
1189 assert_eq!(
1190 intersect_segment3_cylinder3 (segment, cylinder)
1191 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1192 ((0.25, [1.0, 0.0, 0.0].into()), (0.75, [-1.0, 0.0, 0.0].into())));
1193 let segment = Segment3::noisy ([0.0, 0.0, 0.0].into(), [0.0, 0.0, 2.0].into());
1194 assert_eq!(
1195 intersect_segment3_cylinder3 (segment, cylinder)
1196 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1197 ((0.0, [0.0, 0.0, 0.0].into()), (0.5, [0.0, 0.0, 1.0].into())));
1198 let segment = Segment3::noisy ([0.0, 0.0, -2.0].into(), [0.0, 0.0, 0.0].into());
1199 assert_eq!(
1200 intersect_segment3_cylinder3 (segment, cylinder)
1201 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1202 ((0.5, [0.0, 0.0, -1.0].into()), (1.0, [0.0, 0.0, 0.0].into())));
1203 }
1204
1205 #[test]
1206 fn segment3_capsule3() {
1207 let capsule = shape::Capsule::noisy (1.0, 1.0).capsule3 (Point3::origin());
1208 let segment = Segment3::noisy ([-2.0, 0.0, 0.0].into(), [2.0, 0.0, 0.0].into());
1209 assert_eq!(
1210 intersect_segment3_capsule3 (segment, capsule)
1211 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1212 ((0.25, [-1.0, 0.0, 0.0].into()), (0.75, [1.0, 0.0, 0.0].into())));
1213 let segment = Segment3::noisy ([2.0, 0.0, 0.0].into(), [-2.0, 0.0, 0.0].into());
1214 assert_eq!(
1215 intersect_segment3_capsule3 (segment, capsule)
1216 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1217 ((0.25, [1.0, 0.0, 0.0].into()), (0.75, [-1.0, 0.0, 0.0].into())));
1218 let segment = Segment3::noisy ([0.0, 0.0, 0.0].into(), [0.0, 0.0, 4.0].into());
1219 assert_eq!(
1220 intersect_segment3_capsule3 (segment, capsule)
1221 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1222 ((0.0, [0.0, 0.0, 0.0].into()), (0.5, [0.0, 0.0, 2.0].into())));
1223 let segment = Segment3::noisy ([0.0, 0.0, -4.0].into(), [0.0, 0.0, 0.0].into());
1224 assert_eq!(
1225 intersect_segment3_capsule3 (segment, capsule)
1226 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1227 ((0.5, [0.0, 0.0, -2.0].into()), (1.0, [0.0, 0.0, 0.0].into())));
1228 }
1229
1230 #[test]
1231 fn segment3_aabb3() {
1232 let aabb =
1233 Aabb3::with_minmax_unchecked ([1.0, -0.5, 0.0].into(), [2.0, 0.5, 1.0].into());
1234 let segment = Segment3::noisy ([-1.0, 0.0, 0.5].into(), [2.0, 0.0, 0.5].into());
1235 assert_eq!(
1236 intersect_segment3_aabb3 (segment, aabb)
1237 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1238 ((2.0/3.0, [1.0, 0.0, 0.5].into()), (1.0, [2.0, 0.0, 0.5].into())));
1239 }
1240
1241 #[test]
1242 fn segment3_hull3() {
1243 let (hull, mesh) = Hull3::from_points_with_mesh (&[
1244 [ 0.0, 0.0, 1.0],
1245 [ -1.0, -1.0, -1.0],
1246 [ 1.0, -1.0, -1.0],
1247 [ 0.0, 1.0, -1.0]
1248 ].map (Point3::from)).unwrap();
1249 let segment = Segment3::noisy ([0.0, 0.0, -2.0].into(), [0.0, 0.0, 0.0].into());
1250 assert_eq!(
1251 intersect_segment3_hull3 (segment, &hull, &mesh)
1252 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1253 ((0.5, [0.0, 0.0, -1.0].into()), (1.0, [0.0, 0.0, 0.0].into())));
1254 let segment = Segment3::noisy ([0.0, 0.0, 0.0].into(), [0.0, 0.0, -2.0].into());
1255 assert_eq!(
1256 intersect_segment3_hull3 (segment, &hull, &mesh)
1257 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1258 ((0.0, [0.0, 0.0, 0.0].into()), (0.5, [0.0, 0.0, -1.0].into())));
1259 let segment = Segment3::noisy ([0.0, 0.0, -0.5].into(), [0.0, 0.0, 0.5].into());
1260 assert_eq!(
1261 intersect_segment3_hull3 (segment, &hull, &mesh)
1262 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1263 ((0.0, [0.0, 0.0, -0.5].into()), (1.0, [0.0, 0.0, 0.5].into())));
1264 }
1265
1266 #[test]
1267 fn line_triangle_intersect() {
1268 let triangle = Triangle3::noisy (
1269 [0.0, 1.0, 1.0].into(), [-1.0, -1.0, 1.0].into(), [ 1.0, -1.0, 1.0].into());
1270 let line = Segment3::noisy ([0.0, 0.0, -1.0].into(), [0.0, 0.0, 1.0].into());
1271 assert_eq!(
1272 line_triangle (line.into(), triangle).unwrap(),
1273 (1.0, [0.0, 0.0, 1.0].into()));
1274 let line = Segment3::noisy ([0.0, 0.0, 1.0].into(), [0.0, 0.0, 2.0].into());
1275 assert_eq!(
1276 line_triangle (line.into(), triangle).unwrap(),
1277 (0.0, [0.0, 0.0, 1.0].into()));
1278 let line = Segment3::noisy ([0.0, 0.0, 2.0].into(), [0.0, 0.0, 3.0].into());
1279 assert_eq!(
1280 line_triangle (line.into(), triangle).unwrap(),
1281 (-1.0, [0.0, 0.0, 1.0].into()));
1282 let line = Segment3::noisy ([5.0, 5.0, 1.0].into(), [5.0, 5.0, 2.0].into());
1283 assert!(line_triangle (line.into(), triangle).is_none());
1284 }
1285
1286 #[bench]
1287 fn bench_line_triangle (b : &mut test::Bencher) {
1288 use rand::SeedableRng;
1289 let aabb = Aabb3::with_minmax_unchecked (
1290 [-10.0, -10.0, -10.0].into(),
1291 [ 10.0, 10.0, 10.0].into());
1292 let mut rng = rand_xorshift::XorShiftRng::seed_from_u64 (*RAY_TRIANGLE_BENCH_SEED);
1293 let mut iter = 0;
1294 let mut num_intersections = 0;
1295 b.iter(||{
1296 let line = Segment3::noisy (
1297 aabb.rand_point (&mut rng), aabb.rand_point (&mut rng));
1298 let triangle = Triangle3::noisy (
1299 aabb.rand_point (&mut rng), aabb.rand_point (&mut rng),
1300 aabb.rand_point (&mut rng));
1301 if line_triangle (line.into(), triangle).is_some() && iter <= 1_000_000 {
1302 num_intersections += 1;
1303 }
1304 iter += 1;
1305 });
1306 let n = Ord::min (iter, 1_000_000);
1307 println!("{num_intersections}/{n} intersections");
1308 }
1309
1310}