math_utils/geometry/intersect/
mod.rs

1//! Primitive intersection routines
2
3use approx;
4
5use crate::*;
6use super::*;
7
8pub mod integer;
9
10/// Discrete intersection test of 1D axis-aligned bounding boxes (intervals).
11///
12/// AABBs that are merely touching are not counted as intersecting:
13///
14/// ```
15/// # use math_utils::geometry::*;
16/// # use math_utils::geometry::intersect::*;
17/// let a = Interval::from_points ( 0.0, 1.0);
18/// let b = Interval::from_points (-1.0, 0.0);
19/// assert!(!discrete_interval (&a, &b));
20/// ```
21#[inline]
22pub fn discrete_interval <S : Ring> (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  // intersection if intervals overlap
28  max_a > min_b && min_a < max_b
29}
30
31/// Continuous intersection test of 1D axis-aligned bounding boxes (intervals).
32///
33/// AABBs that are merely touching return no intersection:
34///
35/// ```
36/// # use math_utils::geometry::*;
37/// # use math_utils::geometry::intersect::*;
38/// let a = Interval::from_points ( 0.0, 1.0);
39/// let b = Interval::from_points (-1.0, 0.0);
40/// assert!(continuous_interval (&a, &b).is_none());
41/// ```
42#[inline]
43pub fn continuous_interval <S> (a : &Interval <S>, b : &Interval <S>)
44  -> Option <Interval <S>>
45where
46  S : Ring + std::fmt::Debug
47{
48  if discrete_interval (a, b) {
49    Some (Interval::with_minmax (
50      S::max (a.min(), b.min()),
51      S::min (a.max(), b.max())
52    ))
53  } else {
54    None
55  }
56}
57
58/// Discrete intersection test of 2D axis-aligned bounding boxes.
59///
60/// AABBs that are merely touching are not counted as intersecting:
61///
62/// ```
63/// # use math_utils::geometry::*;
64/// # use math_utils::geometry::intersect::*;
65/// let a = Aabb2::from_points ([ 0.0, 0.0].into(), [1.0, 1.0].into());
66/// let b = Aabb2::from_points ([-1.0, 0.0].into(), [0.0, 1.0].into());
67/// assert!(!discrete_aabb2_aabb2 (&a, &b));
68/// ```
69#[inline]
70pub fn discrete_aabb2_aabb2 <S : Ring> (a : &Aabb2 <S>, b : &Aabb2 <S>)
71  -> bool
72{
73  let (min_a, max_a) = (a.min(), a.max());
74  let (min_b, max_b) = (b.min(), b.max());
75  // intersection if overlap exists on both axes
76  max_a.0.x > min_b.0.x && min_a.0.x < max_b.0.x &&
77  max_a.0.y > min_b.0.y && min_a.0.y < max_b.0.y
78}
79
80/// Continuous intersection test of 2D axis-aligned bounding boxes.
81///
82/// AABBs that are merely touching return no intersection:
83///
84/// ```
85/// # use math_utils::geometry::*;
86/// # use math_utils::geometry::intersect::*;
87/// let a = Aabb2::from_points ([ 0.0, 0.0].into(), [1.0, 1.0].into());
88/// let b = Aabb2::from_points ([-1.0, 0.0].into(), [0.0, 1.0].into());
89/// assert!(continuous_aabb2_aabb2 (&a, &b).is_none());
90/// ```
91#[inline]
92pub fn continuous_aabb2_aabb2 <S> (a : &Aabb2 <S>, b : &Aabb2 <S>)
93  -> Option <Aabb2 <S>>
94where
95  S : Ring + std::fmt::Debug
96{
97  if discrete_aabb2_aabb2 (a, b) {
98    Some (Aabb2::with_minmax (
99      point2_max (a.min(), b.min()),
100      point2_min (a.max(), b.max())
101    ))
102  } else {
103    None
104  }
105}
106
107/// Discrete intersection test of 3D axis-aligned bounding boxes.
108///
109/// AABBs that are merely touching are not counted as intersecting:
110///
111/// ```
112/// # use math_utils::geometry::*;
113/// # use math_utils::geometry::intersect::*;
114/// let a = Aabb3::from_points ([ 0.0, 0.0, 0.0].into(), [1.0, 1.0, 1.0].into());
115/// let b = Aabb3::from_points ([-1.0, 0.0, 0.0].into(), [0.0, 1.0, 1.0].into());
116/// assert!(!discrete_aabb3_aabb3 (&a, &b));
117/// ```
118#[inline]
119pub fn discrete_aabb3_aabb3 <S : Ring> (a : &Aabb3 <S>, b : &Aabb3 <S>)
120  -> bool
121{
122  let (min_a, max_a) = (a.min(), a.max());
123  let (min_b, max_b) = (b.min(), b.max());
124  // intersection if overlap exists on all three axes
125  max_a.0.x > min_b.0.x && min_a.0.x < max_b.0.x &&
126  max_a.0.y > min_b.0.y && min_a.0.y < max_b.0.y &&
127  max_a.0.z > min_b.0.z && min_a.0.z < max_b.0.z
128}
129
130/// Continuous intersection test of 3D axis-aligned bounding boxes.
131///
132/// AABBs that are merely touching return no intersection:
133///
134/// ```
135/// # use math_utils::geometry::*;
136/// # use math_utils::geometry::intersect::*;
137/// let a = Aabb3::from_points ([ 0.0, 0.0, 0.0].into(), [1.0, 1.0, 1.0].into());
138/// let b = Aabb3::from_points ([-1.0, 0.0, 0.0].into(), [0.0, 1.0, 1.0].into());
139/// assert!(continuous_aabb3_aabb3 (&a, &b).is_none());
140/// ```
141#[inline]
142pub fn continuous_aabb3_aabb3 <S> (a : &Aabb3 <S>, b : &Aabb3 <S>)
143  -> Option <Aabb3 <S>>
144where
145  S : Ring + std::fmt::Debug
146{
147  if discrete_aabb3_aabb3 (a, b) {
148    Some (Aabb3::with_minmax (
149      point3_max (a.min(), b.min()),
150      point3_min (a.max(), b.max())
151    ))
152  } else {
153    None
154  }
155}
156
157/// Compute the intersection of a 2D line with a 2D AABB (rectangle).
158///
159/// If the line and AABB intersect, the two intersection points are returned
160/// with the scalar parameter corresponding to that point along the line.
161///
162/// ```
163/// # use math_utils::*;
164/// # use math_utils::geometry::*;
165/// # use math_utils::geometry::intersect::*;
166/// let aabb = Aabb2::with_minmax ([-1.0, -1.0].into(), [1.0, 1.0].into());
167/// let line = Line2::new  ([0.0, 0.0].into(), Unit2::axis_x());
168/// assert_eq!(
169///   continuous_line2_aabb2 (&line, &aabb).unwrap(),
170///   ( (-1.0, [-1.0, 0.0].into()),
171///     ( 1.0, [ 1.0, 0.0].into())
172///   )
173/// );
174/// ```
175///
176/// Returns `None` if the line and AABB are tangent:
177///
178/// ```
179/// # use math_utils::*;
180/// # use math_utils::geometry::*;
181/// # use math_utils::geometry::intersect::*;
182/// let aabb = Aabb2::with_minmax ([-1.0, -1.0].into(), [1.0, 1.0].into());
183/// let line = Line2::new  ([0.0, 1.0].into(), Unit2::axis_x());
184/// assert_eq!(continuous_line2_aabb2 (&line, &aabb), None);
185/// ```
186pub fn continuous_line2_aabb2 <S> (line : &Line2 <S>, aabb : &Aabb2 <S>)
187  -> Option <((S, Point2 <S>), (S, Point2 <S>))>
188where
189  S : Real + std::fmt::Debug
190{
191  let aabb_min = aabb.min();
192  let aabb_max = aabb.max();
193  if line.direction.x == S::zero() {
194    // parallel x-axis
195    if aabb_min.0.x < line.base.0.x && line.base.0.x < aabb_max.0.x {
196      let out = if line.direction.y > S::zero() {
197        let (t0, t1) = (aabb_min.0.y - line.base.0.y, aabb_max.0.y - line.base.0.y);
198        ( (t0, [line.base.0.x, aabb_min.0.y].into()),
199          (t1, [line.base.0.x, aabb_max.0.y].into())
200        )
201      } else {
202        let (t0, t1) = (line.base.0.y - aabb_max.0.y, line.base.0.y - aabb_min.0.y);
203        ( (t0, [line.base.0.x, aabb_max.0.y].into()),
204          (t1, [line.base.0.x, aabb_min.0.y].into())
205        )
206      };
207      Some (out)
208    } else {
209      None
210    }
211  } else if line.direction.y == S::zero() {
212    // parallel y-axis
213    if aabb_min.0.y < line.base.0.y && line.base.0.y < aabb_max.0.y {
214      let out = if line.direction.x > S::zero() {
215        let (t0, t1) = (aabb_min.0.x - line.base.0.x, aabb_max.0.x - line.base.0.x);
216        ( (t0, [aabb_min.0.x, line.base.0.y].into()),
217          (t1, [aabb_max.0.x, line.base.0.y].into())
218        )
219      } else {
220        let (t0, t1) = (line.base.0.x - aabb_max.0.x, line.base.0.x - aabb_min.0.x);
221        ( (t0, [aabb_max.0.x, line.base.0.y].into()),
222          (t1, [aabb_min.0.x, line.base.0.y].into())
223        )
224      };
225      Some (out)
226    } else {
227      None
228    }
229  } else {
230    let dir_reciprocal = line.direction.map (|s| S::one() / s);
231    let (t0_x, t1_x)   = {
232      let (near_x, far_x) = if line.direction.x.is_positive() {
233        (aabb_min.0.x, aabb_max.0.x)
234      } else {
235        (aabb_max.0.x, aabb_min.0.x)
236      };
237      ( (near_x - line.base.0.x) * dir_reciprocal.x,
238        (far_x  - line.base.0.x) * dir_reciprocal.x
239      )
240    };
241    let (t0_y, t1_y) = {
242      let (near_y, far_y) = if line.direction.y.is_positive() {
243        (aabb_min.0.y, aabb_max.0.y)
244      } else {
245        (aabb_max.0.y, aabb_min.0.y)
246      };
247      ( (near_y - line.base.0.y) * dir_reciprocal.y,
248        (far_y  - line.base.0.y) * dir_reciprocal.y
249      )
250    };
251    let interval_x = Interval::with_minmax (t0_x, t1_x);
252    let interval_y = Interval::with_minmax (t0_y, t1_y);
253    continuous_interval (&interval_x, &interval_y).map (|interval|{
254      let start = line.point (interval.min());
255      let end   = line.point (interval.max());
256      ( (interval.min(), start), (interval.max(), end) )
257    })
258  }
259}
260
261/// Compute the intersection of a 2D line with a 2D sphere (circle).
262///
263/// If the line and circle intersect, the two intersection points are returned
264/// with the scalar parameter corresponding to that point along the line.
265///
266/// ```
267/// # use math_utils::*;
268/// # use math_utils::geometry::*;
269/// # use math_utils::geometry::intersect::*;
270/// let sphere = Sphere2::unit();
271/// let line   = Line2::new  ([0.0, 0.0].into(), Unit2::axis_x());
272/// assert_eq!(
273///   continuous_line2_sphere2 (&line, &sphere).unwrap(),
274///   ( (-1.0, [-1.0, 0.0].into()),
275///     ( 1.0, [ 1.0, 0.0].into())
276///   )
277/// );
278/// ```
279///
280/// Returns `None` if the line and circle are tangent:
281///
282/// ```
283/// # use math_utils::*;
284/// # use math_utils::geometry::*;
285/// # use math_utils::geometry::intersect::*;
286/// let sphere = Sphere2::unit();
287/// let line   = Line2::new  ([0.0, 1.0].into(), Unit2::axis_x());
288/// assert_eq!(continuous_line2_sphere2 (&line, &sphere), None);
289/// ```
290pub fn continuous_line2_sphere2 <S : Real> (
291  line : &Line2 <S>, sphere : &Sphere2 <S>
292) -> Option <((S, Point2 <S>), (S, Point2 <S>))> {
293  // intersect the line with the cylinder circle in the XY plane
294  let two  = S::two();
295  let four = S::four();
296  // defining intersection points by the equation at^2 + bt + c = 0,
297  // solve for t using quadratic formula:
298  //
299  //  t = (-b +- sqrt (b^2 -4ac)) / 2a
300  //
301  // where a, b, and c are defined in terms of points p1, p2 of the line
302  // segment and the sphere center p3, and sphere radius r
303  let p1   = line.base;
304  let _p2   = line.base + *line.direction;
305  let p3   = sphere.center;
306  let r    = *sphere.radius;
307  let p1p2 = &line.direction;
308  let p3p1 = p1 - p3;
309  let a    = S::one();
310  let b    = two * p1p2.dot (p3p1);
311  let c    = p3p1.norm_squared() - r * r;
312  // this is the portion of the quadratic equation inside the square root
313  // that determines whether the intersection is none, a tangent point, or
314  // a segment
315  let discriminant = b * b - four * a * c;
316  if discriminant <= S::zero() {
317    None
318  } else {
319    let discriminant_sqrt = discriminant.sqrt();
320    let frac_2a           = S::one() / (two * a);
321    let t1                = (-b - discriminant_sqrt) * frac_2a;
322    let t2                = (-b + discriminant_sqrt) * frac_2a;
323    let first  = p1 + (**p1p2) * t1;
324    let second = p1 + (**p1p2) * t2;
325    Some (((t1, first), (t2, second)))
326  }
327}
328
329/// Compute the continuous intersection of a 3D line with a 3D plane.
330///
331/// Returns `None` if the line and plane are parallel:
332///
333/// ```
334/// # use math_utils::*;
335/// # use math_utils::geometry::*;
336/// # use math_utils::geometry::intersect::*;
337/// let plane = Plane3::new ([0.0, 0.0,  0.0].into(), Unit3::axis_z());
338/// let line  = Line3::new  ([0.0, 0.0,  0.0].into(),
339///   Unit3::normalize ([1.0, 1.0, 0.0].into()));
340/// assert_eq!(continuous_line3_plane3 (&line, &plane), None);
341/// ```
342pub fn continuous_line3_plane3 <S> (line : &Line3 <S>, plane : &Plane3 <S>)
343  -> Option <(S, Point3 <S>)>
344where
345  S : Real + approx::RelativeEq
346{
347  let normal_dot_direction = plane.normal.dot (*line.direction);
348  if approx::relative_eq!(normal_dot_direction, S::zero()) {
349    None
350  } else {
351    let plane_to_line = line.base - plane.base;
352    let t     = -plane.normal.dot (plane_to_line) / normal_dot_direction;
353    let point = line.point (t);
354    Some ((t, point))
355  }
356}
357
358/// Compute the intersection of a 3D line with a 3D AABB.
359///
360/// If the line and AABB intersect, the two intersection points are returned
361/// with the scalar parameter corresponding to that point along the line.
362///
363/// ```
364/// # use math_utils::*;
365/// # use math_utils::geometry::*;
366/// # use math_utils::geometry::intersect::*;
367/// let aabb = Aabb3::with_minmax (
368///   [-1.0, -1.0, -1.0].into(), [1.0, 1.0, 1.0].into());
369/// let line = Line3::new  ([0.0, 0.0, 0.0].into(), Unit3::axis_x());
370/// assert_eq!(
371///   continuous_line3_aabb3 (&line, &aabb).unwrap(),
372///   ( (-1.0, [-1.0, 0.0, 0.0].into()),
373///     ( 1.0, [ 1.0, 0.0, 0.0].into())
374///   )
375/// );
376/// ```
377///
378/// Returns `None` if the line and AABB are tangent:
379///
380/// ```
381/// # use math_utils::*;
382/// # use math_utils::geometry::*;
383/// # use math_utils::geometry::intersect::*;
384/// let aabb = Aabb3::with_minmax (
385///   [-1.0, -1.0, -1.0].into(), [1.0, 1.0, 1.0].into());
386/// let line = Line3::new  ([0.0, 1.0, 0.0].into(), Unit3::axis_x());
387/// assert_eq!(continuous_line3_aabb3 (&line, &aabb), None);
388/// ```
389pub fn continuous_line3_aabb3 <S> (line : &Line3 <S>, aabb : &Aabb3 <S>)
390  -> Option <((S, Point3 <S>), (S, Point3 <S>))>
391where
392  S : Real + num_traits::Float + approx::RelativeEq <Epsilon=S> + std::fmt::Debug
393{
394  let aabb_min = aabb.min();
395  let aabb_max = aabb.max();
396  if line.direction.x == S::zero() {
397    if aabb_min.0.x < line.base.0.x && line.base.0.x < aabb_max.0.x {
398      let line2 = Line2::new ([line.base.0.y, line.base.0.z].into(),
399        Unit2::unchecked_approx ([line.direction.y, line.direction.z].into()));
400      let aabb2 = Aabb2::with_minmax (
401        [aabb_min.0.y, aabb_min.0.z].into(),
402        [aabb_max.0.y, aabb_max.0.z].into());
403      continuous_line2_aabb2 (&line2, &aabb2).map (|((t0, p0), (t1, p1))|
404        ( (t0, [line.base.0.x, p0.0.x, p0.0.y].into()),
405          (t1, [line.base.0.x, p1.0.x, p1.0.y].into())
406        )
407      )
408    } else {
409      None
410    }
411  } else if line.direction.y == S::zero() {
412    if aabb_min.0.y < line.base.0.y && line.base.0.y < aabb_max.0.y {
413      let line2 = Line2::new ([line.base.0.x, line.base.0.z].into(),
414        Unit2::unchecked_approx ([line.direction.x, line.direction.z].into()));
415      let aabb2 = Aabb2::with_minmax (
416        [aabb_min.0.x, aabb_min.0.z].into(),
417        [aabb_max.0.x, aabb_max.0.z].into());
418      continuous_line2_aabb2 (&line2, &aabb2).map (|((t0, p0), (t1, p1))|
419        ( (t0, [p0.0.x, line.base.0.y, p0.0.y].into()),
420          (t1, [p1.0.x, line.base.0.y, p1.0.y].into())
421        )
422      )
423    } else {
424      None
425    }
426  } else if line.direction.z == S::zero() {
427    if aabb_min.0.z < line.base.0.z && line.base.0.z < aabb_max.0.z {
428      let line2 = Line2::new ([line.base.0.x, line.base.0.y].into(),
429        Unit2::unchecked_approx ([line.direction.x, line.direction.y].into()));
430      let aabb2 = Aabb2::with_minmax (
431        [aabb_min.0.x, aabb_min.0.y].into(),
432        [aabb_max.0.x, aabb_max.0.y].into());
433      continuous_line2_aabb2 (&line2, &aabb2).map (|((t0, p0), (t1, p1))|
434        ( (t0, [p0.0.x, p0.0.y, line.base.0.z].into()),
435          (t1, [p1.0.x, p1.0.y, line.base.0.z].into())
436        )
437      )
438    } else {
439      None
440    }
441  } else {
442    let dir_reciprocal = line.direction.map (|s| S::one() / s);
443    let (t0_x, t1_x)   = {
444      let (near_x, far_x) = if line.direction.x.is_positive() {
445        (aabb_min.0.x, aabb_max.0.x)
446      } else {
447        (aabb_max.0.x, aabb_min.0.x)
448      };
449      ( (near_x - line.base.0.x) * dir_reciprocal.x,
450        (far_x  - line.base.0.x) * dir_reciprocal.x
451      )
452    };
453    let (t0_y, t1_y) = {
454      let (near_y, far_y) = if line.direction.y.is_positive() {
455        (aabb_min.0.y, aabb_max.0.y)
456      } else {
457        (aabb_max.0.y, aabb_min.0.y)
458      };
459      ( (near_y - line.base.0.y) * dir_reciprocal.y,
460        (far_y  - line.base.0.y) * dir_reciprocal.y
461      )
462    };
463    let (t0_z, t1_z) = {
464      let (near_z, far_z) = if line.direction.z.is_positive() {
465        (aabb_min.0.z, aabb_max.0.z)
466      } else {
467        (aabb_max.0.z, aabb_min.0.z)
468      };
469      ( (near_z - line.base.0.z) * dir_reciprocal.z,
470        (far_z  - line.base.0.z) * dir_reciprocal.z
471      )
472    };
473    let interval_x = Interval::with_minmax (t0_x, t1_x);
474    let interval_y = Interval::with_minmax (t0_y, t1_y);
475    continuous_interval (&interval_x, &interval_y).and_then (|interval| {
476      let interval_z = Interval::with_minmax (t0_z, t1_z);
477      continuous_interval (&interval, &interval_z).map (|interval|{
478        let start = line.point (interval.min());
479        let end   = line.point (interval.max());
480        ( (interval.min(), start), (interval.max(), end) )
481      })
482    })
483  }
484}
485
486/// Compute the continuous intersection of a 3D line with a 3D sphere.
487///
488/// If the line and sphere intersect, the two intersection points are returned
489/// with the scalar parameter corresponding to that point along the line.
490///
491/// ```
492/// # use math_utils::*;
493/// # use math_utils::geometry::*;
494/// # use math_utils::geometry::intersect::*;
495/// let sphere = Sphere3::unit();
496/// let line   = Line3::new  ([0.0, 0.0, 0.0].into(), Unit3::axis_x());
497/// assert_eq!(
498///   continuous_line3_sphere3 (&line, &sphere).unwrap(),
499///   ( (-1.0, [-1.0, 0.0, 0.0].into()),
500///     ( 1.0, [ 1.0, 0.0, 0.0].into())
501///   )
502/// );
503/// ```
504///
505/// Returns `None` if the line and sphere are tangent:
506///
507/// ```
508/// # use math_utils::*;
509/// # use math_utils::geometry::*;
510/// # use math_utils::geometry::intersect::*;
511/// let sphere = Sphere3::unit();
512/// let line   = Line3::new  ([0.0, 0.0, 1.0].into(), Unit3::axis_x());
513/// assert_eq!(continuous_line3_sphere3 (&line, &sphere), None);
514/// ```
515pub fn continuous_line3_sphere3 <S : Real> (
516  line : &Line3 <S>, sphere : &Sphere3 <S>
517) -> Option <((S, Point3 <S>), (S, Point3 <S>))> {
518  let two  = S::two();
519  let four = S::four();
520  // defining intersection points by the equation at^2 + bt + c = 0,
521  // solve for t using quadratic formula:
522  //
523  //  t = (-b +- sqrt (b^2 -4ac)) / 2a
524  //
525  // where a, b, and c are defined in terms of points p1, p2 of the line
526  // segment and the sphere center p3, and sphere radius r
527  let p1   = line.base;
528  let _p2   = line.base + *line.direction;
529  let p3   = sphere.center;
530  let r    = *sphere.radius;
531  let p1p2 = &line.direction;
532  let p3p1 = p1 - p3;
533  let a    = S::one();
534  let b    = two * p1p2.dot (p3p1);
535  let c    = p3p1.norm_squared() - r * r;
536  // this is the portion of the quadratic equation inside the square root
537  // that determines whether the intersection is none, a tangent point, or
538  // a segment
539  let discriminant = b * b - four * a * c;
540  if discriminant <= S::zero() {
541    None
542  } else {
543    let discriminant_sqrt = discriminant.sqrt();
544    let frac_2a           = S::one() / (two * a);
545    let t1                = (-b - discriminant_sqrt) * frac_2a;
546    let t2                = (-b + discriminant_sqrt) * frac_2a;
547    let first  = p1 + (**p1p2) * t1;
548    let second = p1 + (**p1p2) * t2;
549    Some (((t1, first), (t2, second)))
550  }
551}
552
553/// Compute continuous intersection of a 2D line segment with a 2D AABB
554/// (rectangle).
555///
556/// The points are returned in the order given by the ordering of the points
557/// in the segment.
558///
559/// Note that a segment that is tangent to the AABB returns no intersection.
560pub fn continuous_segment2_aabb2 <S> (
561  segment : &Segment2 <S>, aabb : &Aabb2 <S>
562) -> Option <((S, Point2 <S>), (S, Point2 <S>))> where
563  S : Real + std::fmt::Debug
564{
565  let vector = *segment.point_b() - segment.point_a();
566  let length = vector.norm();
567  let line   = Line2::new (
568    *segment.point_a(), Unit2::unchecked (vector / length));
569  if let Some (((t0, _p0), (t1, _p1))) = continuous_line2_aabb2 (&line, aabb) {
570    let interval = Interval::with_minmax (S::zero(), length);
571    continuous_interval (&interval, &Interval::with_minmax (t0, t1)).map (
572      |interval|
573      ( (interval.min() / length, line.point (interval.min())),
574        (interval.max() / length, line.point (interval.max()))
575      )
576    )
577  } else {
578    None
579  }
580}
581
582/// Compute continuous intersection of a 2D line segment with a 2D sphere
583/// (circle).
584///
585/// The points are returned in the order given by the ordering of the points
586/// in the segment.
587///
588/// Note that a segment that is tangent to the surface of the circle returns no
589/// intersection.
590pub fn continuous_segment2_sphere2 <S> (
591  segment : &Segment2 <S>, sphere : &Sphere2 <S>
592) -> Option <((S, Point2 <S>), (S, Point2<S>))> where
593  S : Real + std::fmt::Debug
594{
595  let vector = *segment.point_b() - segment.point_a();
596  let length = vector.norm();
597  let line   = Line2::new (
598    *segment.point_a(), Unit2::unchecked (vector / length));
599  if let Some (((t0, _p0), (t1, _p1))) = continuous_line2_sphere2 (&line, sphere) {
600    let interval = Interval::with_minmax (S::zero(), length);
601    continuous_interval (&interval, &Interval::with_minmax (t0, t1)).map (
602      |interval|
603      ( (interval.min() / length, line.point (interval.min())),
604        (interval.max() / length, line.point (interval.max()))
605      )
606    )
607  } else {
608    None
609  }
610}
611
612/// Compute continuous intersection of a 3D line segment with an AABB.
613///
614/// The points are returned in the order given by the ordering of the points
615/// in the segment.
616///
617/// Note that a segment that is tangent to the AABB returns no intersection.
618pub fn continuous_segment3_aabb3 <S> (
619  segment : &Segment3 <S>, aabb : &Aabb3 <S>
620) -> Option <((S, Point3 <S>), (S, Point3<S>))> where
621  S : Real + num_traits::Float + approx::RelativeEq <Epsilon=S> + std::fmt::Debug
622{
623  let vector = *segment.point_b() - segment.point_a();
624  let length = vector.norm();
625  let line   = Line3::new (
626    *segment.point_a(), Unit3::unchecked_approx (vector / length));
627  if let Some (((t0, _p0), (t1, _p1))) = continuous_line3_aabb3 (&line, aabb) {
628    let interval = Interval::with_minmax (S::zero(), length);
629    continuous_interval (&interval, &Interval::with_minmax (t0, t1)).map (
630      |interval|
631      ( (interval.min() / length, line.point (interval.min())),
632        (interval.max() / length, line.point (interval.max()))
633      )
634    )
635  } else {
636    None
637  }
638}
639
640/// Compute continuous intersection of a 3D line segment with a sphere.
641///
642/// The points are returned in the order given by the ordering of the points
643/// in the segment.
644///
645/// Note that a segment that is tangent to the surface of the sphere returns no
646/// intersection.
647pub fn continuous_segment3_sphere3 <S> (
648  segment : &Segment3 <S>, sphere : &Sphere3 <S>
649) -> Option <((S, Point3 <S>), (S, Point3<S>))> where
650  S : Field + Sqrt
651{
652  let two  = S::two();
653  let four = S::four();
654  // defining intersection points by the equation at^2 + bt + c = 0,
655  // solve for t using quadratic formula:
656  //
657  //  t = (-b +- sqrt (b^2 -4ac)) / 2a
658  //
659  // where a, b, and c are defined in terms of points p1, p2 of the line
660  // segment and the sphere center p3, and sphere radius r
661  let p1   = segment.point_a();
662  let p2   = segment.point_b();
663  let p3   = sphere.center;
664  let r    = *sphere.radius;
665  let p1p2 = *p2-p1;
666  let p3p1 = *p1-p3;
667  let a    = p1p2.norm_squared();
668  let b    = two * p1p2.dot (p3p1);
669  let c    = p3p1.norm_squared() - r * r;
670  // this is the portion of the quadratic equation inside the square root
671  // that determines whether the intersection is none, a tangent point, or
672  // a segment
673  let discriminant = b * b - four * a * c;
674  if discriminant <= S::zero() {
675    None
676  } else {
677    let discriminant_sqrt = discriminant.sqrt();
678    let frac_2a        = S::one() / (two * a);
679    let t1             = S::max (
680      (-b - discriminant_sqrt) * frac_2a,
681      S::zero());
682    let t2             = S::min (
683      (-b + discriminant_sqrt) * frac_2a,
684      S::one());
685    if t2 <= S::zero() || S::one() <= t1 {
686      None
687    } else {
688      let first  = *p1 + p1p2 * t1;
689      let second = *p1 + p1p2 * t2;
690      Some (((t1, first), (t2, second)))
691    }
692  }
693}
694
695/// Compute continuous intersection of a 3D line segment with an axis-aligned
696/// cylinder.
697///
698/// The points are returned in the order given by the ordering of the points
699/// in the segment.
700///
701/// Note that a segment that is tangent to the surface of the cylinder returns
702/// no intersection.
703pub fn continuous_segment3_cylinder3 <S> (
704  segment : &Segment3 <S>, cylinder : &Cylinder3 <S>
705) -> Option <((S, Point3 <S>), (S, Point3<S>))> where
706  S : Real + std::fmt::Debug
707{
708  let segment_aabb  = segment.aabb3();
709  let cylinder_aabb = cylinder.aabb3();
710  if !discrete_aabb3_aabb3 (&segment_aabb, &cylinder_aabb) {
711    None
712  } else {
713    let p1       = segment.point_a();
714    let p2       = segment.point_b();
715    let p3       = cylinder.center;
716    let r        = *cylinder.radius;
717    let r2       = r * r;
718    let p1p2     = *p2 - p1;
719    let p1_xy    = Point2::from ([p1.0.x, p1.0.y]);
720    let p2_xy    = Point2::from ([p2.0.x, p2.0.y]);
721    let p3_xy    = Point2::from ([p3.0.x, p3.0.y]);
722    let p3_z_max = cylinder_aabb.max().0.z;
723    let p3_z_min = cylinder_aabb.min().0.z;
724    if p1_xy == p2_xy {   // segment is aligned vertically (Z axis)
725      let d2 = (p1_xy - p3_xy).norm_squared();
726      if d2 >= r2 {
727        None
728      } else {
729        let (t1, begin_z) = if p1.0.z >= p3_z_max {
730          ((p3_z_max - p1.0.z) / p1p2.z, p3_z_max)
731        } else if p1.0.z <= p3_z_min {
732          ((p3_z_min - p1.0.z) / p1p2.z, p3_z_min)
733        } else {
734          (S::zero(), p1.0.z)
735        };
736        let (t2, end_z)   = if p2.0.z >= p3_z_max {
737          ((p3_z_max - p1.0.z) / p1p2.z, p3_z_max)
738        } else if p2.0.z <= p3_z_min {
739          ((p3_z_min - p1.0.z) / p1p2.z, p3_z_min)
740        } else {
741          (S::one(), p2.0.z)
742        };
743        let begin = [p1_xy.0.x, p1_xy.0.y, begin_z].into();
744        let end   = [p1_xy.0.x, p1_xy.0.y, end_z  ].into();
745        Some (((t1, begin), (t2, end)))
746      }
747    } else {    // segment is not aligned vertically
748      // intersect the line with the cylinder circle in the XY plane
749      let two     = S::two();
750      let four    = S::four();
751      let p1p2_xy = p1p2.xy();
752      let p3p1_xy = p1_xy - p3_xy;
753      let a       = p1p2_xy.norm_squared();
754      let b       = two * p1p2_xy.dot (p3p1_xy);
755      let c       = p3p1_xy.norm_squared() - r * r;
756      // this is the portion of the quadratic equation inside the square root
757      // that determines whether the intersection is none, a tangent point, or
758      // a segment
759      let discriminant = b * b - four * a * c;
760      if discriminant <= S::zero() {
761        None
762      } else {
763        let discriminant_sqrt = discriminant.sqrt();
764        let frac_2a           = S::one() / (two * a);
765        let t1_xy             = S::max (
766          (-b - discriminant_sqrt) * frac_2a,
767          S::zero());
768        let t2_xy             = S::min (
769          (-b + discriminant_sqrt) * frac_2a,
770          S::one());
771        if t2_xy <= S::zero() || S::one() <= t1_xy {
772          None
773        } else {
774          if let Some ((t1, t2)) = if p1.0.z == p2.0.z { // segment aligned horizontally
775            Some ((t1_xy, t2_xy))
776          } else {                         // segment not aligned horizontally
777            // intersect the line with the top and bottom of the cylinder
778            let p1p3_z_max = p3_z_max - p1.0.z;
779            let p1p3_z_min = p3_z_min - p1.0.z;
780            let t_z_max    = S::max (
781              S::min (p1p3_z_max / p1p2.z, S::one()),
782              S::zero());
783            let t_z_min    = S::max (
784              S::min (p1p3_z_min / p1p2.z, S::one()),
785              S::zero());
786            let t1_z       = S::min (t_z_max, t_z_min);
787            let t2_z       = S::max (t_z_max, t_z_min);
788            let aabb_xy    = Interval::with_minmax (t1_xy, t2_xy);
789            let aabb_z     = Interval::with_minmax ( t1_z,  t2_z);
790            if !aabb_xy.intersects (&aabb_z) {
791              None
792            } else {
793              Some ((S::max (t1_xy, t1_z), S::min (t2_xy, t2_z)))
794            }
795          } /*then*/ {
796            debug_assert!(t1 < t2);
797            debug_assert!(t1 >= S::zero());
798            debug_assert!(t1 <  S::one());
799            debug_assert!(t2 >  S::zero());
800            debug_assert!(t2 <= S::one());
801            let first  = *p1 + p1p2 * t1;
802            let second = *p1 + p1p2 * t2;
803            Some (((t1, first), (t2, second)))
804          } else {
805            None
806          }
807        }
808      }
809    }
810  }
811}
812
813/// Compute continuous intersection of a line segment with an axis-aligned
814/// capsule.
815///
816/// The points are returned in the order given by the ordering of the points
817/// in the segment.
818///
819/// Note that a line that is tangent to the surface of the capsule returns no
820/// intersection.
821pub fn continuous_segment3_capsule3 <S> (
822  segment : &Segment3 <S>, capsule : &Capsule3 <S>
823) -> Option <((S, Point3 <S>), (S, Point3 <S>))> where
824  S : Real + std::fmt::Debug
825{
826  let segment_aabb = segment.aabb3();
827  let capsule_aabb = capsule.aabb3();
828  if !discrete_aabb3_aabb3 (&segment_aabb, &capsule_aabb) {
829    None
830  } else {
831    // decompose the capsule into spheres and cylinder
832    let (upper_sphere, cylinder, lower_sphere) = capsule.decompose();
833    let cylinder_result = cylinder.and_then (
834      |cylinder| segment.intersect_cylinder (&cylinder));
835    let upper_result    = segment.intersect_sphere   (&upper_sphere);
836    let lower_result    = segment.intersect_sphere   (&lower_sphere);
837    match (upper_result, cylinder_result, lower_result) {
838      (None, None, None) => None,
839      (one,  None, None) |
840      (None,  one, None) |
841      (None, None,  one) => one,
842      (Some (((t1,p1), (t2,p2))), Some (((u1,q1), (u2,q2))), None) |
843      (Some (((t1,p1), (t2,p2))), None, Some (((u1,q1), (u2,q2)))) |
844      (None, Some (((t1,p1), (t2,p2))), Some (((u1,q1), (u2,q2)))) => {
845        let first = if t1 < u1 {
846          (t1,p1)
847        } else {
848          (u1,q1)
849        };
850        let second = if t2 > u2 {
851          (t2,p2)
852        } else {
853          (u2,q2)
854        };
855        Some ((first, second))
856      }
857      ( Some (((t1,p1), (t2,p2))),
858        Some (((u1,q1), (u2,q2))),
859        Some (((v1,r1), (v2,r2)))
860      ) => {
861        let min1 = S::min (S::min (t1, u1), v1);
862        let max2 = S::max (S::max (t2, u2), v2);
863        let first = if min1 == t1 {
864          (t1,p1)
865        } else if min1 == u1 {
866          (u1,q1)
867        } else {
868          debug_assert_eq!(min1, v1);
869          (v1,r1)
870        };
871        let second = if max2 == t2 {
872          (t2,p2)
873        } else if max2 == u2 {
874          (u2,q2)
875        } else {
876          debug_assert_eq!(max2, v2);
877          (v2,r2)
878        };
879        Some ((first, second))
880      }
881    }
882  }
883}
884
885/// Discrete intersection test of 2D spheres.
886///
887/// Spheres that are merely touching are not counted as intersecting:
888///
889/// ```
890/// # use math_utils::num_traits::One;
891/// # use math_utils::*;
892/// # use math_utils::geometry::*;
893/// # use math_utils::geometry::intersect::*;
894/// let r = Positive::one();
895/// let a = Sphere2 { center: [ 1.0, 0.0].into(), radius: r };
896/// let b = Sphere2 { center: [-1.0, 0.0].into(), radius: r };
897/// assert!(!discrete_sphere2_sphere2 (&a, &b));
898/// ```
899#[inline]
900pub fn discrete_sphere2_sphere2 <S : Ring> (
901  a : &Sphere2 <S>, b : &Sphere2 <S>
902) -> bool {
903  let r_ab = *(a.radius + b.radius);
904  (b.center - a.center).self_dot() < r_ab * r_ab
905}
906
907/// Discrete intersection test of 3D spheres.
908///
909/// Spheres that are merely touching are not counted as intersecting:
910///
911/// ```
912/// # use math_utils::num_traits::One;
913/// # use math_utils::*;
914/// # use math_utils::geometry::*;
915/// # use math_utils::geometry::intersect::*;
916/// let r = Positive::one();
917/// let a = Sphere3 { center: [ 1.0, 0.0,  0.0].into(), radius: r };
918/// let b = Sphere3 { center: [-1.0, 0.0,  0.0].into(), radius: r };
919/// assert!(!discrete_sphere3_sphere3 (&a, &b));
920/// ```
921#[inline]
922pub fn discrete_sphere3_sphere3 <S : Ring> (
923  a : &Sphere3 <S>, b : &Sphere3 <S>
924) -> bool {
925  let r_ab = *(a.radius + b.radius);
926  (b.center - a.center).self_dot() < r_ab * r_ab
927}
928
929#[cfg(test)]
930mod tests {
931  use super::*;
932
933  #[test]
934  fn test_line2_aabb2() {
935    use std::f64::consts::SQRT_2;
936    let aabb = Aabb2::with_minmax ([-1.0, -1.0].into(), [1.0, 1.0].into());
937    let line = Line2::new ([ 0.0, 0.0].into(), Unit2::axis_y());
938    assert_eq!(
939      continuous_line2_aabb2 (&line, &aabb).unwrap(),
940      ((-1.0, [ 0.0, -1.0].into()), (1.0, [ 0.0, 1.0].into())));
941    let line = Line2::new ([ 0.0, 0.0].into(), Unit2::axis_x());
942    assert_eq!(
943      continuous_line2_aabb2 (&line, &aabb).unwrap(),
944      ((-1.0, [-1.0,  0.0].into()), (1.0, [ 1.0, 0.0].into())));
945    let line = Line2::new ([ 0.0, 0.0].into(),
946      Unit2::normalize ([1.0, 1.0].into()));
947    assert_eq!(
948      continuous_line2_aabb2 (&line, &aabb).unwrap(),
949      ((-SQRT_2, [-1.0, -1.0].into()), (SQRT_2, [ 1.0,  1.0].into())));
950    let line = Line2::new ([ 0.0, 0.0].into(),
951      Unit2::normalize ([-1.0, -1.0].into()));
952    assert_eq!(
953      continuous_line2_aabb2 (&line, &aabb).unwrap(),
954      ((-SQRT_2, [ 1.0,  1.0].into()), (SQRT_2, [-1.0, -1.0].into())));
955    let line = Line2::new ([ 0.0, 3.0].into(),
956      Unit2::normalize ([-1.0, -1.0].into()));
957    assert!(continuous_line2_aabb2 (&line, &aabb).is_none());
958    let line = Line2::new ([ 0.0, -3.0].into(),
959      Unit2::normalize ([ 1.0,  1.0].into()));
960    assert!(continuous_line2_aabb2 (&line, &aabb).is_none());
961  }
962
963  #[test]
964  fn test_line3_aabb3() {
965    use approx::assert_ulps_eq;
966    let aabb = Aabb3::with_minmax (
967      [-1.0, -1.0, -1.0].into(), [1.0, 1.0, 1.0].into());
968    let line = Line3::new ([ 0.0, 0.0, 0.0].into(), Unit3::axis_z());
969    assert_eq!(
970      continuous_line3_aabb3 (&line, &aabb).unwrap(),
971      ((-1.0, [ 0.0, 0.0, -1.0].into()), (1.0, [ 0.0, 0.0, 1.0].into())));
972    let line = Line3::new ([ 0.0, 0.0, 0.0].into(), Unit3::axis_y());
973    assert_eq!(
974      continuous_line3_aabb3 (&line, &aabb).unwrap(),
975      ((-1.0, [0.0, -1.0,  0.0].into()), (1.0, [ 0.0, 1.0, 0.0].into())));
976    {
977      let line = Line3::new ([ 0.0, 0.0, 0.0].into(),
978        Unit3::normalize ([1.0, 1.0, 1.0].into()));
979      let result = continuous_line3_aabb3 (&line, &aabb).unwrap();
980      assert_ulps_eq!((result.0).0, -f64::sqrt_3());
981      assert_ulps_eq!((result.1).0, f64::sqrt_3());
982      assert_eq!((result.0).1, [-1.0, -1.0, -1.0].into());
983      assert_eq!((result.1).1, [ 1.0,  1.0,  1.0].into());
984    }
985    {
986      let line = Line3::new ([ 0.0, 0.0, 0.0].into(),
987        Unit3::normalize ([-1.0, -1.0, -1.0].into()));
988      let result = continuous_line3_aabb3 (&line, &aabb).unwrap();
989      assert_ulps_eq!((result.0).0, -f64::sqrt_3());
990      assert_ulps_eq!((result.1).0, f64::sqrt_3());
991      assert_eq!((result.0).1, [ 1.0,  1.0,  1.0].into());
992      assert_eq!((result.1).1, [-1.0, -1.0, -1.0].into());
993    }
994    let line = Line3::new ([ 0.0, 0.0, 3.0].into(),
995      Unit3::normalize ([-1.0, -1.0, -1.0].into()));
996    assert!(continuous_line3_aabb3 (&line, &aabb).is_none());
997    let line = Line3::new ([0.0, 0.0, -3.0].into(),
998      Unit3::normalize ([1.0, 1.0, 1.0].into()));
999    assert!(continuous_line3_aabb3 (&line, &aabb).is_none());
1000  }
1001
1002  #[test]
1003  fn test_segment3_sphere3() {
1004    let sphere  = shape::Sphere::unit().sphere3 (Point3::origin());
1005    let segment = Segment3::new (
1006      [-2.0, 0.0, 0.0].into(), [ 2.0, 0.0, 0.0].into());
1007    assert_eq!(
1008      continuous_segment3_sphere3 (&segment, &sphere).unwrap(),
1009      ((0.25, [-1.0, 0.0, 0.0].into()), (0.75, [ 1.0, 0.0, 0.0].into())));
1010    let segment = Segment3::new (
1011      [ 2.0, 0.0, 0.0].into(), [-2.0, 0.0, 0.0].into());
1012    assert_eq!(
1013      continuous_segment3_sphere3 (&segment, &sphere).unwrap(),
1014      ((0.25, [ 1.0, 0.0, 0.0].into()), (0.75, [-1.0, 0.0, 0.0].into())));
1015    let segment = Segment3::new (
1016      [ 0.0, 0.0, 0.0].into(), [ 0.0, 0.0, 2.0].into());
1017    assert_eq!(
1018      continuous_segment3_sphere3 (&segment, &sphere).unwrap(),
1019      ((0.0, [ 0.0, 0.0, 0.0].into()), (0.5, [ 0.0, 0.0, 1.0].into())));
1020    let segment = Segment3::new (
1021      [ 0.0, 0.0, -2.0].into(), [ 0.0, 0.0, 0.0].into());
1022    assert_eq!(
1023      continuous_segment3_sphere3 (&segment, &sphere).unwrap(),
1024      ((0.5, [ 0.0, 0.0, -1.0].into()), (1.0, [ 0.0, 0.0, 0.0].into())));
1025  }
1026
1027  #[test]
1028  fn test_segment3_cylinder3() {
1029    let cylinder = shape::Cylinder::unit().cylinder3 (Point3::origin());
1030    let segment = Segment3::new (
1031      [-2.0, 0.0, 0.0].into(), [ 2.0, 0.0, 0.0].into());
1032    assert_eq!(
1033      continuous_segment3_cylinder3 (&segment, &cylinder).unwrap(),
1034      ((0.25, [-1.0, 0.0, 0.0].into()), (0.75, [ 1.0, 0.0, 0.0].into())));
1035    let segment = Segment3::new (
1036      [ 2.0, 0.0, 0.0].into(), [-2.0, 0.0, 0.0].into());
1037    assert_eq!(
1038      continuous_segment3_cylinder3 (&segment, &cylinder).unwrap(),
1039      ((0.25, [ 1.0, 0.0, 0.0].into()), (0.75, [-1.0, 0.0, 0.0].into())));
1040    let segment = Segment3::new (
1041      [ 0.0, 0.0, 0.0].into(), [ 0.0, 0.0, 2.0].into());
1042    assert_eq!(
1043      continuous_segment3_cylinder3 (&segment, &cylinder).unwrap(),
1044      ((0.0, [ 0.0, 0.0, 0.0].into()), (0.5, [ 0.0, 0.0, 1.0].into())));
1045    let segment = Segment3::new (
1046      [ 0.0, 0.0, -2.0].into(), [ 0.0, 0.0, 0.0].into());
1047    assert_eq!(
1048      continuous_segment3_cylinder3 (&segment, &cylinder).unwrap(),
1049      ((0.5, [ 0.0, 0.0, -1.0].into()), (1.0, [ 0.0, 0.0, 0.0].into())));
1050  }
1051
1052  #[test]
1053  fn test_segment3_capsule3() {
1054    let capsule = shape::Capsule::noisy (1.0, 1.0)
1055      .capsule3 (Point3::origin());
1056    let segment = Segment3::new (
1057      [-2.0, 0.0, 0.0].into(), [ 2.0, 0.0, 0.0].into());
1058    assert_eq!(
1059      continuous_segment3_capsule3 (&segment, &capsule).unwrap(),
1060      ((0.25, [-1.0, 0.0, 0.0].into()), (0.75, [ 1.0, 0.0, 0.0].into())));
1061    let segment = Segment3::new (
1062      [ 2.0, 0.0, 0.0].into(), [-2.0, 0.0, 0.0].into());
1063    assert_eq!(
1064      continuous_segment3_capsule3 (&segment, &capsule).unwrap(),
1065      ((0.25, [ 1.0, 0.0, 0.0].into()), (0.75, [-1.0, 0.0, 0.0].into())));
1066    let segment = Segment3::new (
1067      [ 0.0, 0.0, 0.0].into(), [ 0.0, 0.0, 4.0].into());
1068    assert_eq!(
1069      continuous_segment3_capsule3 (&segment, &capsule).unwrap(),
1070      ((0.0, [ 0.0, 0.0, 0.0].into()), (0.5, [ 0.0, 0.0, 2.0].into())));
1071    let segment = Segment3::new (
1072      [ 0.0, 0.0, -4.0].into(), [ 0.0, 0.0, 0.0].into());
1073    assert_eq!(
1074      continuous_segment3_capsule3 (&segment, &capsule).unwrap(),
1075      ((0.5, [ 0.0, 0.0, -2.0].into()), (1.0, [ 0.0, 0.0, 0.0].into())));
1076  }
1077
1078  #[test]
1079  fn test_segment3_aabb3() {
1080    let aabb    = Aabb3::with_minmax (
1081      [1.0, -0.5, 0.0].into(), [2.0, 0.5, 1.0].into()
1082    );
1083    let segment = Segment3::new (
1084      [-1.0, 0.0, 0.5].into(), [ 2.0, 0.0, 0.5].into());
1085    assert_eq!(
1086      continuous_segment3_aabb3 (&segment, &aabb).unwrap(),
1087      ((2.0/3.0, [1.0, 0.0, 0.5].into()), (1.0, [ 2.0, 0.0, 0.5].into())));
1088  }
1089
1090} // end tests