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