Skip to main content

math_utils/geometry/intersect/
mod.rs

1//! Primitive intersection routines
2
3#![expect(clippy::module_name_repetitions)]
4
5use approx;
6
7use crate::*;
8use super::*;
9
10pub mod integer;
11
12/// Boolean intersection computation of 1D axis-aligned bounding boxes (intervals).
13///
14/// AABBs that are merely touching return no intersection:
15///
16/// ```
17/// # use math_utils::geometry::*;
18/// # use math_utils::geometry::intersect::*;
19/// let a = Interval::with_minmax ( 0.0, 1.0).unwrap();
20/// let b = Interval::with_minmax (-1.0, 0.0).unwrap();
21/// assert!(intersect_interval (a, b).is_none());
22/// ```
23#[inline]
24pub fn intersect_interval <S> (a : Interval <S>, b : Interval <S>)
25  -> Option <Interval <S>>
26where S : MinMax + Copy + PartialOrd + std::fmt::Debug {
27  if overlaps_interval (a, b) {
28    Some (Interval::with_minmax_unchecked (
29      S::max (a.min(), b.min()), S::min (a.max(), b.max()))
30    )
31  } else {
32    None
33  }
34}
35
36/// Boolean intersection computation of 2D axis-aligned bounding boxes.
37///
38/// AABBs that are merely touching return no intersection:
39///
40/// ```
41/// # use math_utils::geometry::*;
42/// # use math_utils::geometry::intersect::*;
43/// let a = Aabb2::with_minmax ([ 0.0, 0.0].into(), [1.0, 1.0].into()).unwrap();
44/// let b = Aabb2::with_minmax ([-1.0, 0.0].into(), [0.0, 1.0].into()).unwrap();
45/// assert!(intersect_aabb2_aabb2 (a, b).is_none());
46/// ```
47#[inline]
48pub fn intersect_aabb2_aabb2 <S> (a : Aabb2 <S>, b : Aabb2 <S>) -> Option <Aabb2 <S>>
49  where S : Copy + PartialOrd + std::fmt::Debug
50{
51  if overlaps_aabb2_aabb2 (a, b) {
52    Some (Aabb2::with_minmax_unchecked (
53      point2_max (a.min(), b.min()), point2_min (a.max(), b.max()))
54    )
55  } else {
56    None
57  }
58}
59
60/// Boolean intersection computation of 3D axis-aligned bounding boxes.
61///
62/// AABBs that are merely touching return no intersection:
63///
64/// ```
65/// # use math_utils::geometry::*;
66/// # use math_utils::geometry::intersect::*;
67/// let a = Aabb3::with_minmax ([ 0.0, 0.0, 0.0].into(), [1.0, 1.0, 1.0].into())
68///   .unwrap();
69/// let b = Aabb3::with_minmax ([-1.0, 0.0, 0.0].into(), [0.0, 1.0, 1.0].into())
70///   .unwrap();
71/// assert!(intersect_aabb3_aabb3 (a, b).is_none());
72/// ```
73#[inline]
74pub fn intersect_aabb3_aabb3 <S> (a : Aabb3 <S>, b : Aabb3 <S>) -> Option <Aabb3 <S>>
75  where S : Copy + PartialOrd + std::fmt::Debug
76{
77  if overlaps_aabb3_aabb3 (a, b) {
78    Some (
79      Aabb3::with_minmax_unchecked (
80        point3_max (a.min(), b.min()), point3_min (a.max(), b.max()))
81    )
82  } else {
83    None
84  }
85}
86
87/// Boolean intersection computation of a 2D line with a 2D AABB (rectangle).
88///
89/// If the line and AABB intersect, the two intersection points are returned with the
90/// scalar parameter corresponding to that point along the line.
91///
92/// ```
93/// # use math_utils::*;
94/// # use math_utils::geometry::*;
95/// # use math_utils::geometry::intersect::*;
96/// let aabb = Aabb2::with_minmax ([-1.0, -1.0].into(), [1.0, 1.0].into()).unwrap();
97/// let line = Line2::new  ([0.0, 0.0].into(), Unit2::axis_x());
98/// assert_eq!(
99///   intersect_line2_aabb2 (line, aabb).unwrap(),
100///   ( (-1.0, [-1.0, 0.0].into()),
101///     ( 1.0, [ 1.0, 0.0].into())
102///   )
103/// );
104/// ```
105///
106/// Returns `None` if the line and AABB are tangent:
107///
108/// ```
109/// # use math_utils::*;
110/// # use math_utils::geometry::*;
111/// # use math_utils::geometry::intersect::*;
112/// let aabb = Aabb2::with_minmax ([-1.0, -1.0].into(), [1.0, 1.0].into()).unwrap();
113/// let line = Line2::new  ([0.0, 1.0].into(), Unit2::axis_x());
114/// assert_eq!(intersect_line2_aabb2 (line, aabb), None);
115/// ```
116pub fn intersect_line2_aabb2 <S> (line : Line2 <S>, aabb : Aabb2 <S>)
117  -> Option <(Line2Point <S>, Line2Point <S>)>
118where S : OrderedRing + std::fmt::Debug {
119  let aabb_min = aabb.min();
120  let aabb_max = aabb.max();
121  if line.direction.x == S::zero() {
122    // parallel x-axis
123    if aabb_min.0.x < line.base.0.x && line.base.0.x < aabb_max.0.x {
124      let out = if line.direction.y > S::zero() {
125        let (t0, t1) = (aabb_min.0.y - line.base.0.y, aabb_max.0.y - line.base.0.y);
126        ( (t0, [line.base.0.x, aabb_min.0.y].into()),
127          (t1, [line.base.0.x, aabb_max.0.y].into())
128        )
129      } else {
130        let (t0, t1) = (line.base.0.y - aabb_max.0.y, line.base.0.y - aabb_min.0.y);
131        ( (t0, [line.base.0.x, aabb_max.0.y].into()),
132          (t1, [line.base.0.x, aabb_min.0.y].into())
133        )
134      };
135      Some (out)
136    } else {
137      None
138    }
139  } else if line.direction.y == S::zero() {
140    // parallel y-axis
141    if aabb_min.0.y < line.base.0.y && line.base.0.y < aabb_max.0.y {
142      let out = if line.direction.x > S::zero() {
143        let (t0, t1) = (aabb_min.0.x - line.base.0.x, aabb_max.0.x - line.base.0.x);
144        ( (t0, [aabb_min.0.x, line.base.0.y].into()),
145          (t1, [aabb_max.0.x, line.base.0.y].into())
146        )
147      } else {
148        let (t0, t1) = (line.base.0.x - aabb_max.0.x, line.base.0.x - aabb_min.0.x);
149        ( (t0, [aabb_max.0.x, line.base.0.y].into()),
150          (t1, [aabb_min.0.x, line.base.0.y].into())
151        )
152      };
153      Some (out)
154    } else {
155      None
156    }
157  } else {
158    let dir_reciprocal = line.direction.map (|s| S::one() / s);
159    let interval_x = line_aabb_slab_1d (
160      line.base.0.x, line.direction.x, aabb_min.0.x, aabb_max.0.x, dir_reciprocal.x);
161    let interval_y = line_aabb_slab_1d (
162      line.base.0.y, line.direction.y, aabb_min.0.y, aabb_max.0.y, dir_reciprocal.y);
163    intersect_interval (interval_x, interval_y).map (|interval|{
164      let start = line.point (interval.min());
165      let end   = line.point (interval.max());
166      ( (interval.min(), start), (interval.max(), end) )
167    })
168  }
169}
170
171pub fn intersect_ray2_aabb2 <S> (_ray : Ray2 <S>, _aabb : Aabb2 <S>)
172  -> Option <(Ray2Point <S>, Ray2Point <S>)>
173{
174  unimplemented!("TODO: ray2 aabb2 intersect")
175}
176
177/// Boolean intersection computation of a 2D line with a 2D sphere (circle).
178///
179/// If the line and circle intersect, the two intersection points are returned with the
180/// scalar parameter corresponding to that point along the line.
181///
182/// ```
183/// # use math_utils::*;
184/// # use math_utils::geometry::*;
185/// # use math_utils::geometry::intersect::*;
186/// let sphere = Sphere2::unit();
187/// let line   = Line2::new  ([0.0, 0.0].into(), Unit2::axis_x());
188/// assert_eq!(
189///   intersect_line2_sphere2 (line, sphere).unwrap(),
190///   ( (-1.0, [-1.0, 0.0].into()),
191///     ( 1.0, [ 1.0, 0.0].into())
192///   )
193/// );
194/// ```
195///
196/// Returns `None` if the line and circle are tangent:
197///
198/// ```
199/// # use math_utils::*;
200/// # use math_utils::geometry::*;
201/// # use math_utils::geometry::intersect::*;
202/// let sphere = Sphere2::unit();
203/// let line   = Line2::new  ([0.0, 1.0].into(), Unit2::axis_x());
204/// assert_eq!(intersect_line2_sphere2 (line, sphere), None);
205/// ```
206pub fn intersect_line2_sphere2 <S> (line : Line2 <S>, sphere : Sphere2 <S>)
207  -> Option <(Line2Point <S>, Line2Point <S>)>
208where S : OrderedField + Sqrt {
209  // intersect the line with the cylinder circle in the XY plane
210  line_sphere_roots (line.base, *line.direction, sphere.center, sphere.radius)
211}
212
213pub fn intersect_ray2_sphere2 <S> (_ray : Ray2 <S>, _sphere : Sphere2 <S>)
214  -> Option <(Ray2Point <S>, Ray2Point <S>)>
215{
216  unimplemented!("TODO: ray2 sphere2 intersect")
217}
218
219/// Boolean intersection computation of a 3D line with a 3D plane.
220///
221/// Returns `None` if the line and plane are parallel:
222///
223/// ```
224/// # use math_utils::*;
225/// # use math_utils::geometry::*;
226/// # use math_utils::geometry::intersect::*;
227/// let plane = Plane3::new ([0.0, 0.0,  0.0].into(), Unit3::axis_z());
228/// let line  = Line3::new  (
229///   [0.0, 0.0, 0.0].into(),
230///   Unit3::normalize ([1.0, 1.0, 0.0].into()));
231/// assert_eq!(intersect_line3_plane3 (line, plane), None);
232/// ```
233pub fn intersect_line3_plane3 <S> (line : Line3 <S>, plane : Plane3 <S>)
234  -> Option <Line3Point <S>>
235where S : OrderedField + approx::RelativeEq {
236  let normal_dot_direction = plane.normal.dot (*line.direction);
237  if approx::relative_eq!(normal_dot_direction, S::zero()) {
238    None
239  } else {
240    let plane_to_line = line.base - plane.base;
241    let t     = -plane.normal.dot (plane_to_line) / normal_dot_direction;
242    let point = line.point (t);
243    Some ((t, point))
244  }
245}
246
247pub fn intersect_ray3_plane3 <S> (_ray : Ray3 <S>, _plane : Plane3 <S>)
248  -> Option <Ray3Point <S>>
249{
250  unimplemented!("TODO: ray3 plane3 intersect")
251}
252
253/// Boolean intersection computation of a line with a triangle
254pub fn intersect_line3_triangle3 <S> (line : Line3 <S>, triangle : Triangle3 <S>)
255  -> Option <Line3Point <S>>
256where
257  S : OrderedField + num::real::Real + approx::AbsDiffEq <Epsilon = S>
258{
259  line_triangle (line.affine_line(), triangle)
260}
261
262/// Boolean intersection computation of a ray with a triangle
263pub fn intersect_ray3_triangle3 <S> (ray : Ray3 <S>, triangle : Triangle3 <S>)
264  -> Option <Ray3Point <S>>
265where
266  S : Real + num::real::Real + approx::AbsDiffEq <Epsilon = S>
267{
268  line_triangle (ray.affine_line(), triangle)
269    .and_then (|(s, p)| NonNegative::new (s).map (|s| (s, p)))
270}
271
272/// Boolean intersection computation of a 3D line with a 3D AABB.
273///
274/// If the line and AABB intersect, the two intersection points are returned with the
275/// scalar parameter corresponding to that point along the line.
276///
277/// ```
278/// # use math_utils::*;
279/// # use math_utils::geometry::*;
280/// # use math_utils::geometry::intersect::*;
281/// let aabb = Aabb3::with_minmax ([-1.0, -1.0, -1.0].into(), [1.0, 1.0, 1.0].into())
282///   .unwrap();
283/// let line = Line3::new  ([0.0, 0.0, 0.0].into(), Unit3::axis_x());
284/// assert_eq!(
285///   intersect_line3_aabb3 (line, aabb).unwrap(),
286///   ( (-1.0, [-1.0, 0.0, 0.0].into()),
287///     ( 1.0, [ 1.0, 0.0, 0.0].into())
288///   )
289/// );
290/// ```
291///
292/// Returns `None` if the line and AABB are tangent:
293///
294/// ```
295/// # use math_utils::*;
296/// # use math_utils::geometry::*;
297/// # use math_utils::geometry::intersect::*;
298/// let aabb = Aabb3::with_minmax ([-1.0, -1.0, -1.0].into(), [1.0, 1.0, 1.0].into())
299///   .unwrap();
300/// let line = Line3::new  ([0.0, 1.0, 0.0].into(), Unit3::axis_x());
301/// assert_eq!(intersect_line3_aabb3 (line, aabb), None);
302/// ```
303pub fn intersect_line3_aabb3 <S> (line : Line3 <S>, aabb : Aabb3 <S>)
304  -> Option <(Line3Point <S>, Line3Point <S>)>
305where S : OrderedRing + num::real::Real + approx::RelativeEq <Epsilon=S> {
306  let aabb_min = aabb.min();
307  let aabb_max = aabb.max();
308  if line.direction.x == S::zero() {
309    if aabb_min.0.x < line.base.0.x && line.base.0.x < aabb_max.0.x {
310      let line2 = Line2::new (
311        [line.base.0.y, line.base.0.z].into(),
312        Unit2::unchecked_approx ([line.direction.y, line.direction.z].into()));
313      let aabb2 = Aabb2::with_minmax_unchecked (
314        [aabb_min.0.y, aabb_min.0.z].into(),
315        [aabb_max.0.y, aabb_max.0.z].into());
316      intersect_line2_aabb2 (line2, aabb2).map (|((t0, p0), (t1, p1))|
317        ( (t0, [line.base.0.x, p0.0.x, p0.0.y].into()),
318          (t1, [line.base.0.x, p1.0.x, p1.0.y].into())
319        )
320      )
321    } else {
322      None
323    }
324  } else if line.direction.y == S::zero() {
325    if aabb_min.0.y < line.base.0.y && line.base.0.y < aabb_max.0.y {
326      let line2 = Line2::new (
327        [line.base.0.x, line.base.0.z].into(),
328        Unit2::unchecked_approx ([line.direction.x, line.direction.z].into()));
329      let aabb2 = Aabb2::with_minmax_unchecked (
330        [aabb_min.0.x, aabb_min.0.z].into(),
331        [aabb_max.0.x, aabb_max.0.z].into());
332      intersect_line2_aabb2 (line2, aabb2).map (|((t0, p0), (t1, p1))|
333        ( (t0, [p0.0.x, line.base.0.y, p0.0.y].into()),
334          (t1, [p1.0.x, line.base.0.y, p1.0.y].into())
335        )
336      )
337    } else {
338      None
339    }
340  } else if line.direction.z == S::zero() {
341    if aabb_min.0.z < line.base.0.z && line.base.0.z < aabb_max.0.z {
342      let line2 = Line2::new (
343        [line.base.0.x, line.base.0.y].into(),
344        Unit2::unchecked_approx ([line.direction.x, line.direction.y].into()));
345      let aabb2 = Aabb2::with_minmax_unchecked (
346        [aabb_min.0.x, aabb_min.0.y].into(),
347        [aabb_max.0.x, aabb_max.0.y].into());
348      intersect_line2_aabb2 (line2, aabb2).map (|((t0, p0), (t1, p1))|
349        ( (t0, [p0.0.x, p0.0.y, line.base.0.z].into()),
350          (t1, [p1.0.x, p1.0.y, line.base.0.z].into())
351        )
352      )
353    } else {
354      None
355    }
356  } else {
357    let dir_reciprocal = line.direction.map (|s| S::one() / s);
358    let interval_x = line_aabb_slab_1d (
359      line.base.0.x, line.direction.x, aabb_min.0.x, aabb_max.0.x, dir_reciprocal.x);
360    let interval_y = line_aabb_slab_1d (
361      line.base.0.y, line.direction.y, aabb_min.0.y, aabb_max.0.y, dir_reciprocal.y);
362    intersect_interval (interval_x, interval_y).and_then (|interval| {
363      let interval_z = line_aabb_slab_1d (
364        line.base.0.z, line.direction.z, aabb_min.0.z, aabb_max.0.z, dir_reciprocal.z);
365      intersect_interval (interval, interval_z).map (|interval|{
366        let start = line.point (interval.min());
367        let end   = line.point (interval.max());
368        ( (interval.min(), start), (interval.max(), end) )
369      })
370    })
371  }
372}
373
374pub fn intersect_ray3_aabb3 <S> (_ray : Ray3 <S>, _aabb : Aabb3 <S>)
375  -> Option <(Ray3Point <S>, Ray3Point <S>)>
376{
377  unimplemented!("TODO: ray3 aabb3 intersect")
378}
379
380/// Boolean intersection computation of a 3D line with a 3D sphere.
381///
382/// If the line and sphere intersect, the two intersection points are returned with the
383/// scalar parameter corresponding to that point along the line.
384///
385/// ```
386/// # use math_utils::*;
387/// # use math_utils::geometry::*;
388/// # use math_utils::geometry::intersect::*;
389/// let sphere = Sphere3::unit();
390/// let line = Line3::new  ([0.0, 0.0, 0.0].into(), Unit3::axis_x());
391/// assert_eq!(
392///   intersect_line3_sphere3 (line, sphere).unwrap(),
393///   ( (-1.0, [-1.0, 0.0, 0.0].into()),
394///     ( 1.0, [ 1.0, 0.0, 0.0].into())
395///   )
396/// );
397/// ```
398///
399/// Returns `None` if the line and sphere are tangent:
400///
401/// ```
402/// # use math_utils::*;
403/// # use math_utils::geometry::*;
404/// # use math_utils::geometry::intersect::*;
405/// let sphere = Sphere3::unit();
406/// let line   = Line3::new  ([0.0, 0.0, 1.0].into(), Unit3::axis_x());
407/// assert_eq!(intersect_line3_sphere3 (line, sphere), None);
408/// ```
409pub fn intersect_line3_sphere3 <S> (line : Line3 <S>, sphere : Sphere3 <S>)
410  -> Option <(Line3Point <S>, Line3Point <S>)>
411where S : OrderedField + Sqrt {
412  line_sphere_roots (line.base, *line.direction, sphere.center, sphere.radius)
413}
414
415pub fn intersect_ray3_sphere3 <S> (_ray : Ray3 <S>, _sphere : Sphere3 <S>)
416  -> Option <(Ray3Point <S>, Ray3Point <S>)>
417{
418  unimplemented!("TODO: ray3 sphere3 intersect")
419}
420
421/// Boolean intersection computation of a 2D line segment with a 2D AABB (rectangle).
422///
423/// The points are returned in the order given by the ordering of the points in the
424/// segment.
425///
426/// Note that a segment that is tangent to the AABB returns no intersection.
427pub fn intersect_segment2_aabb2 <S> (segment : Segment2 <S>, aabb : Aabb2 <S>)
428  -> Option <(Segment2Point <S>, Segment2Point <S>)>
429where S : Real {
430  let vector = segment.point_b() - segment.point_a();
431  let length = *vector.norm();
432  let line   = Line2::new (segment.point_a(), Unit2::unchecked (vector / length));
433  if let Some (((t0, _p0), (t1, _p1))) = intersect_line2_aabb2 (line, aabb) {
434    clip_line_query_to_segment (length, t0, t1, |t| line.point (t))
435  } else {
436    None
437  }
438}
439
440/// Boolean intersection computation of a 2D line segment with a 2D sphere (circle).
441///
442/// The points are returned in the order given by the ordering of the points in the
443/// segment.
444///
445/// Note that a segment that is tangent to the surface of the circle returns no
446/// intersection.
447pub fn intersect_segment2_sphere2 <S> (segment : Segment2 <S>, sphere : Sphere2 <S>)
448  -> Option <(Segment2Point <S>, Segment2Point <S>)>
449where S : Real {
450  let vector = segment.point_b() - segment.point_a();
451  let length = *vector.norm();
452  let line   = Line2::new (segment.point_a(), Unit2::unchecked (vector / length));
453  if let Some (((t0, _p0), (t1, _p1))) = intersect_line2_sphere2 (line, sphere) {
454    clip_line_query_to_segment (length, t0, t1, |t| line.point (t))
455  } else {
456    None
457  }
458}
459
460/// Boolean intersection computation of a 3D line segment with an AABB.
461///
462/// The points are returned in the order given by the ordering of the points in the
463/// segment.
464///
465/// Note that a segment that is tangent to the AABB returns no intersection.
466pub fn intersect_segment3_aabb3 <S> (segment : Segment3 <S>, aabb : Aabb3 <S>)
467  -> Option <(Segment3Point <S>, Segment3Point <S>)>
468where S : Real + num::Float + approx::RelativeEq <Epsilon=S> {
469  let vector = segment.point_b() - segment.point_a();
470  let length = *vector.norm();
471  let line = Line3::new (segment.point_a(), Unit3::unchecked_approx (vector / length));
472  if let Some (((t0, _p0), (t1, _p1))) = intersect_line3_aabb3 (line, aabb) {
473    clip_line_query_to_segment (length, t0, t1, |t| line.point (t))
474  } else {
475    None
476  }
477}
478
479/// Boolean intersection computation of a 3D line segment with a sphere.
480///
481/// The points are returned in the order given by the ordering of the points in the
482/// segment.
483///
484/// Note that a segment that is tangent to the surface of the sphere returns no
485/// intersection.
486pub fn intersect_segment3_sphere3 <S> (segment : Segment3 <S>, sphere : Sphere3 <S>)
487  -> Option <(Segment3Point <S>, Segment3Point <S>)>
488where S : OrderedField + Sqrt {
489  let two  = S::two();
490  let four = S::four();
491  // defining intersection points by the equation at^2 + bt + c = 0, solve for t using
492  // quadratic formula:
493  //
494  //  t = (-b +- sqrt (b^2 -4ac)) / 2a
495  //
496  // where a, b, and c are defined in terms of points p1, p2 of the line segment and the
497  // sphere center p3, and sphere radius r
498  let p1   = segment.point_a();
499  let p2   = segment.point_b();
500  let p3   = sphere.center;
501  let r    = *sphere.radius;
502  let p1p2 = p2 - p1;
503  let p3p1 = p1 - p3;
504  let a    = *p1p2.norm_squared();
505  let b    = two * p1p2.dot (p3p1);
506  let c    = *p3p1.norm_squared() - r * r;
507  // this is the portion of the quadratic equation inside the square root that
508  // determines whether the intersection is none, a tangent point, or a segment
509  let discriminant = b * b - four * a * c;
510  if discriminant <= S::zero() {
511    None
512  } else {
513    let discriminant_sqrt = discriminant.sqrt();
514    let frac_2a           = S::one() / (two * a);
515    let t1                = S::max ((-b - discriminant_sqrt) * frac_2a, S::zero());
516    let t2                = S::min ((-b + discriminant_sqrt) * frac_2a, S::one());
517    if t2 <= S::zero() || S::one() <= t1 {
518      None
519    } else {
520      let first  = p1 + p1p2 * t1;
521      let second = p1 + p1p2 * t2;
522      Some ((
523        (Normalized::unchecked (t1), first),
524        (Normalized::unchecked (t2), second)
525      ))
526    }
527  }
528}
529
530/// Boolean intersection computation of a 3D line segment with an axis-aligned cylinder.
531///
532/// The points are returned in the order given by the ordering of the points in the
533/// segment.
534///
535/// Note that a segment that is tangent to the surface of the cylinder returns no
536/// intersection.
537pub fn intersect_segment3_cylinder3 <S : Real> (
538  segment : Segment3 <S>, cylinder : Cylinder3 <S>
539) -> Option <(Segment3Point <S>, Segment3Point <S>)> {
540  let segment_aabb  = segment.aabb3();
541  let cylinder_aabb = cylinder.aabb3();
542  if !overlaps_aabb3_aabb3 (segment_aabb, cylinder_aabb) {
543    None
544  } else {
545    let p1       = segment.point_a();
546    let p2       = segment.point_b();
547    let p3       = cylinder.center;
548    let r        = cylinder.radius;
549    let r2       = r * r;
550    let p1p2     = p2 - p1;
551    let p1_xy    = Point2::from ([p1.0.x, p1.0.y]);
552    let p2_xy    = Point2::from ([p2.0.x, p2.0.y]);
553    let p3_xy    = Point2::from ([p3.0.x, p3.0.y]);
554    let p3_z_max = cylinder_aabb.max().0.z;
555    let p3_z_min = cylinder_aabb.min().0.z;
556    if p1_xy == p2_xy {   // segment is aligned vertically (Z axis)
557      let d2 = (p1_xy - p3_xy).norm_squared();
558      if *d2 >= *r2 {
559        None
560      } else {
561        let (t1, begin_z) = if p1.0.z >= p3_z_max {
562          ((p3_z_max - p1.0.z) / p1p2.z, p3_z_max)
563        } else if p1.0.z <= p3_z_min {
564          ((p3_z_min - p1.0.z) / p1p2.z, p3_z_min)
565        } else {
566          (S::zero(), p1.0.z)
567        };
568        let (t2, end_z)   = if p2.0.z >= p3_z_max {
569          ((p3_z_max - p1.0.z) / p1p2.z, p3_z_max)
570        } else if p2.0.z <= p3_z_min {
571          ((p3_z_min - p1.0.z) / p1p2.z, p3_z_min)
572        } else {
573          (S::one(), p2.0.z)
574        };
575        let begin = [p1_xy.0.x, p1_xy.0.y, begin_z].into();
576        let end   = [p1_xy.0.x, p1_xy.0.y, end_z  ].into();
577        Some ((
578          (Normalized::unchecked (t1), begin),
579          (Normalized::unchecked (t2), end)
580        ))
581      }
582    } else {    // segment is not aligned vertically
583      // intersect the line with the cylinder circle in the XY plane
584      let two     = S::two();
585      let four    = S::four();
586      let p1p2_xy = p1p2.xy();
587      let p3p1_xy = p1_xy - p3_xy;
588      let a       = p1p2_xy.norm_squared();
589      let b       = two * p1p2_xy.dot (p3p1_xy);
590      let c       = *p3p1_xy.norm_squared() - *r2;
591      // this is the portion of the quadratic equation inside the square root that
592      // determines whether the intersection is none, a tangent point, or a segment
593      let discriminant = b * b - four * *a * c;
594      if discriminant <= S::zero() {
595        None
596      } else {
597        let discriminant_sqrt = discriminant.sqrt();
598        let frac_2a           = S::one() / (two * *a);
599        let t1_xy             = S::max ((-b - discriminant_sqrt) * frac_2a, S::zero());
600        let t2_xy             = S::min ((-b + discriminant_sqrt) * frac_2a, S::one());
601        if t2_xy <= S::zero() || S::one() <= t1_xy {
602          None
603        } else if let Some ((t1, t2)) = if p1.0.z == p2.0.z {
604          // segment aligned horizontally
605          Some ((t1_xy, t2_xy))
606        } else {
607          // segment not aligned horizontally;
608          // intersect the line with the top and bottom of the cylinder
609          let p1p3_z_max = p3_z_max - p1.0.z;
610          let p1p3_z_min = p3_z_min - p1.0.z;
611          let t_z_max    = S::max (S::min (p1p3_z_max / p1p2.z, S::one()), S::zero());
612          let t_z_min    = S::max (S::min (p1p3_z_min / p1p2.z, S::one()), S::zero());
613          let t1_z       = S::min (t_z_max, t_z_min);
614          let t2_z       = S::max (t_z_max, t_z_min);
615          let aabb_xy    = Interval::with_minmax_unchecked (t1_xy, t2_xy);
616          let aabb_z     = Interval::with_minmax_unchecked (t1_z,  t2_z);
617          if !aabb_xy.intersects (aabb_z) {
618            None
619          } else {
620            Some ((S::max (t1_xy, t1_z), S::min (t2_xy, t2_z)))
621          }
622        } /*then*/ {
623          debug_assert!(t1 < t2);
624          debug_assert!(t1 >= S::zero());
625          debug_assert!(t1 <  S::one());
626          debug_assert!(t2 >  S::zero());
627          debug_assert!(t2 <= S::one());
628          let first  = p1 + p1p2 * t1;
629          let second = p1 + p1p2 * t2;
630          Some ((
631            (Normalized::unchecked (t1), first),
632            (Normalized::unchecked (t2), second)
633          ))
634        } else {
635          None
636        }
637      }
638    }
639  }
640}
641
642/// Boolean intersection computation of a line segment with an axis-aligned capsule.
643///
644/// The points are returned in the order given by the ordering of the points in the
645/// segment.
646///
647/// Note that a line that is tangent to the surface of the capsule returns no
648/// intersection.
649pub fn intersect_segment3_capsule3 <S : Real> (
650  segment : Segment3 <S>, capsule : Capsule3 <S>
651) -> Option <(Segment3Point <S>, Segment3Point <S>)> {
652  let segment_aabb = segment.aabb3();
653  let capsule_aabb = capsule.aabb3();
654  if !overlaps_aabb3_aabb3 (segment_aabb, capsule_aabb) {
655    None
656  } else {
657    // decompose the capsule into spheres and cylinder
658    let (upper_sphere, cylinder, lower_sphere) = capsule.decompose();
659    let cylinder_result = cylinder
660      .and_then (|cylinder| segment.intersect_cylinder (cylinder));
661    let upper_result = segment.intersect_sphere (upper_sphere);
662    let lower_result = segment.intersect_sphere (lower_sphere);
663    match (upper_result, cylinder_result, lower_result) {
664      (None, None, None) => None,
665      (one,  None, None) | (None,  one, None) | (None, None,  one) => one,
666      (Some (((t1,p1), (t2,p2))), Some (((u1,q1), (u2,q2))), None) |
667      (Some (((t1,p1), (t2,p2))), None, Some (((u1,q1), (u2,q2)))) |
668      (None, Some (((t1,p1), (t2,p2))), Some (((u1,q1), (u2,q2)))) => {
669        let first = if t1 < u1 {
670          (t1,p1)
671        } else {
672          (u1,q1)
673        };
674        let second = if t2 > u2 {
675          (t2,p2)
676        } else {
677          (u2,q2)
678        };
679        Some ((first, second))
680      }
681      ( Some (((t1,p1), (t2,p2))), Some (((u1,q1), (u2,q2))), Some (((v1,r1), (v2,r2)))
682      ) => {
683        let min1 = Normalized::min (Normalized::min (t1, u1), v1);
684        let max2 = Normalized::max (Normalized::max (t2, u2), v2);
685        let first = if min1 == t1 {
686          (t1,p1)
687        } else if min1 == u1 {
688          (u1,q1)
689        } else {
690          debug_assert_eq!(min1, v1);
691          (v1,r1)
692        };
693        let second = if max2 == t2 {
694          (t2,p2)
695        } else if max2 == u2 {
696          (u2,q2)
697        } else {
698          debug_assert_eq!(max2, v2);
699          (v2,r2)
700        };
701        Some ((first, second))
702      }
703    }
704  }
705}
706
707/// Boolean intersection computation of a line segment with a convex hull.
708///
709/// The points are returned in the order given by the ordering of the points in the
710/// segment.
711///
712/// Note that a line that is tangent to the surface of the convex hull returns no
713/// intersection.
714pub fn intersect_segment3_hull3 <S> (
715  segment : Segment3 <S>, hull : &Hull3 <S>, mesh : &mesh::VertexEdgeTriangleMesh
716) -> Option <(Segment3Point <S>, Segment3Point <S>)> where
717  S : OrderedField + Sqrt + approx::RelativeEq <Epsilon = S>
718{
719  use num::One;
720  let line = segment.affine_line();
721  let mut start = None;
722  let mut end   = None;
723  for triangle in mesh.triangles().values() {
724    let face = hull.triangle (triangle);
725    if let Some ((t, p)) = line_triangle (line, face) {
726      if let Some ((s, q)) = start {
727        if t < s {
728          end   = Some ((s, q));
729          start = Some ((t, p));
730        } else {
731          if let Some ((s, _q)) = end {
732            if t > s {
733              end = Some ((t, p));
734            }
735          } else {
736            end = Some ((t, p));
737          }
738        }
739        // if the line intersects an edge it may intersect another triangle, but the
740        // distance should be the same
741        if t != s {
742          break
743        }
744      } else {
745        start = Some ((t, p));
746      }
747    }
748  }
749  match (start, end) {
750    (Some ((t, p)), Some ((s, q))) => {
751      debug_assert!(t < s, "t: {t:?}, s: {s:?}");
752      if t < S::zero() && s > S::one() {
753        Some ((
754          (Normalized::zero(), segment.point_a()),
755          (Normalized::one(), segment.point_b())
756        ))
757      } else if t >= S::one() {
758        debug_assert!(s > S::one());
759        None
760      } else if s <= S::zero() {
761        None
762      } else {
763        let (t, p) = if t < S::zero() {
764          (Normalized::zero(), segment.point_a())
765        } else {
766          (Normalized::unchecked (t), p)
767        };
768        let (s, q) = if s > S::one() {
769          (Normalized::one(), segment.point_b())
770        } else {
771          (Normalized::unchecked (s), q)
772        };
773        Some (((t, p), (s, q)))
774      }
775    }
776    (Some(_), None) | (None, Some(_)) => unreachable!(),
777    (None, None) => None
778  }
779}
780
781/// Boolean intersection computation of a line segment with a triangle
782pub fn intersect_segment3_triangle3 <S> (
783  segment : Segment3 <S>, triangle : Triangle3 <S>
784) -> Option <Segment3Point <S>> where
785  S : OrderedField + approx::AbsDiffEq <Epsilon = S>
786{
787  line_triangle (segment.affine_line(), triangle)
788    .and_then (|(s, p)| Normalized::new (s).map (|s| (s, p)))
789}
790
791/// Overlap test of 1D axis-aligned bounding boxes (intervals).
792///
793/// AABBs that are merely touching are not counted as intersecting:
794///
795/// ```
796/// # use math_utils::geometry::*;
797/// # use math_utils::geometry::intersect::*;
798/// let a = Interval::with_minmax ( 0.0, 1.0).unwrap();
799/// let b = Interval::with_minmax (-1.0, 0.0).unwrap();
800/// assert!(!overlaps_interval (a, b));
801/// ```
802#[inline]
803pub fn overlaps_interval <S> (a : Interval <S>, b : Interval <S>) -> bool where
804  S : Copy + PartialOrd
805{
806  let (min_a, max_a) = (a.min(), a.max());
807  let (min_b, max_b) = (b.min(), b.max());
808  // intersection if intervals overlap
809  max_a > min_b && min_a < max_b
810}
811
812/// Overlap test of 2D axis-aligned bounding boxes.
813///
814/// AABBs that are merely touching are not counted as intersecting:
815///
816/// ```
817/// # use math_utils::geometry::*;
818/// # use math_utils::geometry::intersect::*;
819/// let a = Aabb2::with_minmax ([ 0.0, 0.0].into(), [1.0, 1.0].into()).unwrap();
820/// let b = Aabb2::with_minmax ([-1.0, 0.0].into(), [0.0, 1.0].into()).unwrap();
821/// assert!(!overlaps_aabb2_aabb2 (a, b));
822/// ```
823#[inline]
824pub fn overlaps_aabb2_aabb2 <S> (a : Aabb2 <S>, b : Aabb2 <S>) -> bool where
825  S : Copy + PartialOrd
826{
827  let (min_a, max_a) = (a.min(), a.max());
828  let (min_b, max_b) = (b.min(), b.max());
829  // intersection if overlap exists on both axes
830  max_a.0.x > min_b.0.x && min_a.0.x < max_b.0.x &&
831  max_a.0.y > min_b.0.y && min_a.0.y < max_b.0.y
832}
833
834/// Overlap test of 3D axis-aligned bounding boxes.
835///
836/// AABBs that are merely touching are not counted as intersecting:
837///
838/// ```
839/// # use math_utils::geometry::*;
840/// # use math_utils::geometry::intersect::*;
841/// let a = Aabb3::with_minmax ([ 0.0, 0.0, 0.0].into(), [1.0, 1.0, 1.0].into())
842///   .unwrap();
843/// let b = Aabb3::with_minmax ([-1.0, 0.0, 0.0].into(), [0.0, 1.0, 1.0].into())
844///   .unwrap();
845/// assert!(!overlaps_aabb3_aabb3 (a, b));
846/// ```
847#[inline]
848pub fn overlaps_aabb3_aabb3 <S> (a : Aabb3 <S>, b : Aabb3 <S>) -> bool where
849  S : Copy + PartialOrd
850{
851  let (min_a, max_a) = (a.min(), a.max());
852  let (min_b, max_b) = (b.min(), b.max());
853  // intersection if overlap exists on all three axes
854  max_a.0.x > min_b.0.x && min_a.0.x < max_b.0.x &&
855  max_a.0.y > min_b.0.y && min_a.0.y < max_b.0.y &&
856  max_a.0.z > min_b.0.z && min_a.0.z < max_b.0.z
857}
858
859/// Overlap test of a line segment with a triangle
860#[inline]
861pub fn overlaps_triangle3_segment3 <S> (
862  triangle : Triangle3 <S>, segment : Segment3 <S>
863) -> bool where
864  S : OrderedField + num::real::Real + approx::AbsDiffEq <Epsilon = S>
865{
866  intersect_segment3_triangle3 (segment, triangle).is_some()
867}
868
869/// Overlap test of two triangles
870pub fn overlaps_triangle3_triangle3 <S> (
871  triangle_a : Triangle3 <S>, triangle_b : Triangle3 <S>
872) -> bool where
873  S : Real + num::real::Real + approx::AbsDiffEq <Epsilon = S>
874{
875  // at least one edge must intersect in one triangle
876  for edge in triangle_a.edges() {
877    if overlaps_triangle3_segment3 (triangle_b, edge) {
878      return true
879    }
880  }
881  for edge in triangle_b.edges() {
882    if overlaps_triangle3_segment3 (triangle_a, edge) {
883      return true
884    }
885  }
886  false
887}
888
889/// Overlap test of 2D spheres.
890///
891/// Spheres that are merely touching are not counted as intersecting:
892///
893/// ```
894/// # use math_utils::num::One;
895/// # use math_utils::*;
896/// # use math_utils::geometry::*;
897/// # use math_utils::geometry::intersect::*;
898/// let r = Positive::one();
899/// let a = Sphere2 { center: [ 1.0, 0.0].into(), radius: r };
900/// let b = Sphere2 { center: [-1.0, 0.0].into(), radius: r };
901/// assert!(!overlaps_sphere2_sphere2 (a, b));
902/// ```
903#[inline]
904pub fn overlaps_sphere2_sphere2 <S : OrderedRing> (a : Sphere2 <S>, b : Sphere2 <S>)
905  -> bool
906{
907  let r_ab = a.radius + b.radius;
908  (b.center - a.center).self_dot() < (r_ab * r_ab).into()
909}
910
911/// Overlap test of 3D spheres.
912///
913/// Spheres that are merely touching are not counted as intersecting:
914///
915/// ```
916/// # use math_utils::num::One;
917/// # use math_utils::*;
918/// # use math_utils::geometry::*;
919/// # use math_utils::geometry::intersect::*;
920/// let r = Positive::one();
921/// let a = Sphere3 { center: [ 1.0, 0.0,  0.0].into(), radius: r };
922/// let b = Sphere3 { center: [-1.0, 0.0,  0.0].into(), radius: r };
923/// assert!(!overlaps_sphere3_sphere3 (a, b));
924/// ```
925#[inline]
926pub fn overlaps_sphere3_sphere3 <S : OrderedRing> (a : Sphere3 <S>, b : Sphere3 <S>)
927  -> bool
928{
929  let r_ab = a.radius + b.radius;
930  (b.center - a.center).self_dot() < (r_ab * r_ab).into()
931}
932
933/// Given an infinitely extended line defined by segment endpoints (a, b), check to see
934/// if line intersects a given triangle, returning parameterized position along the line
935/// $a + t (b - a)$.
936///
937/// ```
938/// # use math_utils::*;
939/// # use math_utils::geometry::*;
940/// # use math_utils::geometry::intersect::*;
941/// let line = frame::Line3::from (Segment3::noisy (
942///   [0.0, 0.0, -1.0].into(), [0.0, 0.0, 1.0].into()
943/// ));
944/// let triangle = Triangle3::noisy (
945///   [0.0, 1.0, 0.0].into(), [-1.0, -1.0, 0.0].into(), [ 1.0, -1.0, 0.0].into());
946/// assert_eq!(
947///   line_triangle (line, triangle).unwrap(),
948///   (0.5, [0.0, 0.0, 0.0].into()));
949/// let triangle = Triangle3::noisy (
950///   [0.0, 1.0, -2.0].into(), [-1.0, -1.0, -2.0].into(), [ 1.0, -1.0, -2.0].into());
951/// assert_eq!(
952///   line_triangle (line, triangle).unwrap(),
953///   (-0.5, [0.0, 0.0, -2.0].into()));
954/// ```
955pub fn line_triangle <S> (line : frame::Line3 <S>, triangle : Triangle3 <S>)
956  -> Option <Line3Point <S>>
957where S : OrderedField + approx::AbsDiffEq <Epsilon = S> {
958  // M\"oller-Tumbore algorithm from wikipedia
959  let r = line.basis;
960  let edge1 = triangle.point_b() - triangle.point_a();
961  let edge2 = triangle.point_c() - triangle.point_a();
962  // rejection test 1
963  let p = r.cross (edge2);
964  let a = edge1.dot (p);
965  if approx::abs_diff_eq!(a, S::zero()) {
966    return None
967  }
968  // rejection test 2
969  let f = S::one() / a;
970  let s = line.origin - triangle.point_a();
971  let u = f * s.dot (p);
972  if u < S::zero() || u > S::one() {
973    return None
974  }
975  // rejection test 3
976  let q = s.cross (edge1);
977  let v = f * r.dot (q);
978  if v < S::zero() || u + v > S::one() {
979    return None
980  }
981  // intersection
982  let t = f * edge2.dot (q);
983  Some ((t, line.origin + *r * t))
984}
985
986////////////////////////////////////////////////////////////////////////////////////////
987//  helpers
988////////////////////////////////////////////////////////////////////////////////////////
989
990
991fn clip_line_query_to_segment <S, P, F> (
992  length : S, t0 : S, t1 : S, line_get_point : F
993) -> Option <((Normalized <S>, P), (Normalized <S>, P))> where
994  S : Field + MinMax + Copy + PartialOrd + std::fmt::Debug,
995  F : Fn(S) -> P
996{
997  let interval = Interval::with_minmax_unchecked (S::zero(), length);
998  intersect_interval (interval, Interval::with_minmax_unchecked (t0, t1))
999    .map (|interval|(
1000      ( Normalized::unchecked (interval.min() / length),
1001        line_get_point (interval.min())
1002      ),
1003      ( Normalized::unchecked (interval.max() / length),
1004        line_get_point (interval.max())
1005      )
1006    ))
1007}
1008
1009fn line_aabb_slab_1d <S : OrderedRing> (
1010  line_base      : S,
1011  line_direction : S,
1012  aabb_min       : S,
1013  aabb_max       : S,
1014  dir_reciprocal : S
1015) -> Interval <S> {
1016  let (near, far) = if line_direction.is_positive() {
1017    (aabb_min, aabb_max)
1018  } else {
1019    (aabb_max, aabb_min)
1020  };
1021  Interval::with_minmax_unchecked (
1022    (near - line_base) * dir_reciprocal,
1023    (far  - line_base) * dir_reciprocal)
1024}
1025
1026/// Defining intersection points by the equation $at^2 + bt + c = 0$, solve for $t$
1027/// using quadratic formula:
1028/// $$
1029///   t = (-b +- sqrt (b^2 -4ac)) / 2a
1030/// $$
1031/// where $a$, $b$, and $c$ are defined in terms of points $p_1$, $p_2$ of the line
1032/// segment and the sphere center $p_3$, and sphere radius $r$.
1033#[expect(clippy::doc_markdown)]
1034fn line_sphere_roots <S, P, V> (
1035  line_base      : P,
1036  line_direction : V,
1037  sphere_center  : P,
1038  sphere_radius  : Positive <S>
1039) -> Option <((S, P), (S, P))> where
1040  S : OrderedField + Sqrt,
1041  P : Point <V> + std::ops::Add <V, Output=P> + Copy,
1042  V : NormedVectorSpace <S> + GroupAction <Addition, P>
1043{
1044  let two  = S::two();
1045  let four = S::four();
1046  let p1   = line_base;
1047  #[expect(clippy::no_effect_underscore_binding)]
1048  let _p2   = line_base + line_direction;
1049  let p3   = sphere_center;
1050  let r    = *sphere_radius;
1051  let p1p2 = line_direction;
1052  let p3p1 = p1 - p3;
1053  let a    = S::one();
1054  let b    = two * p1p2.dot (p3p1);
1055  let c    = *p3p1.norm_squared() - r * r;
1056  // this is the portion of the quadratic equation inside the square root that
1057  // determines whether the intersection is none, a tangent point, or a segment
1058  let discriminant = b * b - four * a * c;
1059  if discriminant <= S::zero() {
1060    None
1061  } else {
1062    let discriminant_sqrt = discriminant.sqrt();
1063    let frac_2a           = S::one() / (two * a);
1064    let t1                = (-b - discriminant_sqrt) * frac_2a;
1065    let t2                = (-b + discriminant_sqrt) * frac_2a;
1066    let first  = p1 + p1p2 * t1;
1067    let second = p1 + p1p2 * t2;
1068    Some (((t1, first), (t2, second)))
1069  }
1070}
1071
1072////////////////////////////////////////////////////////////////////////////////////////
1073//  tests
1074////////////////////////////////////////////////////////////////////////////////////////
1075
1076#[cfg(test)]
1077mod tests {
1078  extern crate test;
1079
1080  use super::*;
1081
1082  #[expect(clippy::cast_possible_truncation)]
1083  static RAY_TRIANGLE_BENCH_SEED : std::sync::LazyLock <u64> = std::sync::LazyLock::new (
1084    || std::time::SystemTime::UNIX_EPOCH.elapsed().unwrap().as_nanos() as u64);
1085
1086  #[test]
1087  fn line2_aabb2() {
1088    use std::f64::consts::SQRT_2;
1089    let aabb = Aabb2::with_minmax_unchecked ([-1.0, -1.0].into(), [1.0, 1.0].into());
1090    let line = Line2::new ([0.0, 0.0].into(), Unit2::axis_y());
1091    assert_eq!(
1092      intersect_line2_aabb2 (line, aabb).unwrap(),
1093      ((-1.0, [ 0.0, -1.0].into()), (1.0, [ 0.0, 1.0].into())));
1094    let line = Line2::new ([0.0, 0.0].into(), Unit2::axis_x());
1095    assert_eq!(
1096      intersect_line2_aabb2 (line, aabb).unwrap(),
1097      ((-1.0, [-1.0,  0.0].into()), (1.0, [ 1.0, 0.0].into())));
1098    let line = Line2::new ([0.0, 0.0].into(), Unit2::normalize ([1.0, 1.0].into()));
1099    assert_eq!(
1100      intersect_line2_aabb2 (line, aabb).unwrap(),
1101      ((-SQRT_2, [-1.0, -1.0].into()), (SQRT_2, [ 1.0,  1.0].into())));
1102    let line = Line2::new ([0.0, 0.0].into(), Unit2::normalize ([-1.0, -1.0].into()));
1103    assert_eq!(
1104      intersect_line2_aabb2 (line, aabb).unwrap(),
1105      ((-SQRT_2, [1.0,  1.0].into()), (SQRT_2, [-1.0, -1.0].into())));
1106    let line = Line2::new ([0.0, 3.0].into(), Unit2::normalize ([-1.0, -1.0].into()));
1107    assert!(intersect_line2_aabb2 (line, aabb).is_none());
1108    let line = Line2::new ([0.0, -3.0].into(), Unit2::normalize ([1.0,  1.0].into()));
1109    assert!(intersect_line2_aabb2 (line, aabb).is_none());
1110  }
1111
1112  #[test]
1113  fn line3_aabb3() {
1114    use approx::assert_ulps_eq;
1115    let aabb = Aabb3::with_minmax_unchecked (
1116      [-1.0, -1.0, -1.0].into(), [1.0, 1.0, 1.0].into());
1117    let line = Line3::new ([0.0, 0.0, 0.0].into(), Unit3::axis_z());
1118    assert_eq!(
1119      intersect_line3_aabb3 (line, aabb).unwrap(),
1120      ((-1.0, [0.0, 0.0, -1.0].into()), (1.0, [0.0, 0.0, 1.0].into())));
1121    let line = Line3::new ([0.0, 0.0, 0.0].into(), Unit3::axis_y());
1122    assert_eq!(
1123      intersect_line3_aabb3 (line, aabb).unwrap(),
1124      ((-1.0, [0.0, -1.0,  0.0].into()), (1.0, [0.0, 1.0, 0.0].into())));
1125    {
1126      let line = Line3::new (
1127        [0.0, 0.0, 0.0].into(),
1128        Unit3::normalize ([1.0, 1.0, 1.0].into()));
1129      let result = intersect_line3_aabb3 (line, aabb).unwrap();
1130      assert_ulps_eq!((result.0).0, -f64::sqrt_3());
1131      assert_ulps_eq!((result.1).0, f64::sqrt_3());
1132      assert_eq!((result.0).1, [-1.0, -1.0, -1.0].into());
1133      assert_eq!((result.1).1, [ 1.0,  1.0,  1.0].into());
1134    }
1135    {
1136      let line = Line3::new (
1137        [0.0, 0.0, 0.0].into(),
1138        Unit3::normalize ([-1.0, -1.0, -1.0].into()));
1139      let result = intersect_line3_aabb3 (line, aabb).unwrap();
1140      assert_ulps_eq!((result.0).0, -f64::sqrt_3());
1141      assert_ulps_eq!((result.1).0, f64::sqrt_3());
1142      assert_eq!((result.0).1, [ 1.0,  1.0,  1.0].into());
1143      assert_eq!((result.1).1, [-1.0, -1.0, -1.0].into());
1144    }
1145    let line = Line3::new (
1146      [0.0, 0.0, 3.0].into(),
1147      Unit3::normalize ([-1.0, -1.0, -1.0].into()));
1148    assert!(intersect_line3_aabb3 (line, aabb).is_none());
1149    let line = Line3::new (
1150      [0.0, 0.0, -3.0].into(),
1151      Unit3::normalize ([1.0, 1.0, 1.0].into()));
1152    assert!(intersect_line3_aabb3 (line, aabb).is_none());
1153  }
1154
1155  #[test]
1156  fn segment3_sphere3() {
1157    let sphere  = shape::Sphere::unit().sphere3 (Point3::origin());
1158    let segment = Segment3::noisy ([-2.0, 0.0, 0.0].into(), [2.0, 0.0, 0.0].into());
1159    assert_eq!(
1160      intersect_segment3_sphere3 (segment, sphere)
1161        .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1162      ((0.25, [-1.0, 0.0, 0.0].into()), (0.75, [1.0, 0.0, 0.0].into())));
1163    let segment = Segment3::noisy ([2.0, 0.0, 0.0].into(), [-2.0, 0.0, 0.0].into());
1164    assert_eq!(
1165      intersect_segment3_sphere3 (segment, sphere)
1166        .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1167      ((0.25, [1.0, 0.0, 0.0].into()), (0.75, [-1.0, 0.0, 0.0].into())));
1168    let segment = Segment3::noisy ([0.0, 0.0, 0.0].into(), [0.0, 0.0, 2.0].into());
1169    assert_eq!(
1170      intersect_segment3_sphere3 (segment, sphere)
1171        .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1172      ((0.0, [0.0, 0.0, 0.0].into()), (0.5, [0.0, 0.0, 1.0].into())));
1173    let segment = Segment3::noisy ([0.0, 0.0, -2.0].into(), [0.0, 0.0, 0.0].into());
1174    assert_eq!(
1175      intersect_segment3_sphere3 (segment, sphere)
1176        .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1177      ((0.5, [0.0, 0.0, -1.0].into()), (1.0, [0.0, 0.0, 0.0].into())));
1178  }
1179
1180  #[test]
1181  fn segment3_cylinder3() {
1182    let cylinder = shape::Cylinder::unit().cylinder3 (Point3::origin());
1183    let segment = Segment3::noisy ([-2.0, 0.0, 0.0].into(), [2.0, 0.0, 0.0].into());
1184    assert_eq!(
1185      intersect_segment3_cylinder3 (segment, cylinder)
1186        .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1187      ((0.25, [-1.0, 0.0, 0.0].into()), (0.75, [1.0, 0.0, 0.0].into())));
1188    let segment = Segment3::noisy ([2.0, 0.0, 0.0].into(), [-2.0, 0.0, 0.0].into());
1189    assert_eq!(
1190      intersect_segment3_cylinder3 (segment, cylinder)
1191        .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1192      ((0.25, [1.0, 0.0, 0.0].into()), (0.75, [-1.0, 0.0, 0.0].into())));
1193    let segment = Segment3::noisy ([0.0, 0.0, 0.0].into(), [0.0, 0.0, 2.0].into());
1194    assert_eq!(
1195      intersect_segment3_cylinder3 (segment, cylinder)
1196        .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1197      ((0.0, [0.0, 0.0, 0.0].into()), (0.5, [0.0, 0.0, 1.0].into())));
1198    let segment = Segment3::noisy ([0.0, 0.0, -2.0].into(), [0.0, 0.0, 0.0].into());
1199    assert_eq!(
1200      intersect_segment3_cylinder3 (segment, cylinder)
1201        .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1202      ((0.5, [0.0, 0.0, -1.0].into()), (1.0, [0.0, 0.0, 0.0].into())));
1203  }
1204
1205  #[test]
1206  fn segment3_capsule3() {
1207    let capsule = shape::Capsule::noisy (1.0, 1.0).capsule3 (Point3::origin());
1208    let segment = Segment3::noisy ([-2.0, 0.0, 0.0].into(), [2.0, 0.0, 0.0].into());
1209    assert_eq!(
1210      intersect_segment3_capsule3 (segment, capsule)
1211        .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1212      ((0.25, [-1.0, 0.0, 0.0].into()), (0.75, [1.0, 0.0, 0.0].into())));
1213    let segment = Segment3::noisy ([2.0, 0.0, 0.0].into(), [-2.0, 0.0, 0.0].into());
1214    assert_eq!(
1215      intersect_segment3_capsule3 (segment, capsule)
1216        .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1217      ((0.25, [1.0, 0.0, 0.0].into()), (0.75, [-1.0, 0.0, 0.0].into())));
1218    let segment = Segment3::noisy ([0.0, 0.0, 0.0].into(), [0.0, 0.0, 4.0].into());
1219    assert_eq!(
1220      intersect_segment3_capsule3 (segment, capsule)
1221        .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1222      ((0.0, [0.0, 0.0, 0.0].into()), (0.5, [0.0, 0.0, 2.0].into())));
1223    let segment = Segment3::noisy ([0.0, 0.0, -4.0].into(), [0.0, 0.0, 0.0].into());
1224    assert_eq!(
1225      intersect_segment3_capsule3 (segment, capsule)
1226        .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1227      ((0.5, [0.0, 0.0, -2.0].into()), (1.0, [0.0, 0.0, 0.0].into())));
1228  }
1229
1230  #[test]
1231  fn segment3_aabb3() {
1232    let aabb =
1233      Aabb3::with_minmax_unchecked ([1.0, -0.5, 0.0].into(), [2.0, 0.5, 1.0].into());
1234    let segment = Segment3::noisy ([-1.0, 0.0, 0.5].into(), [2.0, 0.0, 0.5].into());
1235    assert_eq!(
1236      intersect_segment3_aabb3 (segment, aabb)
1237        .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1238      ((2.0/3.0, [1.0, 0.0, 0.5].into()), (1.0, [2.0, 0.0, 0.5].into())));
1239  }
1240
1241  #[test]
1242  fn segment3_hull3() {
1243    let (hull, mesh) = Hull3::from_points_with_mesh (&[
1244      [  0.0,  0.0,  1.0],
1245      [ -1.0, -1.0, -1.0],
1246      [  1.0, -1.0, -1.0],
1247      [  0.0,  1.0, -1.0]
1248    ].map (Point3::from)).unwrap();
1249    let segment = Segment3::noisy ([0.0, 0.0, -2.0].into(), [0.0, 0.0, 0.0].into());
1250    assert_eq!(
1251      intersect_segment3_hull3 (segment, &hull, &mesh)
1252        .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1253      ((0.5, [0.0, 0.0, -1.0].into()), (1.0, [0.0, 0.0, 0.0].into())));
1254    let segment = Segment3::noisy ([0.0, 0.0, 0.0].into(), [0.0, 0.0, -2.0].into());
1255    assert_eq!(
1256      intersect_segment3_hull3 (segment, &hull, &mesh)
1257        .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1258      ((0.0, [0.0, 0.0, 0.0].into()), (0.5, [0.0, 0.0, -1.0].into())));
1259    let segment = Segment3::noisy ([0.0, 0.0, -0.5].into(), [0.0, 0.0, 0.5].into());
1260    assert_eq!(
1261      intersect_segment3_hull3 (segment, &hull, &mesh)
1262        .map (|((s, a), (t, b))| ((*s, a), (*t, b))).unwrap(),
1263      ((0.0, [0.0, 0.0, -0.5].into()), (1.0, [0.0, 0.0, 0.5].into())));
1264  }
1265
1266  #[test]
1267  fn line_triangle_intersect() {
1268    let triangle = Triangle3::noisy (
1269      [0.0, 1.0, 1.0].into(), [-1.0, -1.0, 1.0].into(), [ 1.0, -1.0, 1.0].into());
1270    let line = Segment3::noisy ([0.0, 0.0, -1.0].into(), [0.0, 0.0, 1.0].into());
1271    assert_eq!(
1272      line_triangle (line.into(), triangle).unwrap(),
1273      (1.0, [0.0, 0.0, 1.0].into()));
1274    let line = Segment3::noisy ([0.0, 0.0, 1.0].into(), [0.0, 0.0, 2.0].into());
1275    assert_eq!(
1276      line_triangle (line.into(), triangle).unwrap(),
1277      (0.0, [0.0, 0.0, 1.0].into()));
1278    let line = Segment3::noisy ([0.0, 0.0, 2.0].into(), [0.0, 0.0, 3.0].into());
1279    assert_eq!(
1280      line_triangle (line.into(), triangle).unwrap(),
1281      (-1.0, [0.0, 0.0, 1.0].into()));
1282    let line = Segment3::noisy ([5.0, 5.0, 1.0].into(), [5.0, 5.0, 2.0].into());
1283    assert!(line_triangle (line.into(), triangle).is_none());
1284  }
1285
1286  #[bench]
1287  fn bench_line_triangle (b : &mut test::Bencher) {
1288    use rand::SeedableRng;
1289    let aabb = Aabb3::with_minmax_unchecked (
1290      [-10.0, -10.0, -10.0].into(),
1291      [ 10.0,  10.0,  10.0].into());
1292    let mut rng = rand_xorshift::XorShiftRng::seed_from_u64 (*RAY_TRIANGLE_BENCH_SEED);
1293    let mut iter = 0;
1294    let mut num_intersections = 0;
1295    b.iter(||{
1296      let line = Segment3::noisy (
1297        aabb.rand_point (&mut rng), aabb.rand_point (&mut rng));
1298      let triangle = Triangle3::noisy (
1299        aabb.rand_point (&mut rng), aabb.rand_point (&mut rng),
1300        aabb.rand_point (&mut rng));
1301      if line_triangle (line.into(), triangle).is_some() && iter <= 1_000_000 {
1302        num_intersections += 1;
1303      }
1304      iter += 1;
1305    });
1306    let n = Ord::min (iter, 1_000_000);
1307    println!("{num_intersections}/{n} intersections");
1308  }
1309
1310} // end tests