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