1use approx;
4
5use crate::*;
6use super::*;
7
8pub mod integer;
9
10#[inline]
22pub fn discrete_interval <S> (a : Interval <S>, b : Interval <S>) -> bool where
23 S : Copy + PartialOrd
24{
25 let (min_a, max_a) = (a.min(), a.max());
26 let (min_b, max_b) = (b.min(), b.max());
27 max_a > min_b && min_a < max_b
29}
30
31#[inline]
43pub fn continuous_interval <S> (a : Interval <S>, b : Interval <S>)
44 -> Option <Interval <S>>
45where S : MinMax + Copy + PartialOrd + std::fmt::Debug {
46 if discrete_interval (a, b) {
47 Some (Interval::with_minmax_unchecked (
48 S::max (a.min(), b.min()), S::min (a.max(), b.max()))
49 )
50 } else {
51 None
52 }
53}
54
55#[inline]
67pub fn discrete_aabb2_aabb2 <S> (a : Aabb2 <S>, b : Aabb2 <S>) -> bool where
68 S : Copy + PartialOrd
69{
70 let (min_a, max_a) = (a.min(), a.max());
71 let (min_b, max_b) = (b.min(), b.max());
72 max_a.0.x > min_b.0.x && min_a.0.x < max_b.0.x &&
74 max_a.0.y > min_b.0.y && min_a.0.y < max_b.0.y
75}
76
77#[inline]
89pub fn continuous_aabb2_aabb2 <S> (a : Aabb2 <S>, b : Aabb2 <S>) -> Option <Aabb2 <S>>
90 where S : Copy + PartialOrd + std::fmt::Debug
91{
92 if discrete_aabb2_aabb2 (a, b) {
93 Some (Aabb2::with_minmax_unchecked (
94 point2_max (a.min(), b.min()), point2_min (a.max(), b.max()))
95 )
96 } else {
97 None
98 }
99}
100
101#[inline]
115pub fn discrete_aabb3_aabb3 <S> (a : Aabb3 <S>, b : Aabb3 <S>) -> bool where
116 S : Copy + PartialOrd
117{
118 let (min_a, max_a) = (a.min(), a.max());
119 let (min_b, max_b) = (b.min(), b.max());
120 max_a.0.x > min_b.0.x && min_a.0.x < max_b.0.x &&
122 max_a.0.y > min_b.0.y && min_a.0.y < max_b.0.y &&
123 max_a.0.z > min_b.0.z && min_a.0.z < max_b.0.z
124}
125
126#[inline]
140pub fn continuous_aabb3_aabb3 <S> (a : Aabb3 <S>, b : Aabb3 <S>) -> Option <Aabb3 <S>>
141 where S : Copy + PartialOrd + std::fmt::Debug
142{
143 if discrete_aabb3_aabb3 (a, b) {
144 Some (
145 Aabb3::with_minmax_unchecked (
146 point3_max (a.min(), b.min()), point3_min (a.max(), b.max()))
147 )
148 } else {
149 None
150 }
151}
152
153pub fn continuous_line2_aabb2 <S> (line : Line2 <S>, aabb : Aabb2 <S>)
183 -> Option <(Line2Point <S>, Line2Point <S>)>
184where S : OrderedRing + std::fmt::Debug {
185 let aabb_min = aabb.min();
186 let aabb_max = aabb.max();
187 if line.direction.x == S::zero() {
188 if aabb_min.0.x < line.base.0.x && line.base.0.x < aabb_max.0.x {
190 let out = if line.direction.y > S::zero() {
191 let (t0, t1) = (aabb_min.0.y - line.base.0.y, aabb_max.0.y - line.base.0.y);
192 ( (t0, [line.base.0.x, aabb_min.0.y].into()),
193 (t1, [line.base.0.x, aabb_max.0.y].into())
194 )
195 } else {
196 let (t0, t1) = (line.base.0.y - aabb_max.0.y, line.base.0.y - aabb_min.0.y);
197 ( (t0, [line.base.0.x, aabb_max.0.y].into()),
198 (t1, [line.base.0.x, aabb_min.0.y].into())
199 )
200 };
201 Some (out)
202 } else {
203 None
204 }
205 } else if line.direction.y == S::zero() {
206 if aabb_min.0.y < line.base.0.y && line.base.0.y < aabb_max.0.y {
208 let out = if line.direction.x > S::zero() {
209 let (t0, t1) = (aabb_min.0.x - line.base.0.x, aabb_max.0.x - line.base.0.x);
210 ( (t0, [aabb_min.0.x, line.base.0.y].into()),
211 (t1, [aabb_max.0.x, line.base.0.y].into())
212 )
213 } else {
214 let (t0, t1) = (line.base.0.x - aabb_max.0.x, line.base.0.x - aabb_min.0.x);
215 ( (t0, [aabb_max.0.x, line.base.0.y].into()),
216 (t1, [aabb_min.0.x, line.base.0.y].into())
217 )
218 };
219 Some (out)
220 } else {
221 None
222 }
223 } else {
224 let dir_reciprocal = line.direction.map (|s| S::one() / s);
225 let (t0_x, t1_x) = {
226 let (near_x, far_x) = if line.direction.x.is_positive() {
227 (aabb_min.0.x, aabb_max.0.x)
228 } else {
229 (aabb_max.0.x, aabb_min.0.x)
230 };
231 ( (near_x - line.base.0.x) * dir_reciprocal.x,
232 (far_x - line.base.0.x) * dir_reciprocal.x
233 )
234 };
235 let (t0_y, t1_y) = {
236 let (near_y, far_y) = if line.direction.y.is_positive() {
237 (aabb_min.0.y, aabb_max.0.y)
238 } else {
239 (aabb_max.0.y, aabb_min.0.y)
240 };
241 ( (near_y - line.base.0.y) * dir_reciprocal.y,
242 (far_y - line.base.0.y) * dir_reciprocal.y
243 )
244 };
245 let interval_x = Interval::with_minmax_unchecked (t0_x, t1_x);
246 let interval_y = Interval::with_minmax_unchecked (t0_y, t1_y);
247 continuous_interval (interval_x, interval_y).map (|interval|{
248 let start = line.point (interval.min());
249 let end = line.point (interval.max());
250 ( (interval.min(), start), (interval.max(), end) )
251 })
252 }
253}
254
255pub fn continuous_ray2_aabb2 <S> (_ray : Ray2 <S>, _aabb : Aabb2 <S>)
256 -> Option <(Ray2Point <S>, Ray2Point <S>)>
257{
258 unimplemented!("TODO: ray2 aabb2 intersect")
259}
260
261pub fn continuous_line2_sphere2 <S> (line : Line2 <S>, sphere : Sphere2 <S>)
291 -> Option <(Line2Point <S>, Line2Point <S>)>
292where S : OrderedField + Sqrt {
293 let two = S::two();
295 let four = S::four();
296 let p1 = line.base;
304 #[expect(clippy::no_effect_underscore_binding)]
305 let _p2 = line.base + *line.direction;
306 let p3 = sphere.center;
307 let r = *sphere.radius;
308 let p1p2 = line.direction;
309 let p3p1 = p1 - p3;
310 let a = S::one();
311 let b = two * p1p2.dot (p3p1);
312 let c = *p3p1.norm_squared() - r * r;
313 let discriminant = b * b - four * a * c;
316 if discriminant <= S::zero() {
317 None
318 } else {
319 let discriminant_sqrt = discriminant.sqrt();
320 let frac_2a = S::one() / (two * a);
321 let t1 = (-b - discriminant_sqrt) * frac_2a;
322 let t2 = (-b + discriminant_sqrt) * frac_2a;
323 let first = p1 + (*p1p2) * t1;
324 let second = p1 + (*p1p2) * t2;
325 Some (((t1, first), (t2, second)))
326 }
327}
328
329pub fn continuous_ray2_sphere2 <S> (_ray : Ray2 <S>, _sphere : Sphere2 <S>)
330 -> Option <(Ray2Point <S>, Ray2Point <S>)>
331{
332 unimplemented!("TODO: ray2 sphere2 intersect")
333}
334
335pub fn continuous_line3_plane3 <S> (line : Line3 <S>, plane : Plane3 <S>)
350 -> Option <Line3Point <S>>
351where S : OrderedField + approx::RelativeEq {
352 let normal_dot_direction = plane.normal.dot (*line.direction);
353 if approx::relative_eq!(normal_dot_direction, S::zero()) {
354 None
355 } else {
356 let plane_to_line = line.base - plane.base;
357 let t = -plane.normal.dot (plane_to_line) / normal_dot_direction;
358 let point = line.point (t);
359 Some ((t, point))
360 }
361}
362
363pub fn continuous_ray3_plane3 <S> (_ray : Ray3 <S>, _plane : Plane3 <S>)
364 -> Option <Ray3Point <S>>
365{
366 unimplemented!("TODO: ray3 plane3 intersect")
367}
368
369pub fn continuous_line3_triangle3 <S> (line : Line3 <S>, triangle : Triangle3 <S>)
371 -> Option <Line3Point <S>>
372where
373 S : OrderedField + num::real::Real + approx::AbsDiffEq <Epsilon = S>
374{
375 line_triangle (line.affine_line(), triangle)
376}
377
378pub fn continuous_ray3_triangle3 <S> (ray : Ray3 <S>, triangle : Triangle3 <S>)
380 -> Option <Ray3Point <S>>
381where
382 S : Real + num::real::Real + approx::AbsDiffEq <Epsilon = S>
383{
384 line_triangle (ray.affine_line(), triangle)
385 .and_then (|(s, p)| NonNegative::new (s).map (|s| (s, p)))
386}
387
388pub fn continuous_line3_aabb3 <S> (line : Line3 <S>, aabb : Aabb3 <S>)
420 -> Option <(Line3Point <S>, Line3Point <S>)>
421where S : OrderedRing + num::real::Real + approx::RelativeEq <Epsilon=S> {
422 let aabb_min = aabb.min();
423 let aabb_max = aabb.max();
424 if line.direction.x == S::zero() {
425 if aabb_min.0.x < line.base.0.x && line.base.0.x < aabb_max.0.x {
426 let line2 = Line2::new (
427 [line.base.0.y, line.base.0.z].into(),
428 Unit2::unchecked_approx ([line.direction.y, line.direction.z].into()));
429 let aabb2 = Aabb2::with_minmax_unchecked (
430 [aabb_min.0.y, aabb_min.0.z].into(),
431 [aabb_max.0.y, aabb_max.0.z].into());
432 continuous_line2_aabb2 (line2, aabb2).map (|((t0, p0), (t1, p1))|
433 ( (t0, [line.base.0.x, p0.0.x, p0.0.y].into()),
434 (t1, [line.base.0.x, p1.0.x, p1.0.y].into())
435 )
436 )
437 } else {
438 None
439 }
440 } else if line.direction.y == S::zero() {
441 if aabb_min.0.y < line.base.0.y && line.base.0.y < aabb_max.0.y {
442 let line2 = Line2::new (
443 [line.base.0.x, line.base.0.z].into(),
444 Unit2::unchecked_approx ([line.direction.x, line.direction.z].into()));
445 let aabb2 = Aabb2::with_minmax_unchecked (
446 [aabb_min.0.x, aabb_min.0.z].into(),
447 [aabb_max.0.x, aabb_max.0.z].into());
448 continuous_line2_aabb2 (line2, aabb2).map (|((t0, p0), (t1, p1))|
449 ( (t0, [p0.0.x, line.base.0.y, p0.0.y].into()),
450 (t1, [p1.0.x, line.base.0.y, p1.0.y].into())
451 )
452 )
453 } else {
454 None
455 }
456 } else if line.direction.z == S::zero() {
457 if aabb_min.0.z < line.base.0.z && line.base.0.z < aabb_max.0.z {
458 let line2 = Line2::new (
459 [line.base.0.x, line.base.0.y].into(),
460 Unit2::unchecked_approx ([line.direction.x, line.direction.y].into()));
461 let aabb2 = Aabb2::with_minmax_unchecked (
462 [aabb_min.0.x, aabb_min.0.y].into(),
463 [aabb_max.0.x, aabb_max.0.y].into());
464 continuous_line2_aabb2 (line2, aabb2).map (|((t0, p0), (t1, p1))|
465 ( (t0, [p0.0.x, p0.0.y, line.base.0.z].into()),
466 (t1, [p1.0.x, p1.0.y, line.base.0.z].into())
467 )
468 )
469 } else {
470 None
471 }
472 } else {
473 let dir_reciprocal = line.direction.map (|s| S::one() / s);
474 let (t0_x, t1_x) = {
475 let (near_x, far_x) = if line.direction.x.is_positive() {
476 (aabb_min.0.x, aabb_max.0.x)
477 } else {
478 (aabb_max.0.x, aabb_min.0.x)
479 };
480 ( (near_x - line.base.0.x) * dir_reciprocal.x,
481 (far_x - line.base.0.x) * dir_reciprocal.x
482 )
483 };
484 let (t0_y, t1_y) = {
485 let (near_y, far_y) = if line.direction.y.is_positive() {
486 (aabb_min.0.y, aabb_max.0.y)
487 } else {
488 (aabb_max.0.y, aabb_min.0.y)
489 };
490 ( (near_y - line.base.0.y) * dir_reciprocal.y,
491 (far_y - line.base.0.y) * dir_reciprocal.y
492 )
493 };
494 let (t0_z, t1_z) = {
495 let (near_z, far_z) = if line.direction.z.is_positive() {
496 (aabb_min.0.z, aabb_max.0.z)
497 } else {
498 (aabb_max.0.z, aabb_min.0.z)
499 };
500 ( (near_z - line.base.0.z) * dir_reciprocal.z,
501 (far_z - line.base.0.z) * dir_reciprocal.z
502 )
503 };
504 let interval_x = Interval::with_minmax_unchecked (t0_x, t1_x);
505 let interval_y = Interval::with_minmax_unchecked (t0_y, t1_y);
506 continuous_interval (interval_x, interval_y).and_then (|interval| {
507 let interval_z = Interval::with_minmax_unchecked (t0_z, t1_z);
508 continuous_interval (interval, interval_z).map (|interval|{
509 let start = line.point (interval.min());
510 let end = line.point (interval.max());
511 ( (interval.min(), start), (interval.max(), end) )
512 })
513 })
514 }
515}
516
517pub fn continuous_ray3_aabb3 <S> (_ray : Ray3 <S>, _aabb : Aabb3 <S>)
518 -> Option <(Ray3Point <S>, Ray3Point <S>)>
519{
520 unimplemented!("TODO: ray3 aabb3 intersect")
521}
522
523pub fn continuous_line3_sphere3 <S> (line : Line3 <S>, sphere : Sphere3 <S>)
553 -> Option <(Line3Point <S>, Line3Point <S>)>
554where S : OrderedField + Sqrt {
555 let two = S::two();
556 let four = S::four();
557 let p1 = line.base;
565 #[expect(clippy::no_effect_underscore_binding)]
566 let _p2 = line.base + *line.direction;
567 let p3 = sphere.center;
568 let r = *sphere.radius;
569 let p1p2 = &line.direction;
570 let p3p1 = p1 - p3;
571 let a = S::one();
572 let b = two * p1p2.dot (p3p1);
573 let c = *p3p1.norm_squared() - r * r;
574 let discriminant = b * b - four * a * c;
577 if discriminant <= S::zero() {
578 None
579 } else {
580 let discriminant_sqrt = discriminant.sqrt();
581 let frac_2a = S::one() / (two * a);
582 let t1 = (-b - discriminant_sqrt) * frac_2a;
583 let t2 = (-b + discriminant_sqrt) * frac_2a;
584 let first = p1 + (**p1p2) * t1;
585 let second = p1 + (**p1p2) * t2;
586 Some (((t1, first), (t2, second)))
587 }
588}
589
590pub fn continuous_ray3_sphere3 <S> (_ray : Ray3 <S>, _sphere : Sphere3 <S>)
591 -> Option <(Ray3Point <S>, Ray3Point <S>)>
592{
593 unimplemented!("TODO: ray3 sphere3 intersect")
594}
595
596pub fn continuous_segment2_aabb2 <S> (segment : Segment2 <S>, aabb : Aabb2 <S>)
603 -> Option <(Segment2Point <S>, Segment2Point <S>)>
604where S : Real {
605 let vector = segment.point_b() - segment.point_a();
606 let length = *vector.norm();
607 let line = Line2::new (segment.point_a(), Unit2::unchecked (vector / length));
608 if let Some (((t0, _p0), (t1, _p1))) = continuous_line2_aabb2 (line, aabb) {
609 let interval = Interval::with_minmax_unchecked (S::zero(), length);
610 continuous_interval (interval, Interval::with_minmax_unchecked (t0, t1)).map (
611 |interval|
612 ( (Normalized::unchecked (interval.min() / length), line.point (interval.min())),
613 (Normalized::unchecked (interval.max() / length), line.point (interval.max()))
614 )
615 )
616 } else {
617 None
618 }
619}
620
621pub fn continuous_segment2_sphere2 <S> (segment : Segment2 <S>, sphere : Sphere2 <S>)
629 -> Option <(Segment2Point <S>, Segment2Point <S>)>
630where S : Real {
631 let vector = segment.point_b() - segment.point_a();
632 let length = *vector.norm();
633 let line = Line2::new (segment.point_a(), Unit2::unchecked (vector / length));
634 if let Some (((t0, _p0), (t1, _p1))) = continuous_line2_sphere2 (line, sphere) {
635 let interval = Interval::with_minmax_unchecked (S::zero(), length);
636 continuous_interval (interval, Interval::with_minmax_unchecked (t0, t1))
637 .map (|interval|
638 ( (Normalized::unchecked (interval.min() / length), line.point (interval.min())),
639 (Normalized::unchecked (interval.max() / length), line.point (interval.max()))
640 )
641 )
642 } else {
643 None
644 }
645}
646
647pub fn continuous_segment3_aabb3 <S> (segment : Segment3 <S>, aabb : Aabb3 <S>)
654 -> Option <(Segment3Point <S>, Segment3Point <S>)>
655where S : Real + num::Float + approx::RelativeEq <Epsilon=S> {
656 let vector = segment.point_b() - segment.point_a();
657 let length = *vector.norm();
658 let line = Line3::new (segment.point_a(), Unit3::unchecked_approx (vector / length));
659 if let Some (((t0, _p0), (t1, _p1))) = continuous_line3_aabb3 (line, aabb) {
660 let interval = Interval::with_minmax_unchecked (S::zero(), length);
661 continuous_interval (interval, Interval::with_minmax_unchecked (t0, t1))
662 .map (|interval|
663 ( (Normalized::unchecked (interval.min() / length), line.point (interval.min())),
664 (Normalized::unchecked (interval.max() / length), line.point (interval.max()))
665 )
666 )
667 } else {
668 None
669 }
670}
671
672pub fn continuous_segment3_sphere3 <S> (segment : Segment3 <S>, sphere : Sphere3 <S>)
680 -> Option <(Segment3Point <S>, Segment3Point <S>)>
681where S : OrderedField + Sqrt {
682 let two = S::two();
683 let four = S::four();
684 let p1 = segment.point_a();
692 let p2 = segment.point_b();
693 let p3 = sphere.center;
694 let r = *sphere.radius;
695 let p1p2 = p2 - p1;
696 let p3p1 = p1 - p3;
697 let a = *p1p2.norm_squared();
698 let b = two * p1p2.dot (p3p1);
699 let c = *p3p1.norm_squared() - r * r;
700 let discriminant = b * b - four * a * c;
703 if discriminant <= S::zero() {
704 None
705 } else {
706 let discriminant_sqrt = discriminant.sqrt();
707 let frac_2a = S::one() / (two * a);
708 let t1 = S::max ((-b - discriminant_sqrt) * frac_2a, S::zero());
709 let t2 = S::min ((-b + discriminant_sqrt) * frac_2a, S::one());
710 if t2 <= S::zero() || S::one() <= t1 {
711 None
712 } else {
713 let first = p1 + p1p2 * t1;
714 let second = p1 + p1p2 * t2;
715 Some ((
716 (Normalized::unchecked (t1), first),
717 (Normalized::unchecked (t2), second)
718 ))
719 }
720 }
721}
722
723pub fn continuous_segment3_cylinder3 <S : Real> (
731 segment : Segment3 <S>, cylinder : Cylinder3 <S>
732) -> Option <(Segment3Point <S>, Segment3Point <S>)> {
733 let segment_aabb = segment.aabb3();
734 let cylinder_aabb = cylinder.aabb3();
735 if !discrete_aabb3_aabb3 (segment_aabb, cylinder_aabb) {
736 None
737 } else {
738 let p1 = segment.point_a();
739 let p2 = segment.point_b();
740 let p3 = cylinder.center;
741 let r = cylinder.radius;
742 let r2 = r * r;
743 let p1p2 = p2 - p1;
744 let p1_xy = Point2::from ([p1.0.x, p1.0.y]);
745 let p2_xy = Point2::from ([p2.0.x, p2.0.y]);
746 let p3_xy = Point2::from ([p3.0.x, p3.0.y]);
747 let p3_z_max = cylinder_aabb.max().0.z;
748 let p3_z_min = cylinder_aabb.min().0.z;
749 if p1_xy == p2_xy { let d2 = (p1_xy - p3_xy).norm_squared();
751 if *d2 >= *r2 {
752 None
753 } else {
754 let (t1, begin_z) = if p1.0.z >= p3_z_max {
755 ((p3_z_max - p1.0.z) / p1p2.z, p3_z_max)
756 } else if p1.0.z <= p3_z_min {
757 ((p3_z_min - p1.0.z) / p1p2.z, p3_z_min)
758 } else {
759 (S::zero(), p1.0.z)
760 };
761 let (t2, end_z) = if p2.0.z >= p3_z_max {
762 ((p3_z_max - p1.0.z) / p1p2.z, p3_z_max)
763 } else if p2.0.z <= p3_z_min {
764 ((p3_z_min - p1.0.z) / p1p2.z, p3_z_min)
765 } else {
766 (S::one(), p2.0.z)
767 };
768 let begin = [p1_xy.0.x, p1_xy.0.y, begin_z].into();
769 let end = [p1_xy.0.x, p1_xy.0.y, end_z ].into();
770 Some ((
771 (Normalized::unchecked (t1), begin),
772 (Normalized::unchecked (t2), end)
773 ))
774 }
775 } else { let two = S::two();
778 let four = S::four();
779 let p1p2_xy = p1p2.xy();
780 let p3p1_xy = p1_xy - p3_xy;
781 let a = p1p2_xy.norm_squared();
782 let b = two * p1p2_xy.dot (p3p1_xy);
783 let c = *p3p1_xy.norm_squared() - *r2;
784 let discriminant = b * b - four * *a * c;
787 if discriminant <= S::zero() {
788 None
789 } else {
790 let discriminant_sqrt = discriminant.sqrt();
791 let frac_2a = S::one() / (two * *a);
792 let t1_xy = S::max ((-b - discriminant_sqrt) * frac_2a, S::zero());
793 let t2_xy = S::min ((-b + discriminant_sqrt) * frac_2a, S::one());
794 if t2_xy <= S::zero() || S::one() <= t1_xy {
795 None
796 } else if let Some ((t1, t2)) = if p1.0.z == p2.0.z {
797 Some ((t1_xy, t2_xy))
799 } else {
800 let p1p3_z_max = p3_z_max - p1.0.z;
803 let p1p3_z_min = p3_z_min - p1.0.z;
804 let t_z_max = S::max (S::min (p1p3_z_max / p1p2.z, S::one()), S::zero());
805 let t_z_min = S::max (S::min (p1p3_z_min / p1p2.z, S::one()), S::zero());
806 let t1_z = S::min (t_z_max, t_z_min);
807 let t2_z = S::max (t_z_max, t_z_min);
808 let aabb_xy = Interval::with_minmax_unchecked (t1_xy, t2_xy);
809 let aabb_z = Interval::with_minmax_unchecked (t1_z, t2_z);
810 if !aabb_xy.intersects (aabb_z) {
811 None
812 } else {
813 Some ((S::max (t1_xy, t1_z), S::min (t2_xy, t2_z)))
814 }
815 } {
816 debug_assert!(t1 < t2);
817 debug_assert!(t1 >= S::zero());
818 debug_assert!(t1 < S::one());
819 debug_assert!(t2 > S::zero());
820 debug_assert!(t2 <= S::one());
821 let first = p1 + p1p2 * t1;
822 let second = p1 + p1p2 * t2;
823 Some ((
824 (Normalized::unchecked (t1), first),
825 (Normalized::unchecked (t2), second)
826 ))
827 } else {
828 None
829 }
830 }
831 }
832 }
833}
834
835pub fn continuous_segment3_capsule3 <S : Real> (
843 segment : Segment3 <S>, capsule : Capsule3 <S>
844) -> Option <(Segment3Point <S>, Segment3Point <S>)> {
845 let segment_aabb = segment.aabb3();
846 let capsule_aabb = capsule.aabb3();
847 if !discrete_aabb3_aabb3 (segment_aabb, capsule_aabb) {
848 None
849 } else {
850 let (upper_sphere, cylinder, lower_sphere) = capsule.decompose();
852 let cylinder_result = cylinder
853 .and_then (|cylinder| segment.intersect_cylinder (cylinder));
854 let upper_result = segment.intersect_sphere (upper_sphere);
855 let lower_result = segment.intersect_sphere (lower_sphere);
856 match (upper_result, cylinder_result, lower_result) {
857 (None, None, None) => None,
858 (one, None, None) | (None, one, None) | (None, None, one) => one,
859 (Some (((t1,p1), (t2,p2))), Some (((u1,q1), (u2,q2))), None) |
860 (Some (((t1,p1), (t2,p2))), None, Some (((u1,q1), (u2,q2)))) |
861 (None, Some (((t1,p1), (t2,p2))), Some (((u1,q1), (u2,q2)))) => {
862 let first = if t1 < u1 {
863 (t1,p1)
864 } else {
865 (u1,q1)
866 };
867 let second = if t2 > u2 {
868 (t2,p2)
869 } else {
870 (u2,q2)
871 };
872 Some ((first, second))
873 }
874 ( Some (((t1,p1), (t2,p2))), Some (((u1,q1), (u2,q2))), Some (((v1,r1), (v2,r2)))
875 ) => {
876 let min1 = Normalized::min (Normalized::min (t1, u1), v1);
877 let max2 = Normalized::max (Normalized::max (t2, u2), v2);
878 let first = if min1 == t1 {
879 (t1,p1)
880 } else if min1 == u1 {
881 (u1,q1)
882 } else {
883 debug_assert_eq!(min1, v1);
884 (v1,r1)
885 };
886 let second = if max2 == t2 {
887 (t2,p2)
888 } else if max2 == u2 {
889 (u2,q2)
890 } else {
891 debug_assert_eq!(max2, v2);
892 (v2,r2)
893 };
894 Some ((first, second))
895 }
896 }
897 }
898}
899
900pub fn continuous_segment3_hull3 <S> (
908 segment : Segment3 <S>, hull : &Hull3 <S>, mesh : &mesh::VertexEdgeTriangleMesh
909) -> Option <(Segment3Point <S>, Segment3Point <S>)> where
910 S : OrderedField + Sqrt + approx::RelativeEq <Epsilon = S>
911{
912 use num::One;
913 let line = segment.affine_line();
914 let mut start = None;
915 let mut end = None;
916 for triangle in mesh.triangles().values() {
917 let face = hull.triangle (triangle);
918 if let Some ((t, p)) = line_triangle (line, face) {
919 if let Some ((s, q)) = start {
920 if t < s {
921 end = Some ((s, q));
922 start = Some ((t, p));
923 } else {
924 if let Some ((s, _q)) = end {
925 if t > s {
926 end = Some ((t, p));
927 }
928 } else {
929 end = Some ((t, p));
930 }
931 }
932 if t != s {
935 break
936 }
937 } else {
938 start = Some ((t, p));
939 }
940 }
941 }
942 match (start, end) {
943 (Some ((t, p)), Some ((s, q))) => {
944 debug_assert!(t < s, "t: {t:?}, s: {s:?}");
945 if t < S::zero() && s > S::one() {
946 Some ((
947 (Normalized::zero(), segment.point_a()),
948 (Normalized::one(), segment.point_b())
949 ))
950 } else if t >= S::one() {
951 debug_assert!(s > S::one());
952 None
953 } else if s <= S::zero() {
954 None
955 } else {
956 let (t, p) = if t < S::zero() {
957 (Normalized::zero(), segment.point_a())
958 } else {
959 (Normalized::unchecked (t), p)
960 };
961 let (s, q) = if s > S::one() {
962 (Normalized::one(), segment.point_b())
963 } else {
964 (Normalized::unchecked (s), q)
965 };
966 Some (((t, p), (s, q)))
967 }
968 }
969 (Some(_), None) | (None, Some(_)) => unreachable!(),
970 (None, None) => None
971 }
972}
973
974pub fn continuous_triangle3_segment3 <S> (
976 triangle : Triangle3 <S>, segment : Segment3 <S>
977) -> Option <Segment3Point <S>> where
978 S : OrderedField + approx::AbsDiffEq <Epsilon = S>
979{
980 line_triangle (segment.affine_line(), triangle)
981 .and_then (|(s, p)| Normalized::new (s).map (|s| (s, p)))
982}
983
984#[inline]
986pub fn discrete_triangle3_segment3 <S> (
987 triangle : Triangle3 <S>, segment : Segment3 <S>
988) -> bool where
989 S : OrderedField + num::real::Real + approx::AbsDiffEq <Epsilon = S>
990{
991 continuous_triangle3_segment3 (triangle, segment).is_some()
992}
993
994pub fn discrete_triangle3 <S> (triangle_a : Triangle3 <S>, triangle_b : Triangle3 <S>)
996 -> bool
997where S : Real + num::real::Real + approx::AbsDiffEq <Epsilon = S> {
998 for edge in triangle_a.edges() {
1000 if discrete_triangle3_segment3 (triangle_b, edge) {
1001 return true
1002 }
1003 }
1004 for edge in triangle_b.edges() {
1005 if discrete_triangle3_segment3 (triangle_a, edge) {
1006 return true
1007 }
1008 }
1009 false
1010}
1011
1012
1013#[inline]
1028pub fn discrete_sphere2_sphere2 <S : OrderedRing> (a : Sphere2 <S>, b : Sphere2 <S>)
1029 -> bool
1030{
1031 let r_ab = a.radius + b.radius;
1032 (b.center - a.center).self_dot() < (r_ab * r_ab).into()
1033}
1034
1035#[inline]
1050pub fn discrete_sphere3_sphere3 <S : OrderedRing> (a : Sphere3 <S>, b : Sphere3 <S>)
1051 -> bool
1052{
1053 let r_ab = a.radius + b.radius;
1054 (b.center - a.center).self_dot() < (r_ab * r_ab).into()
1055}
1056
1057pub fn line_triangle <S> (line : frame::Line3 <S>, triangle : Triangle3 <S>)
1080 -> Option <Line3Point <S>>
1081where S : OrderedField + approx::AbsDiffEq <Epsilon = S> {
1082 let r = line.basis;
1084 let edge1 = triangle.point_b() - triangle.point_a();
1085 let edge2 = triangle.point_c() - triangle.point_a();
1086 let p = r.cross (edge2);
1088 let a = edge1.dot (p);
1089 if approx::abs_diff_eq!(a, S::zero()) {
1090 return None
1091 }
1092 let f = S::one() / a;
1094 let s = line.origin - triangle.point_a();
1095 let u = f * s.dot (p);
1096 if u < S::zero() || u > S::one() {
1097 return None
1098 }
1099 let q = s.cross (edge1);
1101 let v = f * r.dot (q);
1102 if v < S::zero() || u + v > S::one() {
1103 return None
1104 }
1105 let t = f * edge2.dot (q);
1107 Some ((t, line.origin + *r * t))
1108}
1109
1110#[cfg(test)]
1111mod tests {
1112 extern crate test;
1113
1114 use super::*;
1115
1116 #[expect(clippy::cast_possible_truncation)]
1117 static RAY_TRIANGLE_BENCH_SEED : std::sync::LazyLock <u64> = std::sync::LazyLock::new (
1118 || std::time::SystemTime::UNIX_EPOCH.elapsed().unwrap().as_nanos() as u64);
1119
1120 #[test]
1121 fn line2_aabb2() {
1122 use std::f64::consts::SQRT_2;
1123 let aabb = Aabb2::with_minmax_unchecked ([-1.0, -1.0].into(), [1.0, 1.0].into());
1124 let line = Line2::new ([0.0, 0.0].into(), Unit2::axis_y());
1125 assert_eq!(
1126 continuous_line2_aabb2 (line, aabb).unwrap(),
1127 ((-1.0, [ 0.0, -1.0].into()), (1.0, [ 0.0, 1.0].into())));
1128 let line = Line2::new ([0.0, 0.0].into(), Unit2::axis_x());
1129 assert_eq!(
1130 continuous_line2_aabb2 (line, aabb).unwrap(),
1131 ((-1.0, [-1.0, 0.0].into()), (1.0, [ 1.0, 0.0].into())));
1132 let line = Line2::new ([0.0, 0.0].into(), Unit2::normalize ([1.0, 1.0].into()));
1133 assert_eq!(
1134 continuous_line2_aabb2 (line, aabb).unwrap(),
1135 ((-SQRT_2, [-1.0, -1.0].into()), (SQRT_2, [ 1.0, 1.0].into())));
1136 let line = Line2::new ([0.0, 0.0].into(), Unit2::normalize ([-1.0, -1.0].into()));
1137 assert_eq!(
1138 continuous_line2_aabb2 (line, aabb).unwrap(),
1139 ((-SQRT_2, [1.0, 1.0].into()), (SQRT_2, [-1.0, -1.0].into())));
1140 let line = Line2::new ([0.0, 3.0].into(), Unit2::normalize ([-1.0, -1.0].into()));
1141 assert!(continuous_line2_aabb2 (line, aabb).is_none());
1142 let line = Line2::new ([0.0, -3.0].into(), Unit2::normalize ([1.0, 1.0].into()));
1143 assert!(continuous_line2_aabb2 (line, aabb).is_none());
1144 }
1145
1146 #[test]
1147 fn line3_aabb3() {
1148 use approx::assert_ulps_eq;
1149 let aabb = Aabb3::with_minmax_unchecked (
1150 [-1.0, -1.0, -1.0].into(), [1.0, 1.0, 1.0].into());
1151 let line = Line3::new ([0.0, 0.0, 0.0].into(), Unit3::axis_z());
1152 assert_eq!(
1153 continuous_line3_aabb3 (line, aabb).unwrap(),
1154 ((-1.0, [0.0, 0.0, -1.0].into()), (1.0, [0.0, 0.0, 1.0].into())));
1155 let line = Line3::new ([0.0, 0.0, 0.0].into(), Unit3::axis_y());
1156 assert_eq!(
1157 continuous_line3_aabb3 (line, aabb).unwrap(),
1158 ((-1.0, [0.0, -1.0, 0.0].into()), (1.0, [0.0, 1.0, 0.0].into())));
1159 {
1160 let line = Line3::new (
1161 [0.0, 0.0, 0.0].into(),
1162 Unit3::normalize ([1.0, 1.0, 1.0].into()));
1163 let result = continuous_line3_aabb3 (line, aabb).unwrap();
1164 assert_ulps_eq!((result.0).0, -f64::sqrt_3());
1165 assert_ulps_eq!((result.1).0, f64::sqrt_3());
1166 assert_eq!((result.0).1, [-1.0, -1.0, -1.0].into());
1167 assert_eq!((result.1).1, [ 1.0, 1.0, 1.0].into());
1168 }
1169 {
1170 let line = Line3::new (
1171 [0.0, 0.0, 0.0].into(),
1172 Unit3::normalize ([-1.0, -1.0, -1.0].into()));
1173 let result = continuous_line3_aabb3 (line, aabb).unwrap();
1174 assert_ulps_eq!((result.0).0, -f64::sqrt_3());
1175 assert_ulps_eq!((result.1).0, f64::sqrt_3());
1176 assert_eq!((result.0).1, [ 1.0, 1.0, 1.0].into());
1177 assert_eq!((result.1).1, [-1.0, -1.0, -1.0].into());
1178 }
1179 let line = Line3::new (
1180 [0.0, 0.0, 3.0].into(),
1181 Unit3::normalize ([-1.0, -1.0, -1.0].into()));
1182 assert!(continuous_line3_aabb3 (line, aabb).is_none());
1183 let line = Line3::new (
1184 [0.0, 0.0, -3.0].into(),
1185 Unit3::normalize ([1.0, 1.0, 1.0].into()));
1186 assert!(continuous_line3_aabb3 (line, aabb).is_none());
1187 }
1188
1189 #[test]
1190 fn segment3_sphere3() {
1191 let sphere = shape::Sphere::unit().sphere3 (Point3::origin());
1192 let segment = Segment3::noisy ([-2.0, 0.0, 0.0].into(), [2.0, 0.0, 0.0].into());
1193 assert_eq!(
1194 continuous_segment3_sphere3 (segment, sphere)
1195 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1196 ((0.25, [-1.0, 0.0, 0.0].into()), (0.75, [1.0, 0.0, 0.0].into())));
1197 let segment = Segment3::noisy ([2.0, 0.0, 0.0].into(), [-2.0, 0.0, 0.0].into());
1198 assert_eq!(
1199 continuous_segment3_sphere3 (segment, sphere)
1200 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1201 ((0.25, [1.0, 0.0, 0.0].into()), (0.75, [-1.0, 0.0, 0.0].into())));
1202 let segment = Segment3::noisy ([0.0, 0.0, 0.0].into(), [0.0, 0.0, 2.0].into());
1203 assert_eq!(
1204 continuous_segment3_sphere3 (segment, sphere)
1205 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1206 ((0.0, [0.0, 0.0, 0.0].into()), (0.5, [0.0, 0.0, 1.0].into())));
1207 let segment = Segment3::noisy ([0.0, 0.0, -2.0].into(), [0.0, 0.0, 0.0].into());
1208 assert_eq!(
1209 continuous_segment3_sphere3 (segment, sphere)
1210 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1211 ((0.5, [0.0, 0.0, -1.0].into()), (1.0, [0.0, 0.0, 0.0].into())));
1212 }
1213
1214 #[test]
1215 fn segment3_cylinder3() {
1216 let cylinder = shape::Cylinder::unit().cylinder3 (Point3::origin());
1217 let segment = Segment3::noisy ([-2.0, 0.0, 0.0].into(), [2.0, 0.0, 0.0].into());
1218 assert_eq!(
1219 continuous_segment3_cylinder3 (segment, cylinder)
1220 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1221 ((0.25, [-1.0, 0.0, 0.0].into()), (0.75, [1.0, 0.0, 0.0].into())));
1222 let segment = Segment3::noisy ([2.0, 0.0, 0.0].into(), [-2.0, 0.0, 0.0].into());
1223 assert_eq!(
1224 continuous_segment3_cylinder3 (segment, cylinder)
1225 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1226 ((0.25, [1.0, 0.0, 0.0].into()), (0.75, [-1.0, 0.0, 0.0].into())));
1227 let segment = Segment3::noisy ([0.0, 0.0, 0.0].into(), [0.0, 0.0, 2.0].into());
1228 assert_eq!(
1229 continuous_segment3_cylinder3 (segment, cylinder)
1230 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1231 ((0.0, [0.0, 0.0, 0.0].into()), (0.5, [0.0, 0.0, 1.0].into())));
1232 let segment = Segment3::noisy ([0.0, 0.0, -2.0].into(), [0.0, 0.0, 0.0].into());
1233 assert_eq!(
1234 continuous_segment3_cylinder3 (segment, cylinder)
1235 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1236 ((0.5, [0.0, 0.0, -1.0].into()), (1.0, [0.0, 0.0, 0.0].into())));
1237 }
1238
1239 #[test]
1240 fn segment3_capsule3() {
1241 let capsule = shape::Capsule::noisy (1.0, 1.0).capsule3 (Point3::origin());
1242 let segment = Segment3::noisy ([-2.0, 0.0, 0.0].into(), [2.0, 0.0, 0.0].into());
1243 assert_eq!(
1244 continuous_segment3_capsule3 (segment, capsule)
1245 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1246 ((0.25, [-1.0, 0.0, 0.0].into()), (0.75, [1.0, 0.0, 0.0].into())));
1247 let segment = Segment3::noisy ([2.0, 0.0, 0.0].into(), [-2.0, 0.0, 0.0].into());
1248 assert_eq!(
1249 continuous_segment3_capsule3 (segment, capsule)
1250 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1251 ((0.25, [1.0, 0.0, 0.0].into()), (0.75, [-1.0, 0.0, 0.0].into())));
1252 let segment = Segment3::noisy ([0.0, 0.0, 0.0].into(), [0.0, 0.0, 4.0].into());
1253 assert_eq!(
1254 continuous_segment3_capsule3 (segment, capsule)
1255 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1256 ((0.0, [0.0, 0.0, 0.0].into()), (0.5, [0.0, 0.0, 2.0].into())));
1257 let segment = Segment3::noisy ([0.0, 0.0, -4.0].into(), [0.0, 0.0, 0.0].into());
1258 assert_eq!(
1259 continuous_segment3_capsule3 (segment, capsule)
1260 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1261 ((0.5, [0.0, 0.0, -2.0].into()), (1.0, [0.0, 0.0, 0.0].into())));
1262 }
1263
1264 #[test]
1265 fn segment3_aabb3() {
1266 let aabb =
1267 Aabb3::with_minmax_unchecked ([1.0, -0.5, 0.0].into(), [2.0, 0.5, 1.0].into());
1268 let segment = Segment3::noisy ([-1.0, 0.0, 0.5].into(), [2.0, 0.0, 0.5].into());
1269 assert_eq!(
1270 continuous_segment3_aabb3 (segment, aabb)
1271 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1272 ((2.0/3.0, [1.0, 0.0, 0.5].into()), (1.0, [2.0, 0.0, 0.5].into())));
1273 }
1274
1275 #[test]
1276 fn segment3_hull3() {
1277 let (hull, mesh) = Hull3::from_points_with_mesh (&[
1278 [ 0.0, 0.0, 1.0],
1279 [ -1.0, -1.0, -1.0],
1280 [ 1.0, -1.0, -1.0],
1281 [ 0.0, 1.0, -1.0]
1282 ].map (Point3::from)).unwrap();
1283 let segment = Segment3::noisy ([0.0, 0.0, -2.0].into(), [0.0, 0.0, 0.0].into());
1284 assert_eq!(
1285 continuous_segment3_hull3 (segment, &hull, &mesh)
1286 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1287 ((0.5, [0.0, 0.0, -1.0].into()), (1.0, [0.0, 0.0, 0.0].into())));
1288 let segment = Segment3::noisy ([0.0, 0.0, 0.0].into(), [0.0, 0.0, -2.0].into());
1289 assert_eq!(
1290 continuous_segment3_hull3 (segment, &hull, &mesh)
1291 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1292 ((0.0, [0.0, 0.0, 0.0].into()), (0.5, [0.0, 0.0, -1.0].into())));
1293 let segment = Segment3::noisy ([0.0, 0.0, -0.5].into(), [0.0, 0.0, 0.5].into());
1294 assert_eq!(
1295 continuous_segment3_hull3 (segment, &hull, &mesh)
1296 .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1297 ((0.0, [0.0, 0.0, -0.5].into()), (1.0, [0.0, 0.0, 0.5].into())));
1298 }
1299
1300 #[test]
1301 fn line_triangle_intersect() {
1302 let triangle = Triangle3::noisy (
1303 [0.0, 1.0, 1.0].into(), [-1.0, -1.0, 1.0].into(), [ 1.0, -1.0, 1.0].into());
1304 let line = Segment3::noisy ([0.0, 0.0, -1.0].into(), [0.0, 0.0, 1.0].into());
1305 assert_eq!(
1306 line_triangle (line.into(), triangle).unwrap(),
1307 (1.0, [0.0, 0.0, 1.0].into()));
1308 let line = Segment3::noisy ([0.0, 0.0, 1.0].into(), [0.0, 0.0, 2.0].into());
1309 assert_eq!(
1310 line_triangle (line.into(), triangle).unwrap(),
1311 (0.0, [0.0, 0.0, 1.0].into()));
1312 let line = Segment3::noisy ([0.0, 0.0, 2.0].into(), [0.0, 0.0, 3.0].into());
1313 assert_eq!(
1314 line_triangle (line.into(), triangle).unwrap(),
1315 (-1.0, [0.0, 0.0, 1.0].into()));
1316 let line = Segment3::noisy ([5.0, 5.0, 1.0].into(), [5.0, 5.0, 2.0].into());
1317 assert!(line_triangle (line.into(), triangle).is_none());
1318 }
1319
1320 #[bench]
1321 fn bench_line_triangle (b : &mut test::Bencher) {
1322 use rand::SeedableRng;
1323 let aabb = Aabb3::with_minmax_unchecked (
1324 [-10.0, -10.0, -10.0].into(),
1325 [ 10.0, 10.0, 10.0].into());
1326 let mut rng = rand_xorshift::XorShiftRng::seed_from_u64 (*RAY_TRIANGLE_BENCH_SEED);
1327 let mut iter = 0;
1328 let mut num_intersections = 0;
1329 b.iter(||{
1330 let line = Segment3::noisy (
1331 aabb.rand_point (&mut rng), aabb.rand_point (&mut rng));
1332 let triangle = Triangle3::noisy (
1333 aabb.rand_point (&mut rng), aabb.rand_point (&mut rng),
1334 aabb.rand_point (&mut rng));
1335 if line_triangle (line.into(), triangle).is_some() && iter <= 1_000_000 {
1336 num_intersections += 1;
1337 }
1338 iter += 1;
1339 });
1340 let n = Ord::min (iter, 1_000_000);
1341 println!("{num_intersections}/{n} intersections");
1342 }
1343
1344}