1use approx;
4
5use crate::*;
6use super::*;
7
8pub mod integer;
9
10#[inline]
22pub fn discrete_interval <S : Ring> (a : &Interval <S>, b : &Interval <S>) -> bool {
23 let (min_a, max_a) = (a.min(), a.max());
24 let (min_b, max_b) = (b.min(), b.max());
25 max_a > min_b && min_a < max_b
27}
28
29#[inline]
41pub fn continuous_interval <S> (a : &Interval <S>, b : &Interval <S>)
42 -> Option <Interval <S>>
43where S : Ring + std::fmt::Debug {
44 if discrete_interval (a, b) {
45 Some (
46 Interval::with_minmax (S::max (a.min(), b.min()), S::min (a.max(), b.max())))
47 } else {
48 None
49 }
50}
51
52#[inline]
64pub fn discrete_aabb2_aabb2 <S : Ring> (a : &Aabb2 <S>, b : &Aabb2 <S>) -> bool {
65 let (min_a, max_a) = (a.min(), a.max());
66 let (min_b, max_b) = (b.min(), b.max());
67 max_a.0.x > min_b.0.x && min_a.0.x < max_b.0.x &&
69 max_a.0.y > min_b.0.y && min_a.0.y < max_b.0.y
70}
71
72#[inline]
84pub fn continuous_aabb2_aabb2 <S> (a : &Aabb2 <S>, b : &Aabb2 <S>)
85 -> Option <Aabb2 <S>>
86where S : Ring + std::fmt::Debug {
87 if discrete_aabb2_aabb2 (a, b) {
88 Some (
89 Aabb2::with_minmax (point2_max (a.min(), b.min()), point2_min (a.max(), b.max())))
90 } else {
91 None
92 }
93}
94
95#[inline]
107pub fn discrete_aabb3_aabb3 <S : Ring> (a : &Aabb3 <S>, b : &Aabb3 <S>) -> bool {
108 let (min_a, max_a) = (a.min(), a.max());
109 let (min_b, max_b) = (b.min(), b.max());
110 max_a.0.x > min_b.0.x && min_a.0.x < max_b.0.x &&
112 max_a.0.y > min_b.0.y && min_a.0.y < max_b.0.y &&
113 max_a.0.z > min_b.0.z && min_a.0.z < max_b.0.z
114}
115
116#[inline]
128pub fn continuous_aabb3_aabb3 <S> (a : &Aabb3 <S>, b : &Aabb3 <S>)
129 -> Option <Aabb3 <S>>
130where S : Ring + std::fmt::Debug {
131 if discrete_aabb3_aabb3 (a, b) {
132 Some (
133 Aabb3::with_minmax (point3_max (a.min(), b.min()), point3_min (a.max(), b.max())))
134 } else {
135 None
136 }
137}
138
139#[expect(clippy::type_complexity)]
169pub fn continuous_line2_aabb2 <S> (line : &Line2 <S>, aabb : &Aabb2 <S>)
170 -> Option <((S, Point2 <S>), (S, Point2 <S>))>
171where S : Real + std::fmt::Debug {
172 let aabb_min = aabb.min();
173 let aabb_max = aabb.max();
174 if line.direction.x == S::zero() {
175 if aabb_min.0.x < line.base.0.x && line.base.0.x < aabb_max.0.x {
177 let out = if line.direction.y > S::zero() {
178 let (t0, t1) = (aabb_min.0.y - line.base.0.y, aabb_max.0.y - line.base.0.y);
179 ( (t0, [line.base.0.x, aabb_min.0.y].into()),
180 (t1, [line.base.0.x, aabb_max.0.y].into())
181 )
182 } else {
183 let (t0, t1) = (line.base.0.y - aabb_max.0.y, line.base.0.y - aabb_min.0.y);
184 ( (t0, [line.base.0.x, aabb_max.0.y].into()),
185 (t1, [line.base.0.x, aabb_min.0.y].into())
186 )
187 };
188 Some (out)
189 } else {
190 None
191 }
192 } else if line.direction.y == S::zero() {
193 if aabb_min.0.y < line.base.0.y && line.base.0.y < aabb_max.0.y {
195 let out = if line.direction.x > S::zero() {
196 let (t0, t1) = (aabb_min.0.x - line.base.0.x, aabb_max.0.x - line.base.0.x);
197 ( (t0, [aabb_min.0.x, line.base.0.y].into()),
198 (t1, [aabb_max.0.x, line.base.0.y].into())
199 )
200 } else {
201 let (t0, t1) = (line.base.0.x - aabb_max.0.x, line.base.0.x - aabb_min.0.x);
202 ( (t0, [aabb_max.0.x, line.base.0.y].into()),
203 (t1, [aabb_min.0.x, line.base.0.y].into())
204 )
205 };
206 Some (out)
207 } else {
208 None
209 }
210 } else {
211 let dir_reciprocal = line.direction.map (|s| S::one() / s);
212 let (t0_x, t1_x) = {
213 let (near_x, far_x) = if line.direction.x.is_positive() {
214 (aabb_min.0.x, aabb_max.0.x)
215 } else {
216 (aabb_max.0.x, aabb_min.0.x)
217 };
218 ( (near_x - line.base.0.x) * dir_reciprocal.x,
219 (far_x - line.base.0.x) * dir_reciprocal.x
220 )
221 };
222 let (t0_y, t1_y) = {
223 let (near_y, far_y) = if line.direction.y.is_positive() {
224 (aabb_min.0.y, aabb_max.0.y)
225 } else {
226 (aabb_max.0.y, aabb_min.0.y)
227 };
228 ( (near_y - line.base.0.y) * dir_reciprocal.y,
229 (far_y - line.base.0.y) * dir_reciprocal.y
230 )
231 };
232 let interval_x = Interval::with_minmax (t0_x, t1_x);
233 let interval_y = Interval::with_minmax (t0_y, t1_y);
234 continuous_interval (&interval_x, &interval_y).map (|interval|{
235 let start = line.point (interval.min());
236 let end = line.point (interval.max());
237 ( (interval.min(), start), (interval.max(), end) )
238 })
239 }
240}
241
242#[expect(clippy::type_complexity)]
272pub fn continuous_line2_sphere2 <S : Real> (line : &Line2 <S>, sphere : &Sphere2 <S>)
273 -> Option <((S, Point2 <S>), (S, Point2 <S>))>
274{
275 let two = S::two();
277 let four = S::four();
278 let p1 = line.base;
286 #[expect(clippy::no_effect_underscore_binding)]
287 let _p2 = line.base + *line.direction;
288 let p3 = sphere.center;
289 let r = *sphere.radius;
290 let p1p2 = &line.direction;
291 let p3p1 = p1 - p3;
292 let a = S::one();
293 let b = two * p1p2.dot (p3p1);
294 let c = p3p1.norm_squared() - r * r;
295 let discriminant = b * b - four * a * c;
298 if discriminant <= S::zero() {
299 None
300 } else {
301 let discriminant_sqrt = discriminant.sqrt();
302 let frac_2a = S::one() / (two * a);
303 let t1 = (-b - discriminant_sqrt) * frac_2a;
304 let t2 = (-b + discriminant_sqrt) * frac_2a;
305 let first = p1 + (**p1p2) * t1;
306 let second = p1 + (**p1p2) * t2;
307 Some (((t1, first), (t2, second)))
308 }
309}
310
311pub fn continuous_line3_plane3 <S> (line : &Line3 <S>, plane : &Plane3 <S>)
326 -> Option <(S, Point3 <S>)>
327where S : Real + approx::RelativeEq {
328 let normal_dot_direction = plane.normal.dot (*line.direction);
329 if approx::relative_eq!(normal_dot_direction, S::zero()) {
330 None
331 } else {
332 let plane_to_line = line.base - plane.base;
333 let t = -plane.normal.dot (plane_to_line) / normal_dot_direction;
334 let point = line.point (t);
335 Some ((t, point))
336 }
337}
338
339#[expect(clippy::type_complexity)]
371pub fn continuous_line3_aabb3 <S> (line : &Line3 <S>, aabb : &Aabb3 <S>)
372 -> Option <((S, Point3 <S>), (S, Point3 <S>))>
373where S : Real + num_traits::Float + approx::RelativeEq <Epsilon=S> + std::fmt::Debug {
374 let aabb_min = aabb.min();
375 let aabb_max = aabb.max();
376 if line.direction.x == S::zero() {
377 if aabb_min.0.x < line.base.0.x && line.base.0.x < aabb_max.0.x {
378 let line2 = Line2::new (
379 [line.base.0.y, line.base.0.z].into(),
380 Unit2::unchecked_approx ([line.direction.y, line.direction.z].into()));
381 let aabb2 = Aabb2::with_minmax (
382 [aabb_min.0.y, aabb_min.0.z].into(),
383 [aabb_max.0.y, aabb_max.0.z].into());
384 continuous_line2_aabb2 (&line2, &aabb2).map (|((t0, p0), (t1, p1))|
385 ( (t0, [line.base.0.x, p0.0.x, p0.0.y].into()),
386 (t1, [line.base.0.x, p1.0.x, p1.0.y].into())
387 )
388 )
389 } else {
390 None
391 }
392 } else if line.direction.y == S::zero() {
393 if aabb_min.0.y < line.base.0.y && line.base.0.y < aabb_max.0.y {
394 let line2 = Line2::new (
395 [line.base.0.x, line.base.0.z].into(),
396 Unit2::unchecked_approx ([line.direction.x, line.direction.z].into()));
397 let aabb2 = Aabb2::with_minmax (
398 [aabb_min.0.x, aabb_min.0.z].into(),
399 [aabb_max.0.x, aabb_max.0.z].into());
400 continuous_line2_aabb2 (&line2, &aabb2).map (|((t0, p0), (t1, p1))|
401 ( (t0, [p0.0.x, line.base.0.y, p0.0.y].into()),
402 (t1, [p1.0.x, line.base.0.y, p1.0.y].into())
403 )
404 )
405 } else {
406 None
407 }
408 } else if line.direction.z == S::zero() {
409 if aabb_min.0.z < line.base.0.z && line.base.0.z < aabb_max.0.z {
410 let line2 = Line2::new (
411 [line.base.0.x, line.base.0.y].into(),
412 Unit2::unchecked_approx ([line.direction.x, line.direction.y].into()));
413 let aabb2 = Aabb2::with_minmax (
414 [aabb_min.0.x, aabb_min.0.y].into(),
415 [aabb_max.0.x, aabb_max.0.y].into());
416 continuous_line2_aabb2 (&line2, &aabb2).map (|((t0, p0), (t1, p1))|
417 ( (t0, [p0.0.x, p0.0.y, line.base.0.z].into()),
418 (t1, [p1.0.x, p1.0.y, line.base.0.z].into())
419 )
420 )
421 } else {
422 None
423 }
424 } else {
425 let dir_reciprocal = line.direction.map (|s| S::one() / s);
426 let (t0_x, t1_x) = {
427 let (near_x, far_x) = if line.direction.x.is_positive() {
428 (aabb_min.0.x, aabb_max.0.x)
429 } else {
430 (aabb_max.0.x, aabb_min.0.x)
431 };
432 ( (near_x - line.base.0.x) * dir_reciprocal.x,
433 (far_x - line.base.0.x) * dir_reciprocal.x
434 )
435 };
436 let (t0_y, t1_y) = {
437 let (near_y, far_y) = if line.direction.y.is_positive() {
438 (aabb_min.0.y, aabb_max.0.y)
439 } else {
440 (aabb_max.0.y, aabb_min.0.y)
441 };
442 ( (near_y - line.base.0.y) * dir_reciprocal.y,
443 (far_y - line.base.0.y) * dir_reciprocal.y
444 )
445 };
446 let (t0_z, t1_z) = {
447 let (near_z, far_z) = if line.direction.z.is_positive() {
448 (aabb_min.0.z, aabb_max.0.z)
449 } else {
450 (aabb_max.0.z, aabb_min.0.z)
451 };
452 ( (near_z - line.base.0.z) * dir_reciprocal.z,
453 (far_z - line.base.0.z) * dir_reciprocal.z
454 )
455 };
456 let interval_x = Interval::with_minmax (t0_x, t1_x);
457 let interval_y = Interval::with_minmax (t0_y, t1_y);
458 continuous_interval (&interval_x, &interval_y).and_then (|interval| {
459 let interval_z = Interval::with_minmax (t0_z, t1_z);
460 continuous_interval (&interval, &interval_z).map (|interval|{
461 let start = line.point (interval.min());
462 let end = line.point (interval.max());
463 ( (interval.min(), start), (interval.max(), end) )
464 })
465 })
466 }
467}
468
469#[expect(clippy::type_complexity)]
499pub fn continuous_line3_sphere3 <S : Real> (line : &Line3 <S>, sphere : &Sphere3 <S>)
500 -> Option <((S, Point3 <S>), (S, Point3 <S>))>
501{
502 let two = S::two();
503 let four = S::four();
504 let p1 = line.base;
512 #[expect(clippy::no_effect_underscore_binding)]
513 let _p2 = line.base + *line.direction;
514 let p3 = sphere.center;
515 let r = *sphere.radius;
516 let p1p2 = &line.direction;
517 let p3p1 = p1 - p3;
518 let a = S::one();
519 let b = two * p1p2.dot (p3p1);
520 let c = p3p1.norm_squared() - r * r;
521 let discriminant = b * b - four * a * c;
524 if discriminant <= S::zero() {
525 None
526 } else {
527 let discriminant_sqrt = discriminant.sqrt();
528 let frac_2a = S::one() / (two * a);
529 let t1 = (-b - discriminant_sqrt) * frac_2a;
530 let t2 = (-b + discriminant_sqrt) * frac_2a;
531 let first = p1 + (**p1p2) * t1;
532 let second = p1 + (**p1p2) * t2;
533 Some (((t1, first), (t2, second)))
534 }
535}
536
537#[expect(clippy::type_complexity)]
544pub fn continuous_segment2_aabb2 <S> (segment : &Segment2 <S>, aabb : &Aabb2 <S>)
545 -> Option <((S, Point2 <S>), (S, Point2 <S>))>
546where S : Real + std::fmt::Debug {
547 let vector = *segment.point_b() - segment.point_a();
548 let length = vector.norm();
549 let line = Line2::new (*segment.point_a(), Unit2::unchecked (vector / length));
550 if let Some (((t0, _p0), (t1, _p1))) = continuous_line2_aabb2 (&line, aabb) {
551 let interval = Interval::with_minmax (S::zero(), length);
552 continuous_interval (&interval, &Interval::with_minmax (t0, t1)).map (
553 |interval|
554 ( (interval.min() / length, line.point (interval.min())),
555 (interval.max() / length, line.point (interval.max()))
556 )
557 )
558 } else {
559 None
560 }
561}
562
563#[expect(clippy::type_complexity)]
571pub fn continuous_segment2_sphere2 <S> (segment : &Segment2 <S>, sphere : &Sphere2 <S>)
572 -> Option <((S, Point2 <S>), (S, Point2<S>))>
573where S : Real + std::fmt::Debug {
574 let vector = *segment.point_b() - segment.point_a();
575 let length = vector.norm();
576 let line = Line2::new (*segment.point_a(), Unit2::unchecked (vector / length));
577 if let Some (((t0, _p0), (t1, _p1))) = continuous_line2_sphere2 (&line, sphere) {
578 let interval = Interval::with_minmax (S::zero(), length);
579 continuous_interval (&interval, &Interval::with_minmax (t0, t1)).map (|interval|
580 ( (interval.min() / length, line.point (interval.min())),
581 (interval.max() / length, line.point (interval.max()))
582 )
583 )
584 } else {
585 None
586 }
587}
588
589#[expect(clippy::type_complexity)]
596pub fn continuous_segment3_aabb3 <S> (segment : &Segment3 <S>, aabb : &Aabb3 <S>)
597 -> Option <((S, Point3 <S>), (S, Point3<S>))>
598where S : Real + num_traits::Float + approx::RelativeEq <Epsilon=S> + std::fmt::Debug {
599 let vector = *segment.point_b() - segment.point_a();
600 let length = vector.norm();
601 let line = Line3::new (*segment.point_a(), Unit3::unchecked_approx (vector / length));
602 if let Some (((t0, _p0), (t1, _p1))) = continuous_line3_aabb3 (&line, aabb) {
603 let interval = Interval::with_minmax (S::zero(), length);
604 continuous_interval (&interval, &Interval::with_minmax (t0, t1)).map (|interval|
605 ( (interval.min() / length, line.point (interval.min())),
606 (interval.max() / length, line.point (interval.max()))
607 )
608 )
609 } else {
610 None
611 }
612}
613
614#[expect(clippy::type_complexity)]
622pub fn continuous_segment3_sphere3 <S> (segment : &Segment3 <S>, sphere : &Sphere3 <S>)
623 -> Option <((S, Point3 <S>), (S, Point3<S>))>
624where S : Field + Sqrt {
625 let two = S::two();
626 let four = S::four();
627 let p1 = segment.point_a();
635 let p2 = segment.point_b();
636 let p3 = sphere.center;
637 let r = *sphere.radius;
638 let p1p2 = *p2-p1;
639 let p3p1 = *p1-p3;
640 let a = p1p2.norm_squared();
641 let b = two * p1p2.dot (p3p1);
642 let c = p3p1.norm_squared() - r * r;
643 let discriminant = b * b - four * a * c;
646 if discriminant <= S::zero() {
647 None
648 } else {
649 let discriminant_sqrt = discriminant.sqrt();
650 let frac_2a = S::one() / (two * a);
651 let t1 = S::max ((-b - discriminant_sqrt) * frac_2a, S::zero());
652 let t2 = S::min ((-b + discriminant_sqrt) * frac_2a, S::one());
653 if t2 <= S::zero() || S::one() <= t1 {
654 None
655 } else {
656 let first = *p1 + p1p2 * t1;
657 let second = *p1 + p1p2 * t2;
658 Some (((t1, first), (t2, second)))
659 }
660 }
661}
662
663#[expect(clippy::type_complexity)]
671pub fn continuous_segment3_cylinder3 <S> (
672 segment : &Segment3 <S>, cylinder : &Cylinder3 <S>
673) -> Option <((S, Point3 <S>), (S, Point3<S>))> where
674 S : Real + std::fmt::Debug
675{
676 let segment_aabb = segment.aabb3();
677 let cylinder_aabb = cylinder.aabb3();
678 if !discrete_aabb3_aabb3 (&segment_aabb, &cylinder_aabb) {
679 None
680 } else {
681 let p1 = segment.point_a();
682 let p2 = segment.point_b();
683 let p3 = cylinder.center;
684 let r = *cylinder.radius;
685 let r2 = r * r;
686 let p1p2 = *p2 - p1;
687 let p1_xy = Point2::from ([p1.0.x, p1.0.y]);
688 let p2_xy = Point2::from ([p2.0.x, p2.0.y]);
689 let p3_xy = Point2::from ([p3.0.x, p3.0.y]);
690 let p3_z_max = cylinder_aabb.max().0.z;
691 let p3_z_min = cylinder_aabb.min().0.z;
692 if p1_xy == p2_xy { let d2 = (p1_xy - p3_xy).norm_squared();
694 if d2 >= r2 {
695 None
696 } else {
697 let (t1, begin_z) = if p1.0.z >= p3_z_max {
698 ((p3_z_max - p1.0.z) / p1p2.z, p3_z_max)
699 } else if p1.0.z <= p3_z_min {
700 ((p3_z_min - p1.0.z) / p1p2.z, p3_z_min)
701 } else {
702 (S::zero(), p1.0.z)
703 };
704 let (t2, end_z) = if p2.0.z >= p3_z_max {
705 ((p3_z_max - p1.0.z) / p1p2.z, p3_z_max)
706 } else if p2.0.z <= p3_z_min {
707 ((p3_z_min - p1.0.z) / p1p2.z, p3_z_min)
708 } else {
709 (S::one(), p2.0.z)
710 };
711 let begin = [p1_xy.0.x, p1_xy.0.y, begin_z].into();
712 let end = [p1_xy.0.x, p1_xy.0.y, end_z ].into();
713 Some (((t1, begin), (t2, end)))
714 }
715 } else { let two = S::two();
718 let four = S::four();
719 let p1p2_xy = p1p2.xy();
720 let p3p1_xy = p1_xy - p3_xy;
721 let a = p1p2_xy.norm_squared();
722 let b = two * p1p2_xy.dot (p3p1_xy);
723 let c = p3p1_xy.norm_squared() - r * r;
724 let discriminant = b * b - four * a * c;
727 if discriminant <= S::zero() {
728 None
729 } else {
730 let discriminant_sqrt = discriminant.sqrt();
731 let frac_2a = S::one() / (two * a);
732 let t1_xy = S::max ((-b - discriminant_sqrt) * frac_2a, S::zero());
733 let t2_xy = S::min ((-b + discriminant_sqrt) * frac_2a, S::one());
734 if t2_xy <= S::zero() || S::one() <= t1_xy {
735 None
736 } else if let Some ((t1, t2)) = if p1.0.z == p2.0.z {
737 Some ((t1_xy, t2_xy))
739 } else {
740 let p1p3_z_max = p3_z_max - p1.0.z;
743 let p1p3_z_min = p3_z_min - p1.0.z;
744 let t_z_max = S::max (S::min (p1p3_z_max / p1p2.z, S::one()), S::zero());
745 let t_z_min = S::max (S::min (p1p3_z_min / p1p2.z, S::one()), S::zero());
746 let t1_z = S::min (t_z_max, t_z_min);
747 let t2_z = S::max (t_z_max, t_z_min);
748 let aabb_xy = Interval::with_minmax (t1_xy, t2_xy);
749 let aabb_z = Interval::with_minmax ( t1_z, t2_z);
750 if !aabb_xy.intersects (&aabb_z) {
751 None
752 } else {
753 Some ((S::max (t1_xy, t1_z), S::min (t2_xy, t2_z)))
754 }
755 } {
756 debug_assert!(t1 < t2);
757 debug_assert!(t1 >= S::zero());
758 debug_assert!(t1 < S::one());
759 debug_assert!(t2 > S::zero());
760 debug_assert!(t2 <= S::one());
761 let first = *p1 + p1p2 * t1;
762 let second = *p1 + p1p2 * t2;
763 Some (((t1, first), (t2, second)))
764 } else {
765 None
766 }
767 }
768 }
769 }
770}
771
772#[expect(clippy::type_complexity)]
780pub fn continuous_segment3_capsule3 <S> (
781 segment : &Segment3 <S>, capsule : &Capsule3 <S>
782) -> Option <((S, Point3 <S>), (S, Point3 <S>))> where
783 S : Real + std::fmt::Debug
784{
785 let segment_aabb = segment.aabb3();
786 let capsule_aabb = capsule.aabb3();
787 if !discrete_aabb3_aabb3 (&segment_aabb, &capsule_aabb) {
788 None
789 } else {
790 let (upper_sphere, cylinder, lower_sphere) = capsule.decompose();
792 let cylinder_result = cylinder
793 .and_then (|cylinder| segment.intersect_cylinder (&cylinder));
794 let upper_result = segment.intersect_sphere (&upper_sphere);
795 let lower_result = segment.intersect_sphere (&lower_sphere);
796 match (upper_result, cylinder_result, lower_result) {
797 (None, None, None) => None,
798 (one, None, None) | (None, one, None) | (None, None, one) => one,
799 (Some (((t1,p1), (t2,p2))), Some (((u1,q1), (u2,q2))), None) |
800 (Some (((t1,p1), (t2,p2))), None, Some (((u1,q1), (u2,q2)))) |
801 (None, Some (((t1,p1), (t2,p2))), Some (((u1,q1), (u2,q2)))) => {
802 let first = if t1 < u1 {
803 (t1,p1)
804 } else {
805 (u1,q1)
806 };
807 let second = if t2 > u2 {
808 (t2,p2)
809 } else {
810 (u2,q2)
811 };
812 Some ((first, second))
813 }
814 ( Some (((t1,p1), (t2,p2))), Some (((u1,q1), (u2,q2))), Some (((v1,r1), (v2,r2)))
815 ) => {
816 let min1 = S::min (S::min (t1, u1), v1);
817 let max2 = S::max (S::max (t2, u2), v2);
818 let first = if min1 == t1 {
819 (t1,p1)
820 } else if min1 == u1 {
821 (u1,q1)
822 } else {
823 debug_assert_eq!(min1, v1);
824 (v1,r1)
825 };
826 let second = if max2 == t2 {
827 (t2,p2)
828 } else if max2 == u2 {
829 (u2,q2)
830 } else {
831 debug_assert_eq!(max2, v2);
832 (v2,r2)
833 };
834 Some ((first, second))
835 }
836 }
837 }
838}
839
840#[inline]
855pub fn discrete_sphere2_sphere2 <S : Ring> (a : &Sphere2 <S>, b : &Sphere2 <S>) -> bool {
856 let r_ab = *(a.radius + b.radius);
857 (b.center - a.center).self_dot() < r_ab * r_ab
858}
859
860#[inline]
875pub fn discrete_sphere3_sphere3 <S : Ring> (a : &Sphere3 <S>, b : &Sphere3 <S>) -> bool {
876 let r_ab = *(a.radius + b.radius);
877 (b.center - a.center).self_dot() < r_ab * r_ab
878}
879
880#[cfg(test)]
881mod tests {
882 use super::*;
883
884 #[test]
885 fn line2_aabb2() {
886 use std::f64::consts::SQRT_2;
887 let aabb = Aabb2::with_minmax ([-1.0, -1.0].into(), [1.0, 1.0].into());
888 let line = Line2::new ([0.0, 0.0].into(), Unit2::axis_y());
889 assert_eq!(
890 continuous_line2_aabb2 (&line, &aabb).unwrap(),
891 ((-1.0, [ 0.0, -1.0].into()), (1.0, [ 0.0, 1.0].into())));
892 let line = Line2::new ([0.0, 0.0].into(), Unit2::axis_x());
893 assert_eq!(
894 continuous_line2_aabb2 (&line, &aabb).unwrap(),
895 ((-1.0, [-1.0, 0.0].into()), (1.0, [ 1.0, 0.0].into())));
896 let line = Line2::new ([0.0, 0.0].into(), Unit2::normalize ([1.0, 1.0].into()));
897 assert_eq!(
898 continuous_line2_aabb2 (&line, &aabb).unwrap(),
899 ((-SQRT_2, [-1.0, -1.0].into()), (SQRT_2, [ 1.0, 1.0].into())));
900 let line = Line2::new ([0.0, 0.0].into(), Unit2::normalize ([-1.0, -1.0].into()));
901 assert_eq!(
902 continuous_line2_aabb2 (&line, &aabb).unwrap(),
903 ((-SQRT_2, [1.0, 1.0].into()), (SQRT_2, [-1.0, -1.0].into())));
904 let line = Line2::new ([0.0, 3.0].into(), Unit2::normalize ([-1.0, -1.0].into()));
905 assert!(continuous_line2_aabb2 (&line, &aabb).is_none());
906 let line = Line2::new ([0.0, -3.0].into(), Unit2::normalize ([1.0, 1.0].into()));
907 assert!(continuous_line2_aabb2 (&line, &aabb).is_none());
908 }
909
910 #[test]
911 fn line3_aabb3() {
912 use approx::assert_ulps_eq;
913 let aabb = Aabb3::with_minmax ([-1.0, -1.0, -1.0].into(), [1.0, 1.0, 1.0].into());
914 let line = Line3::new ([0.0, 0.0, 0.0].into(), Unit3::axis_z());
915 assert_eq!(
916 continuous_line3_aabb3 (&line, &aabb).unwrap(),
917 ((-1.0, [0.0, 0.0, -1.0].into()), (1.0, [0.0, 0.0, 1.0].into())));
918 let line = Line3::new ([0.0, 0.0, 0.0].into(), Unit3::axis_y());
919 assert_eq!(
920 continuous_line3_aabb3 (&line, &aabb).unwrap(),
921 ((-1.0, [0.0, -1.0, 0.0].into()), (1.0, [0.0, 1.0, 0.0].into())));
922 {
923 let line = Line3::new (
924 [0.0, 0.0, 0.0].into(),
925 Unit3::normalize ([1.0, 1.0, 1.0].into()));
926 let result = continuous_line3_aabb3 (&line, &aabb).unwrap();
927 assert_ulps_eq!((result.0).0, -f64::sqrt_3());
928 assert_ulps_eq!((result.1).0, f64::sqrt_3());
929 assert_eq!((result.0).1, [-1.0, -1.0, -1.0].into());
930 assert_eq!((result.1).1, [ 1.0, 1.0, 1.0].into());
931 }
932 {
933 let line = Line3::new (
934 [0.0, 0.0, 0.0].into(),
935 Unit3::normalize ([-1.0, -1.0, -1.0].into()));
936 let result = continuous_line3_aabb3 (&line, &aabb).unwrap();
937 assert_ulps_eq!((result.0).0, -f64::sqrt_3());
938 assert_ulps_eq!((result.1).0, f64::sqrt_3());
939 assert_eq!((result.0).1, [ 1.0, 1.0, 1.0].into());
940 assert_eq!((result.1).1, [-1.0, -1.0, -1.0].into());
941 }
942 let line = Line3::new (
943 [0.0, 0.0, 3.0].into(),
944 Unit3::normalize ([-1.0, -1.0, -1.0].into()));
945 assert!(continuous_line3_aabb3 (&line, &aabb).is_none());
946 let line = Line3::new (
947 [0.0, 0.0, -3.0].into(),
948 Unit3::normalize ([1.0, 1.0, 1.0].into()));
949 assert!(continuous_line3_aabb3 (&line, &aabb).is_none());
950 }
951
952 #[test]
953 fn segment3_sphere3() {
954 let sphere = shape::Sphere::unit().sphere3 (Point3::origin());
955 let segment = Segment3::new ([-2.0, 0.0, 0.0].into(), [2.0, 0.0, 0.0].into());
956 assert_eq!(
957 continuous_segment3_sphere3 (&segment, &sphere).unwrap(),
958 ((0.25, [-1.0, 0.0, 0.0].into()), (0.75, [1.0, 0.0, 0.0].into())));
959 let segment = Segment3::new ([2.0, 0.0, 0.0].into(), [-2.0, 0.0, 0.0].into());
960 assert_eq!(
961 continuous_segment3_sphere3 (&segment, &sphere).unwrap(),
962 ((0.25, [1.0, 0.0, 0.0].into()), (0.75, [-1.0, 0.0, 0.0].into())));
963 let segment = Segment3::new ([0.0, 0.0, 0.0].into(), [0.0, 0.0, 2.0].into());
964 assert_eq!(
965 continuous_segment3_sphere3 (&segment, &sphere).unwrap(),
966 ((0.0, [0.0, 0.0, 0.0].into()), (0.5, [0.0, 0.0, 1.0].into())));
967 let segment = Segment3::new ([0.0, 0.0, -2.0].into(), [0.0, 0.0, 0.0].into());
968 assert_eq!(
969 continuous_segment3_sphere3 (&segment, &sphere).unwrap(),
970 ((0.5, [0.0, 0.0, -1.0].into()), (1.0, [0.0, 0.0, 0.0].into())));
971 }
972
973 #[test]
974 fn segment3_cylinder3() {
975 let cylinder = shape::Cylinder::unit().cylinder3 (Point3::origin());
976 let segment = Segment3::new ([-2.0, 0.0, 0.0].into(), [2.0, 0.0, 0.0].into());
977 assert_eq!(
978 continuous_segment3_cylinder3 (&segment, &cylinder).unwrap(),
979 ((0.25, [-1.0, 0.0, 0.0].into()), (0.75, [1.0, 0.0, 0.0].into())));
980 let segment = Segment3::new ([2.0, 0.0, 0.0].into(), [-2.0, 0.0, 0.0].into());
981 assert_eq!(
982 continuous_segment3_cylinder3 (&segment, &cylinder).unwrap(),
983 ((0.25, [1.0, 0.0, 0.0].into()), (0.75, [-1.0, 0.0, 0.0].into())));
984 let segment = Segment3::new ([0.0, 0.0, 0.0].into(), [0.0, 0.0, 2.0].into());
985 assert_eq!(
986 continuous_segment3_cylinder3 (&segment, &cylinder).unwrap(),
987 ((0.0, [0.0, 0.0, 0.0].into()), (0.5, [0.0, 0.0, 1.0].into())));
988 let segment = Segment3::new ([0.0, 0.0, -2.0].into(), [0.0, 0.0, 0.0].into());
989 assert_eq!(
990 continuous_segment3_cylinder3 (&segment, &cylinder).unwrap(),
991 ((0.5, [0.0, 0.0, -1.0].into()), (1.0, [0.0, 0.0, 0.0].into())));
992 }
993
994 #[test]
995 fn segment3_capsule3() {
996 let capsule = shape::Capsule::noisy (1.0, 1.0).capsule3 (Point3::origin());
997 let segment = Segment3::new ([-2.0, 0.0, 0.0].into(), [2.0, 0.0, 0.0].into());
998 assert_eq!(
999 continuous_segment3_capsule3 (&segment, &capsule).unwrap(),
1000 ((0.25, [-1.0, 0.0, 0.0].into()), (0.75, [1.0, 0.0, 0.0].into())));
1001 let segment = Segment3::new ([2.0, 0.0, 0.0].into(), [-2.0, 0.0, 0.0].into());
1002 assert_eq!(
1003 continuous_segment3_capsule3 (&segment, &capsule).unwrap(),
1004 ((0.25, [1.0, 0.0, 0.0].into()), (0.75, [-1.0, 0.0, 0.0].into())));
1005 let segment = Segment3::new ([0.0, 0.0, 0.0].into(), [0.0, 0.0, 4.0].into());
1006 assert_eq!(
1007 continuous_segment3_capsule3 (&segment, &capsule).unwrap(),
1008 ((0.0, [0.0, 0.0, 0.0].into()), (0.5, [0.0, 0.0, 2.0].into())));
1009 let segment = Segment3::new ([0.0, 0.0, -4.0].into(), [0.0, 0.0, 0.0].into());
1010 assert_eq!(
1011 continuous_segment3_capsule3 (&segment, &capsule).unwrap(),
1012 ((0.5, [0.0, 0.0, -2.0].into()), (1.0, [0.0, 0.0, 0.0].into())));
1013 }
1014
1015 #[test]
1016 fn segment3_aabb3() {
1017 let aabb = Aabb3::with_minmax ([1.0, -0.5, 0.0].into(), [2.0, 0.5, 1.0].into());
1018 let segment = Segment3::new ([-1.0, 0.0, 0.5].into(), [2.0, 0.0, 0.5].into());
1019 assert_eq!(
1020 continuous_segment3_aabb3 (&segment, &aabb).unwrap(),
1021 ((2.0/3.0, [1.0, 0.0, 0.5].into()), (1.0, [2.0, 0.0, 0.5].into())));
1022 }
1023
1024}