Skip to main content

linear_sim/collision/proximity/
mod.rs

1use crate::{collision, component, constraint, object};
2use crate::geometry::shape;
3use crate::math::*;
4
5mod gjk;
6
7#[cfg(test)]
8mod bench;
9
10/// Relation configuration of a pair of bounded objects.
11///
12/// - 'Disjoint' corresponds to a positive distance `> CONTACT_DISTANCE`
13/// - 'Contact' corresponds to a positive distance `<= CONTACT_DISTANCE`
14/// - 'Inersect' corresponds to a strictly negative contact distance `< 0.0`
15#[derive(Clone, Copy, Debug, Eq, PartialEq)]
16pub enum Relation {
17  Disjoint,
18  Contact,
19  Intersect
20}
21
22/// Result of a proximity query between a pair of bounded objects.
23///
24/// Note that the *axis vector* points from object A to object B, while the *normal
25/// vector* points from object B to object A, but they should be parallel when the axis
26/// vector is non-zero.
27#[derive(Clone, Debug, PartialEq)]
28pub struct Proximity {
29  /// Signed distance.
30  ///
31  /// A positive distance indicates the separating distance for a disjoint result, and a
32  /// negative distance indicates a penetration depth of an intersection result.
33  pub distance  : f64,
34  /// Non-normalized vector pointing along the axis of separation or penetration from
35  /// object A to object B.
36  ///
37  /// For a disjoint configuration, adding this vector to the midpoint gives the nearest
38  /// point on object B and subtracting this vector from the midpoint gives the nearest
39  /// point on object A. Translating object A by this vector and translating object B by
40  /// its inverse will bring the objects into contact.
41  ///
42  /// For an intersecting configuration, translating object A by this vector and
43  /// translating object B by its inverse will resolve the inter-penetration.
44  ///
45  /// Note this vector can be zero if the objects are in contact.
46  pub half_axis : Vector3 <f64>,
47  /// Indicates the midpoint of the proximity query
48  pub midpoint  : Point3  <f64>,
49  /// The unit normal to the separating plane directed from object B to object A
50  pub normal    : Unit3 <f64>
51}
52
53impl Proximity {
54  pub fn nearest_a (&self) -> Point3 <f64> {
55    self.midpoint - self.half_axis
56  }
57
58  pub fn nearest_b (&self) -> Point3 <f64> {
59    self.midpoint + self.half_axis
60  }
61
62  pub fn relation (&self) -> Relation {
63    if self.distance < 0.0 {
64      Relation::Intersect
65    } else if self.distance <= collision::CONTACT_DISTANCE {
66      Relation::Contact
67    } else {
68      debug_assert!(self.distance > collision::CONTACT_DISTANCE);
69      Relation::Disjoint
70    }
71  }
72
73  #[inline]
74  pub const fn constraint_planar (&self) -> constraint::Planar {
75    constraint::Planar::new (self.midpoint, self.normal)
76  }
77
78  /// Flip the axis and normal so that the relation of objects A and B are reversed
79  pub fn flip (mut self) -> Self {
80    self.half_axis = -self.half_axis;
81    self.normal = -self.normal;
82    self
83  }
84
85  /// Proximity query.
86  ///
87  /// # Examples
88  ///
89  /// **Capsule v. capsule**
90  ///
91  /// By convention if the axes of capsules A and B intersect, the +X axis will be
92  /// chosen for the axis vector and normal:
93  ///
94  /// ```
95  /// # extern crate linear_sim;
96  /// # use linear_sim::*;
97  /// # use collision::*;
98  /// # use geometry::shape;
99  /// # use math;
100  /// # fn main() {
101  /// let a = object::Static {
102  ///   position:   component::Position ([0.0, 0.0, 1.0].into()),
103  ///   bound:      component::Bound::from (shape::Capsule::noisy (2.0, 3.0)),
104  ///   material:   component::MATERIAL_STONE,
105  ///   collidable: true
106  /// };
107  /// let b = object::Static {
108  ///   position:   component::Position ([0.0, 0.0, -1.0].into()),
109  ///   bound:      component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
110  ///   material:   component::MATERIAL_STONE,
111  ///   collidable: true
112  /// };
113  /// assert_eq!(
114  ///   Proximity::query (&a, &b),
115  ///   Proximity {
116  ///     distance:  -3.0,
117  ///     half_axis: [ 1.5, 0.0,  0.0].into(),
118  ///     normal:    math::Unit3::axis_x(),
119  ///     midpoint:  [-0.5, 0.0, -0.5].into()
120  ///   }
121  /// );
122  /// # }
123  /// ```
124  pub fn query <A, B> (object_a : &A, object_b : &B) -> Self where
125    A : object::Bounded, B : object::Bounded
126  {
127    let component::Position (position_a) = object_a.position();
128    let component::Position (position_b) = object_b.position();
129    let component::Bound    (shape_a)    = object_a.bound();
130    let component::Bound    (shape_b)    = object_b.bound();
131
132    match (shape_a, shape_b) {
133      //
134      // sphere vs. sphere
135      //
136      ( shape::Variant::Bounded (shape::Bounded::Sphere (sphere_a)),
137        shape::Variant::Bounded (shape::Bounded::Sphere (sphere_b))
138      ) => Self::query_sphere_sphere (position_a, sphere_a, position_b, sphere_b),
139      //
140      // capsule vs. capsule
141      //
142      ( shape::Variant::Bounded (shape::Bounded::Capsule (capsule_a)),
143        shape::Variant::Bounded (shape::Bounded::Capsule (capsule_b))
144      ) => Self::query_capsule_capsule (position_a, capsule_a, position_b, capsule_b),
145      //
146      // cuboid vs. cuboid
147      //
148      ( shape::Variant::Bounded (shape::Bounded::Cuboid (cuboid_a)),
149        shape::Variant::Bounded (shape::Bounded::Cuboid (cuboid_b))
150      ) => Self::query_cuboid_cuboid (position_a, cuboid_a, position_b, cuboid_b),
151      //
152      // sphere vs. capsule
153      //
154      ( shape::Variant::Bounded (shape::Bounded::Sphere (sphere_a)),
155        shape::Variant::Bounded (shape::Bounded::Capsule (capsule_b))
156      ) => Self::query_sphere_capsule (position_a, sphere_a, position_b, capsule_b),
157      ( shape::Variant::Bounded (shape::Bounded::Capsule (capsule_a)),
158        shape::Variant::Bounded (shape::Bounded::Sphere (sphere_b))
159      ) => Self::query_sphere_capsule (position_b, sphere_b, position_a, capsule_a)
160        .flip(),
161      //
162      // sphere vs. cuboid
163      //
164      ( shape::Variant::Bounded (shape::Bounded::Sphere (sphere_a)),
165        shape::Variant::Bounded (shape::Bounded::Cuboid (cuboid_b))
166      ) => Self::query_sphere_cuboid (
167        position_a, sphere_a, position_b, cuboid_b),
168      ( shape::Variant::Bounded (shape::Bounded::Cuboid (cuboid_a)),
169        shape::Variant::Bounded (shape::Bounded::Sphere (sphere_b))
170      ) => Self::query_sphere_cuboid (position_b, sphere_b, position_a, cuboid_a).flip(),
171      //
172      // sphere vs. orthant
173      //
174      ( shape::Variant::Bounded   (shape::Bounded::Sphere    (sphere_a)),
175        shape::Variant::Unbounded (shape::Unbounded::Orthant (orthant_b))
176      ) => Self::query_sphere_orthant (position_a, sphere_a, position_b, orthant_b),
177      ( shape::Variant::Unbounded (shape::Unbounded::Orthant (orthant_a)),
178        shape::Variant::Bounded   (shape::Bounded::Sphere    (sphere_b))
179      ) => Self::query_sphere_orthant (position_b, sphere_b, position_a, orthant_a)
180        .flip(),
181      //
182      // capsule vs. cuboid
183      //
184      ( shape::Variant::Bounded (shape::Bounded::Capsule (capsule_a)),
185        shape::Variant::Bounded (shape::Bounded::Cuboid  (cuboid_b))
186      ) => Self::query_capsule_cuboid (position_a, capsule_a, position_b, cuboid_b),
187      ( shape::Variant::Bounded (shape::Bounded::Cuboid  (cuboid_a)),
188        shape::Variant::Bounded (shape::Bounded::Capsule (capsule_b))
189      ) => Self::query_capsule_cuboid (position_b, capsule_b, position_a, cuboid_a)
190        .flip(),
191      //
192      // capsule vs. orthant
193      //
194      ( shape::Variant::Bounded   (shape::Bounded::Capsule   (capsule_a)),
195        shape::Variant::Unbounded (shape::Unbounded::Orthant (orthant_b))
196      ) => Self::query_capsule_orthant (position_a, capsule_a, position_b, orthant_b),
197      ( shape::Variant::Unbounded (shape::Unbounded::Orthant (orthant_a)),
198        shape::Variant::Bounded   (shape::Bounded::Capsule   (capsule_b))
199      ) => Self::query_capsule_orthant (position_b, capsule_b, position_a, orthant_a)
200        .flip(),
201      //
202      // cuboid vs. orthant
203      //
204      ( shape::Variant::Bounded   (shape::Bounded::Cuboid    (cuboid_a)),
205        shape::Variant::Unbounded (shape::Unbounded::Orthant (orthant_b))
206      ) => Self::query_cuboid_orthant (position_a, cuboid_a, position_b, orthant_b),
207      ( shape::Variant::Unbounded (shape::Unbounded::Orthant (orthant_a)),
208        shape::Variant::Bounded   (shape::Bounded::Cuboid    (cuboid_b))
209      ) => Self::query_cuboid_orthant (position_b, cuboid_b, position_a, orthant_a)
210        .flip(),
211      //
212      // hull vs. any
213      //
214      (shape::Variant::Bounded (shape::Bounded::Hull (_)), _) =>
215        Self::query_gjk (object_a, object_b),
216      (_, shape::Variant::Bounded (shape::Bounded::Hull (_))) =>
217        Self::query_gjk (object_b, object_a).flip(),
218      _ => unimplemented!("proximity query not implemented for (A, B): {:?}",
219        (shape_a, shape_b))
220    }
221  }
222
223  pub fn query_sphere_sphere (
224    position_a : &Point3 <f64>, sphere_a : &shape::Sphere <f64>,
225    position_b : &Point3 <f64>, sphere_b : &shape::Sphere <f64>
226  ) -> Self {
227    use num::Zero;
228    let (radius_a, radius_b) = (*sphere_a.radius, *sphere_b.radius);
229    let distance_normal_half_axis = |axis : &Vector3 <f64>| {
230      let axis_distance = axis.magnitude();
231      let distance      = axis_distance - radius_a - radius_b;
232      let normal        = if axis.is_zero() {
233        Unit3::axis_x()
234      } else {
235        Unit3::unchecked_approx (-(axis / axis_distance))
236      };
237      let half_axis     = -0.5 * distance * *normal;
238      (distance, normal, half_axis)
239    };
240    let axis = position_b - position_a;
241    let (distance, normal, half_axis) = distance_normal_half_axis (&axis);
242    let midpoint = position_a - radius_a * *normal + half_axis;
243    Proximity { distance, half_axis, midpoint, normal }
244  }
245
246  pub fn query_capsule_capsule (
247    position_a : &Point3 <f64>, capsule_a : &shape::Capsule <f64>,
248    position_b : &Point3 <f64>, capsule_b : &shape::Capsule <f64>
249  ) -> Self {
250    use num::Zero;
251    let (half_height_a, half_height_b) =
252      (*capsule_a.half_height, *capsule_b.half_height);
253    let (radius_a, radius_b) = (*capsule_a.radius, *capsule_b.radius);
254    let shaft_max_a = position_a + vector3 (0.0, 0.0, half_height_a);
255    let shaft_min_a = position_a - vector3 (0.0, 0.0, half_height_a);
256    let shaft_max_b = position_b + vector3 (0.0, 0.0, half_height_b);
257    let shaft_min_b = position_b - vector3 (0.0, 0.0, half_height_b);
258    let distance_normal_half_axis = |axis : &Vector3 <f64>| {
259      let axis_distance = axis.magnitude();
260      let distance      = axis_distance - radius_a - radius_b;
261      let normal        = if axis.is_zero() {
262        Unit3::axis_x()
263      } else {
264        Unit3::unchecked_approx (-(axis / axis_distance))
265      };
266      let half_axis     = -0.5 * distance * *normal;
267      (distance, normal, half_axis)
268    };
269    if shaft_max_a.0.z <= shaft_min_b.0.z {
270      // cylinder shaft of B is above cylinder shaft of A
271      let axis     = shaft_min_b - shaft_max_a;
272      let (distance, normal, half_axis) = distance_normal_half_axis (&axis);
273      let midpoint = shaft_max_a - radius_a * *normal + half_axis;
274      Proximity { distance, half_axis, midpoint, normal }
275    } else if shaft_max_b.0.z <= shaft_min_a.0.z {
276      // cylinder shaft of B is below cylinder shaft of A
277      let axis     = shaft_max_b.0 - shaft_min_a.0;
278      let (distance, normal, half_axis) = distance_normal_half_axis (&axis);
279      let midpoint = shaft_min_a - radius_a * *normal + half_axis;
280      Proximity { distance, half_axis, midpoint, normal }
281    } else {
282      // cylinder shafts are adjacent
283      let axis =
284        Point3::from ([position_b.0.x, position_b.0.y, 0.0]) -
285        Point3::from ([position_a.0.x, position_a.0.y, 0.0]);
286      let (distance, normal, half_axis) = distance_normal_half_axis (&axis);
287      let midpoint = {
288        let max_z  = f64::min (shaft_max_a.0.z, shaft_max_b.0.z);
289        let min_z  = f64::max (shaft_min_a.0.z, shaft_min_b.0.z);
290        let mid_z  = 0.5 * (max_z + min_z);
291        let mid_a  = position_a - radius_a * *normal + half_axis;
292        [mid_a.0.x, mid_a.0.y, mid_z].into()
293      };
294      Proximity { distance, half_axis, midpoint, normal }
295    }
296  }
297
298  pub fn query_cuboid_cuboid (
299    position_a : &Point3 <f64>, cuboid_a : &shape::Cuboid <f64>,
300    position_b : &Point3 <f64>, cuboid_b : &shape::Cuboid <f64>
301  ) -> Self {
302    use VectorSpace;
303    use num::Zero;
304    fn nearest_ab_to_distance (
305      nearest_a      : &Point3  <f64>,
306      nearest_b      : &Point3  <f64>,
307      default_normal : &Unit3 <f64>
308    ) -> Proximity {
309      let axis      = nearest_b - nearest_a;
310      let distance  = axis.magnitude();
311      let half_axis = 0.5 * (nearest_b - nearest_a);
312      let midpoint  = nearest_a + half_axis;
313      let normal    = if axis.is_zero() {
314        *default_normal
315      } else {
316        Unit3::unchecked_approx (-axis / distance)
317      };
318      Proximity { distance, half_axis, midpoint, normal }
319    }
320
321    let max_a = position_a + cuboid_a.max().0;
322    let min_a = position_a + cuboid_a.min().0;
323    let max_b = position_b + cuboid_b.max().0;
324    let min_b = position_b + cuboid_b.min().0;
325    let overlap_x = min_b.0.x < max_a.0.x && min_a.0.x < max_b.0.x;
326    let overlap_y = min_b.0.y < max_a.0.y && min_a.0.y < max_b.0.y;
327    let overlap_z = min_b.0.z < max_a.0.z && min_a.0.z < max_b.0.z;
328    match (overlap_x, overlap_y, overlap_z) {
329      (false, false, false) => {
330        // no overlaps: nearest features are corners
331        let a_to_b = position_b - position_a;
332        debug_assert!(!a_to_b.x.is_zero());
333        debug_assert!(!a_to_b.y.is_zero());
334        debug_assert!(!a_to_b.z.is_zero());
335        let a_to_b_sigvec = a_to_b.map (f64::signum);
336        let nearest_a = position_a + cuboid_a.max().0 * a_to_b_sigvec;
337        let nearest_b = position_b + cuboid_b.max().0 * (-a_to_b_sigvec);
338        nearest_ab_to_distance (&nearest_a, &nearest_b,
339          &Unit3::normalize (-a_to_b_sigvec))
340      }
341      (true, false, false)  => {
342        // overlap x only: nearest features are edges
343        let max_x  = f64::min (max_a.0.x, max_b.0.x);
344        let min_x  = f64::max (min_a.0.x, min_b.0.x);
345        debug_assert_ne!(min_x, max_x);
346        let mid_x  = 0.5 * (min_x + max_x);
347        let a_to_b = position_b - position_a;
348        let a_to_b_yz = vector3 (0.0, a_to_b.y, a_to_b.z);
349        let a_to_b_yz_sigvec = Vector3::sigvec (a_to_b_yz);
350        debug_assert!(a_to_b_yz_sigvec.x.is_zero());
351        debug_assert!(!a_to_b_yz_sigvec.y.is_zero());
352        debug_assert!(!a_to_b_yz_sigvec.z.is_zero());
353        let nearest_a = {
354          let y = *cuboid_a.extents.y * a_to_b_yz_sigvec.y;
355          let z = *cuboid_a.extents.z * a_to_b_yz_sigvec.z;
356          [mid_x, position_a.0.y + y, position_a.0.z + z].into()
357        };
358        let nearest_b = {
359          let y = -*cuboid_b.extents.y * a_to_b_yz_sigvec.y;
360          let z = -*cuboid_b.extents.z * a_to_b_yz_sigvec.z;
361          [mid_x, position_b.0.y + y, position_b.0.z + z].into()
362        };
363        nearest_ab_to_distance (&nearest_a, &nearest_b,
364          &Unit3::normalize (-a_to_b_yz_sigvec))
365      }
366      (false, true, false)  => {
367        // overlap y only: nearest features are edges
368        let max_y  = f64::min (max_a.0.y, max_b.0.y);
369        let min_y  = f64::max (min_a.0.y, min_b.0.y);
370        debug_assert_ne!(min_y, max_y);
371        let mid_y  = 0.5 * (min_y + max_y);
372        let a_to_b = position_b - position_a;
373        let a_to_b_xz = vector3 (a_to_b.x, 0.0, a_to_b.z);
374        let a_to_b_xz_sigvec = Vector3::sigvec (a_to_b_xz);
375        debug_assert!(!a_to_b_xz_sigvec.x.is_zero());
376        debug_assert!(a_to_b_xz_sigvec.y.is_zero());
377        debug_assert!(!a_to_b_xz_sigvec.z.is_zero());
378        let nearest_a = {
379          let x = *cuboid_a.extents.x * a_to_b_xz_sigvec.x;
380          let z = *cuboid_a.extents.z * a_to_b_xz_sigvec.z;
381          [position_a.0.x + x, mid_y, position_a.0.z + z].into()
382        };
383        let nearest_b = {
384          let x = -*cuboid_b.extents.x * a_to_b_xz_sigvec.x;
385          let z = -*cuboid_b.extents.z * a_to_b_xz_sigvec.z;
386          [position_b.0.x + x, mid_y, position_b.0.z + z].into()
387        };
388        nearest_ab_to_distance (&nearest_a, &nearest_b,
389          &Unit3::normalize (-a_to_b_xz_sigvec))
390      }
391      (false, false, true)  => {
392        // overlap z only: nearest features are edges
393        let max_z  = f64::min (max_a.0.z, max_b.0.z);
394        let min_z  = f64::max (min_a.0.z, min_b.0.z);
395        debug_assert_ne!(min_z, max_z);
396        let mid_z  = 0.5 * (min_z + max_z);
397        let a_to_b = position_b - position_a;
398        let a_to_b_xy = vector3 (a_to_b.x, a_to_b.y, 0.0);
399        let a_to_b_xy_sigvec = Vector3::sigvec (a_to_b_xy);
400        debug_assert!(!a_to_b_xy_sigvec.x.is_zero());
401        debug_assert!(!a_to_b_xy_sigvec.y.is_zero());
402        debug_assert!(a_to_b_xy_sigvec.z.is_zero());
403        let nearest_a = {
404          let x = *cuboid_a.extents.x * a_to_b_xy_sigvec.x;
405          let y = *cuboid_a.extents.y * a_to_b_xy_sigvec.y;
406          [position_a.0.x + x, position_a.0.y + y, mid_z].into()
407        };
408        let nearest_b = {
409          let x = -*cuboid_b.extents.x * a_to_b_xy_sigvec.x;
410          let y = -*cuboid_b.extents.y * a_to_b_xy_sigvec.y;
411          [position_b.0.x + x, position_b.0.y + y, mid_z].into()
412        };
413        nearest_ab_to_distance (&nearest_a, &nearest_b,
414          &Unit3::normalize (-a_to_b_xy_sigvec))
415      }
416      (true, true, false)   => {
417        // overlap x and y: nearest features are faces
418        let max_x    = f64::min (max_a.0.x, max_b.0.x);
419        let min_x    = f64::max (min_a.0.x, min_b.0.x);
420        let mid_x    = 0.5 * (min_x + max_x);
421        let max_y    = f64::min (max_a.0.y, max_b.0.y);
422        let min_y    = f64::max (min_a.0.y, min_b.0.y);
423        let mid_y    = 0.5 * (min_y + max_y);
424        let a_to_b_z = position_b.0.z - position_a.0.z;
425        let a_to_b_signum_z = a_to_b_z.signum();
426        debug_assert_ne!(a_to_b_signum_z, 0.0);
427        let nearest_a = point3 (
428          mid_x, mid_y,
429          //position_a.0.z + a_to_b_signum_z * *cuboid_a.extents.z
430          a_to_b_signum_z.mul_add (*cuboid_a.extents.z, position_a.0.z)
431        );
432        let nearest_b = point3 (
433          mid_x, mid_y,
434          //position_b.0.z - a_to_b_signum_z * *cuboid_b.extents.z
435          a_to_b_signum_z.mul_add (-*cuboid_b.extents.z, position_b.0.z)
436        );
437        let nearest_a_to_b_z = nearest_b.0.z - nearest_a.0.z;
438        let distance  = nearest_a_to_b_z.abs();
439        let half_axis = [0.0, 0.0, 0.5 * nearest_a_to_b_z].into();
440        let normal    = Unit3::unchecked ([0.0, 0.0, -a_to_b_signum_z].into());
441        let midpoint  = nearest_a + half_axis;
442        Proximity { distance, half_axis, midpoint, normal }
443      }
444      (true, false, true)   => {
445        // overlap x and z: nearest features are faces
446        let max_x    = f64::min (max_a.0.x, max_b.0.x);
447        let min_x    = f64::max (min_a.0.x, min_b.0.x);
448        let mid_x    = 0.5 * (min_x + max_x);
449        let max_z    = f64::min (max_a.0.z, max_b.0.z);
450        let min_z    = f64::max (min_a.0.z, min_b.0.z);
451        let mid_z    = 0.5 * (min_z + max_z);
452        let a_to_b_y = position_b.0.y - position_a.0.y;
453        let a_to_b_signum_y = a_to_b_y.signum();
454        debug_assert_ne!(a_to_b_signum_y, 0.0);
455        let nearest_a = point3 (
456          mid_x,
457          //position_a.0.y + a_to_b_signum_y * *cuboid_a.extents.y,
458          a_to_b_signum_y.mul_add (*cuboid_a.extents.y, position_a.0.y),
459          mid_z
460        );
461        let nearest_b = point3 (
462          mid_x,
463          //position_b.0.y - a_to_b_signum_y * *cuboid_b.extents.y,
464          a_to_b_signum_y.mul_add (-*cuboid_b.extents.y, position_b.0.y),
465          mid_z
466        );
467        let nearest_a_to_b_y = nearest_b.0.y - nearest_a.0.y;
468        let distance  = nearest_a_to_b_y.abs();
469        let half_axis = [0.0, 0.5 * nearest_a_to_b_y, 0.0].into();
470        let normal    = Unit3::unchecked ([0.0, -a_to_b_signum_y, 0.0].into());
471        let midpoint  = nearest_a + half_axis;
472        Proximity { distance, half_axis, midpoint, normal }
473      }
474      (false, true, true)   => {
475        // overlap y and z: nearest features are faces
476        let max_y    = f64::min (max_a.0.y, max_b.0.y);
477        let min_y    = f64::max (min_a.0.y, min_b.0.y);
478        let mid_y    = 0.5 * (min_y + max_y);
479        let max_z    = f64::min (max_a.0.z, max_b.0.z);
480        let min_z    = f64::max (min_a.0.z, min_b.0.z);
481        let mid_z    = 0.5 * (min_z + max_z);
482        let a_to_b_x = position_b.0.x - position_a.0.x;
483        let a_to_b_signum_x = a_to_b_x.signum();
484        debug_assert_ne!(a_to_b_signum_x, 0.0);
485        let nearest_a = point3 (
486          //position_a.0.x + a_to_b_signum_x * *cuboid_a.extents.x,
487          a_to_b_signum_x.mul_add (*cuboid_a.extents.x, position_a.0.x),
488          mid_y,
489          mid_z
490        );
491        let nearest_b = point3 (
492          //position_b.0.x - a_to_b_signum_x * *cuboid_b.extents.x,
493          a_to_b_signum_x.mul_add (-*cuboid_b.extents.x, position_b.0.x),
494          mid_y,
495          mid_z
496        );
497        let nearest_a_to_b_x = nearest_b.0.x - nearest_a.0.x;
498        let distance  = nearest_a_to_b_x.abs();
499        let half_axis = [0.5 * nearest_a_to_b_x, 0.0, 0.0].into();
500        let normal    = Unit3::unchecked ([-a_to_b_signum_x, 0.0, 0.0].into());
501        let midpoint  = nearest_a + half_axis;
502        Proximity { distance, half_axis, midpoint, normal }
503      }
504      (true, true, true)    => {
505        // overlap on all axes: intersection
506        let max_x     = f64::min (max_a.0.x, max_b.0.x);
507        let min_x     = f64::max (min_a.0.x, min_b.0.x);
508        let mid_x     = 0.5 * (min_x + max_x);
509        let max_y     = f64::min (max_a.0.y, max_b.0.y);
510        let min_y     = f64::max (min_a.0.y, min_b.0.y);
511        let mid_y     = 0.5 * (min_y + max_y);
512        let max_z     = f64::min (max_a.0.z, max_b.0.z);
513        let min_z     = f64::max (min_a.0.z, min_b.0.z);
514        let mid_z     = 0.5 * (min_z + max_z);
515        let midpoint  = [mid_x, mid_y, mid_z].into();
516        let resolve_x = if position_a.0.x < position_b.0.x {
517          min_b.0.x - max_a.0.x
518        } else {
519          debug_assert!(position_a.0.x >= position_b.0.x);
520          max_b.0.x - min_a.0.x
521        };
522        let resolve_y = if position_a.0.y < position_b.0.y {
523          min_b.0.y - max_a.0.y
524        } else {
525          debug_assert!(position_a.0.y >= position_b.0.y);
526          max_b.0.y - min_a.0.y
527        };
528        let resolve_z = if position_a.0.z < position_b.0.z {
529          min_b.0.z - max_a.0.z
530        } else {
531          debug_assert!(position_a.0.z >= position_b.0.z);
532          max_b.0.z - min_a.0.z
533        };
534        let resolve_x_abs = resolve_x.abs();
535        let resolve_y_abs = resolve_y.abs();
536        let resolve_z_abs = resolve_z.abs();
537        let (distance, half_axis, normal) : (
538          f64, Vector3 <f64>, Unit3 <f64>
539        ) = if resolve_x_abs <= resolve_y_abs && resolve_x_abs <= resolve_z_abs {
540          ( -resolve_x_abs,
541            0.5 * vector3 (resolve_x, 0.0, 0.0),
542            Unit3::unchecked ([resolve_x.signum(), 0.0, 0.0].into())
543          )
544        } else if resolve_y_abs <= resolve_x_abs && resolve_y_abs <= resolve_z_abs {
545          ( -resolve_y_abs,
546            0.5 * vector3 (0.0, resolve_y, 0.0),
547            Unit3::unchecked ([0.0, resolve_y.signum(), 0.0].into())
548          )
549        } else {
550          debug_assert!(resolve_z_abs <= resolve_x_abs);
551          debug_assert!(resolve_z_abs <= resolve_y_abs);
552          ( -resolve_z_abs,
553            0.5 * vector3 (0.0, 0.0, resolve_z),
554            Unit3::unchecked ([0.0, 0.0, resolve_z.signum()].into())
555          )
556        };
557        debug_assert!(!half_axis.is_zero());
558        Proximity { distance, half_axis, midpoint, normal }
559      }
560    }
561  }
562
563  /*
564  /// Uses pre-computed sigvec normals with array lookup.
565  ///
566  /// This benchmarked 2-3ns slower than the version using `normalize`.
567  pub fn query_cuboid_cuboid_array (
568    position_a : &Point3 <f64>, cuboid_a : &shape::Cuboid <f64>,
569    position_b : &Point3 <f64>, cuboid_b : &shape::Cuboid <f64>
570  ) -> Self {
571    use {EuclideanSpace, InnerSpace, Zero};
572
573    fn nearest_ab_to_distance (
574      nearest_a      : &Point3  <f64>,
575      nearest_b      : &Point3  <f64>,
576      default_normal : &Vector3 <f64>
577    ) -> Proximity {
578      let axis      = nearest_b - nearest_a;
579      let distance  = axis.magnitude();
580      let half_axis = 0.5 * (nearest_b - nearest_a);
581      let midpoint  = nearest_a + half_axis;
582      let normal    = if axis.is_zero() {
583        *default_normal
584      } else {
585        -axis / distance
586      };
587      Proximity { distance, half_axis, midpoint, normal }
588    }
589
590    let max_a = position_a + cuboid_a.max().0;
591    let min_a = position_a + cuboid_a.min().0;
592    let max_b = position_b + cuboid_b.max().0;
593    let min_b = position_b + cuboid_b.min().0;
594    let overlap_x = min_b.0.x < max_a.0.x && min_a.0.x < max_b.0.x;
595    let overlap_y = min_b.0.y < max_a.0.y && min_a.0.y < max_b.0.y;
596    let overlap_z = min_b.0.z < max_a.0.z && min_a.0.z < max_b.0.z;
597    match (overlap_x, overlap_y, overlap_z) {
598      (false, false, false) => {
599        use ElementWise;
600        // no overlaps: nearest features are corners
601        let a_to_b = position_b - position_a;
602        debug_assert!(!a_to_b.x.is_zero());
603        debug_assert!(!a_to_b.y.is_zero());
604        debug_assert!(!a_to_b.z.is_zero());
605        let a_to_b_sigvec = a_to_b.map (f64::signum);
606        let nearest_a = position_a +
607          cuboid_a.half_extents().mul_element_wise (a_to_b_sigvec);
608        let nearest_b = position_b +
609          cuboid_b.half_extents().mul_element_wise (-a_to_b_sigvec);
610        nearest_ab_to_distance (&nearest_a, &nearest_b,
611          &-sigvec_unit_f64_unchecked (a_to_b_sigvec))
612      }
613      (true, false, false)  => {
614        // overlap x only: nearest features are edges
615        let max_x  = f64::min (max_a.0.x, max_b.0.x);
616        let min_x  = f64::max (min_a.0.x, min_b.0.x);
617        debug_assert_ne!(min_x, max_x);
618        let mid_x  = 0.5 * (min_x + max_x);
619        let a_to_b = position_b - position_a;
620        let a_to_b_yz = vector3 (0.0, a_to_b.y, a_to_b.z);
621        let a_to_b_yz_sigvec = sigvec (a_to_b_yz);
622        debug_assert!(a_to_b_yz_sigvec.x.is_zero());
623        debug_assert!(!a_to_b_yz_sigvec.y.is_zero());
624        debug_assert!(!a_to_b_yz_sigvec.z.is_zero());
625        let nearest_a = {
626          let y = cuboid_a.extents.y * a_to_b_yz_sigvec.y;
627          let z = cuboid_a.extents.z * a_to_b_yz_sigvec.z;
628          [mid_x, position_a.0.y + y, position_a.0.z + z].into()
629        };
630        let nearest_b = {
631          let y = -cuboid_b.extents.y * a_to_b_yz_sigvec.y;
632          let z = -cuboid_b.extents.z * a_to_b_yz_sigvec.z;
633          [mid_x, position_b.0.y + y, position_b.0.z + z].into()
634        };
635        nearest_ab_to_distance (&nearest_a, &nearest_b,
636          &-sigvec_unit_f64_unchecked (a_to_b_yz_sigvec))
637      }
638      (false, true, false)  => {
639        // overlap y only: nearest features are edges
640        let max_y  = f64::min (max_a.0.y, max_b.0.y);
641        let min_y  = f64::max (min_a.0.y, min_b.0.y);
642        debug_assert_ne!(min_y, max_y);
643        let mid_y  = 0.5 * (min_y + max_y);
644        let a_to_b = position_b - position_a;
645        let a_to_b_xz = vector3 (a_to_b.x, 0.0, a_to_b.z);
646        let a_to_b_xz_sigvec = sigvec (a_to_b_xz);
647        debug_assert!(!a_to_b_xz_sigvec.x.is_zero());
648        debug_assert!(a_to_b_xz_sigvec.y.is_zero());
649        debug_assert!(!a_to_b_xz_sigvec.z.is_zero());
650        let nearest_a = {
651          let x = cuboid_a.extents.x * a_to_b_xz_sigvec.x;
652          let z = cuboid_a.extents.z * a_to_b_xz_sigvec.z;
653          [position_a.0.x + x, mid_y, position_a.0.z + z].into()
654        };
655        let nearest_b = {
656          let x = -cuboid_b.extents.x * a_to_b_xz_sigvec.x;
657          let z = -cuboid_b.extents.z * a_to_b_xz_sigvec.z;
658          [position_b.0.x + x, mid_y, position_b.0.z + z].into()
659        };
660        nearest_ab_to_distance (&nearest_a, &nearest_b,
661          &-sigvec_unit_f64_unchecked (a_to_b_xz_sigvec))
662      }
663      (false, false, true)  => {
664        // overlap z only: nearest features are edges
665        let max_z  = f64::min (max_a.0.z, max_b.0.z);
666        let min_z  = f64::max (min_a.0.z, min_b.0.z);
667        debug_assert_ne!(min_z, max_z);
668        let mid_z  = 0.5 * (min_z + max_z);
669        let a_to_b = position_b - position_a;
670        let a_to_b_xy = vector3 (a_to_b.x, a_to_b.y, 0.0);
671        let a_to_b_xy_sigvec = sigvec (a_to_b_xy);
672        debug_assert!(!a_to_b_xy_sigvec.x.is_zero());
673        debug_assert!(!a_to_b_xy_sigvec.y.is_zero());
674        debug_assert!(a_to_b_xy_sigvec.z.is_zero());
675        let nearest_a = {
676          let x = cuboid_a.extents.x * a_to_b_xy_sigvec.x;
677          let y = cuboid_a.extents.y * a_to_b_xy_sigvec.y;
678          [position_a.0.x + x, position_a.0.y + y, mid_z].into()
679        };
680        let nearest_b = {
681          let x = -cuboid_b.extents.x * a_to_b_xy_sigvec.x;
682          let y = -cuboid_b.extents.y * a_to_b_xy_sigvec.y;
683          [position_b.0.x + x, position_b.0.y + y, mid_z].into()
684        };
685        nearest_ab_to_distance (&nearest_a, &nearest_b,
686          &-sigvec_unit_f64_unchecked (a_to_b_xy_sigvec))
687      }
688      (true, true, false)   => {
689        // overlap x and y: nearest features are faces
690        let max_x    = f64::min (max_a.0.x, max_b.0.x);
691        let min_x    = f64::max (min_a.0.x, min_b.0.x);
692        let mid_x    = 0.5 * (min_x + max_x);
693        let max_y    = f64::min (max_a.0.y, max_b.0.y);
694        let min_y    = f64::max (min_a.0.y, min_b.0.y);
695        let mid_y    = 0.5 * (min_y + max_y);
696        let a_to_b_z = position_b.0.z - position_a.0.z;
697        let a_to_b_signum_z = a_to_b_z.signum();
698        debug_assert_ne!(a_to_b_signum_z, 0.0);
699        let nearest_a = point3 (
700          mid_x, mid_y,
701          position_a.0.z + a_to_b_signum_z * cuboid_a.extents.z
702        );
703        let nearest_b = point3 (
704          mid_x, mid_y,
705          position_b.0.z - a_to_b_signum_z * cuboid_b.extents.z
706        );
707        let nearest_a_to_b_z = nearest_b.0.z - nearest_a.0.z;
708        let distance  = nearest_a_to_b_z.abs();
709        let half_axis = [0.0, 0.0, 0.5 * nearest_a_to_b_z].into();
710        let normal    = [0.0, 0.0, -a_to_b_signum_z].into();
711        let midpoint  = nearest_a + half_axis;
712        Proximity { distance, half_axis, midpoint, normal }
713      }
714      (true, false, true)   => {
715        // overlap x and z: nearest features are faces
716        let max_x    = f64::min (max_a.0.x, max_b.0.x);
717        let min_x    = f64::max (min_a.0.x, min_b.0.x);
718        let mid_x    = 0.5 * (min_x + max_x);
719        let max_z    = f64::min (max_a.0.z, max_b.0.z);
720        let min_z    = f64::max (min_a.0.z, min_b.0.z);
721        let mid_z    = 0.5 * (min_z + max_z);
722        let a_to_b_y = position_b.0.y - position_a.0.y;
723        let a_to_b_signum_y = a_to_b_y.signum();
724        debug_assert_ne!(a_to_b_signum_y, 0.0);
725        let nearest_a = point3 (
726          mid_x,
727          position_a.0.y + a_to_b_signum_y * cuboid_a.extents.y,
728          mid_z
729        );
730        let nearest_b = point3 (
731          mid_x,
732          position_b.0.y - a_to_b_signum_y * cuboid_b.extents.y,
733          mid_z
734        );
735        let nearest_a_to_b_y = nearest_b.0.y - nearest_a.0.y;
736        let distance  = nearest_a_to_b_y.abs();
737        let half_axis = [0.0, 0.5 * nearest_a_to_b_y, 0.0].into();
738        let normal    = [0.0, -a_to_b_signum_y, 0.0].into();
739        let midpoint  = nearest_a + half_axis;
740        Proximity { distance, half_axis, midpoint, normal }
741      }
742      (false, true, true)   => {
743        // overlap y and z: nearest features are faces
744        let max_y    = f64::min (max_a.0.y, max_b.0.y);
745        let min_y    = f64::max (min_a.0.y, min_b.0.y);
746        let mid_y    = 0.5 * (min_y + max_y);
747        let max_z    = f64::min (max_a.0.z, max_b.0.z);
748        let min_z    = f64::max (min_a.0.z, min_b.0.z);
749        let mid_z    = 0.5 * (min_z + max_z);
750        let a_to_b_x = position_b.0.x - position_a.0.x;
751        let a_to_b_signum_x = a_to_b_x.signum();
752        debug_assert_ne!(a_to_b_signum_x, 0.0);
753        let nearest_a = point3 (
754          position_a.0.x + a_to_b_signum_x * cuboid_a.extents.x,
755          mid_y,
756          mid_z
757        );
758        let nearest_b = point3 (
759          position_b.0.x - a_to_b_signum_x * cuboid_b.extents.x,
760          mid_y,
761          mid_z
762        );
763        let nearest_a_to_b_x = nearest_b.0.x - nearest_a.0.x;
764        let distance  = nearest_a_to_b_x.abs();
765        let half_axis = [0.5 * nearest_a_to_b_x, 0.0, 0.0].into();
766        let normal    = [-a_to_b_signum_x, 0.0, 0.0].into();
767        let midpoint  = nearest_a + half_axis;
768        Proximity { distance, half_axis, midpoint, normal }
769      }
770      (true, true, true)    => {
771        // overlap on all axes: intersection
772        let max_x     = f64::min (max_a.0.x, max_b.0.x);
773        let min_x     = f64::max (min_a.0.x, min_b.0.x);
774        let mid_x     = 0.5 * (min_x + max_x);
775        let max_y     = f64::min (max_a.0.y, max_b.0.y);
776        let min_y     = f64::max (min_a.0.y, min_b.0.y);
777        let mid_y     = 0.5 * (min_y + max_y);
778        let max_z     = f64::min (max_a.0.z, max_b.0.z);
779        let min_z     = f64::max (min_a.0.z, min_b.0.z);
780        let mid_z     = 0.5 * (min_z + max_z);
781        let midpoint  = [mid_x, mid_y, mid_z].into();
782        let resolve_x = if position_a.0.x < position_b.0.x {
783          min_b.0.x - max_a.0.x
784        } else {
785          debug_assert!(position_a.0.x >= position_b.0.x);
786          max_b.0.x - min_a.0.x
787        };
788        let resolve_y = if position_a.0.y < position_b.0.y {
789          min_b.0.y - max_a.0.y
790        } else {
791          debug_assert!(position_a.0.y >= position_b.0.y);
792          max_b.0.y - min_a.0.y
793        };
794        let resolve_z = if position_a.0.z < position_b.0.z {
795          min_b.0.z - max_a.0.z
796        } else {
797          debug_assert!(position_a.0.z >= position_b.0.z);
798          max_b.0.z - min_a.0.z
799        };
800        let resolve_x_abs = resolve_x.abs();
801        let resolve_y_abs = resolve_y.abs();
802        let resolve_z_abs = resolve_z.abs();
803        let (distance, half_axis, normal) : (
804          f64, Vector3 <f64>, Vector3 <f64>
805        ) = if resolve_x_abs <= resolve_y_abs && resolve_x_abs <= resolve_z_abs {
806          ( -resolve_x_abs,
807            0.5 * vector3 (resolve_x, 0.0, 0.0),
808            [resolve_x.signum(), 0.0, 0.0].into()
809          )
810        } else if
811          resolve_y_abs <= resolve_x_abs && resolve_y_abs <= resolve_z_abs
812        {
813          ( -resolve_y_abs,
814            0.5 * vector3 (0.0, resolve_y, 0.0),
815            [0.0, resolve_y.signum(), 0.0].into()
816          )
817        } else {
818          debug_assert!(resolve_z_abs <= resolve_x_abs);
819          debug_assert!(resolve_z_abs <= resolve_y_abs);
820          ( -resolve_z_abs,
821            0.5 * vector3 (0.0, 0.0, resolve_z),
822            [0.0, 0.0, resolve_z.signum()].into()
823          )
824        };
825        debug_assert!(!half_axis.is_zero());
826        Proximity { distance, half_axis, midpoint, normal }
827      }
828    }
829  } // end query_cuboid_cuboid_array
830
831  /// This is a version of the cuboid distance query with the three edge
832  /// cases factored out into a function taking vector indices. This turns out
833  /// to be slower in benchmarks (190ns vs. the 180ns inline versions above).
834  pub fn query_cuboid_cuboid_refactor (
835    position_a : &Point3 <f64>, cuboid_a : &shape::Cuboid <f64>,
836    position_b : &Point3 <f64>, cuboid_b : &shape::Cuboid <f64>
837  ) -> Self {
838    use {EuclideanSpace, InnerSpace, Zero};
839
840    #[inline]
841    fn distance_edge (
842      edge : usize, other1 : usize, other2 : usize,
843      position_a : &Point3 <f64>, cuboid_a : &shape::Cuboid <f64>,
844      position_b : &Point3 <f64>, cuboid_b : &shape::Cuboid <f64>,
845      max_a      : &Point3 <f64>, min_a    : &Point3 <f64>,
846      max_b      : &Point3 <f64>, min_b    : &Point3 <f64>
847    ) -> Proximity {
848      let max_edge  = f64::min (max_a[edge], max_b[edge]);
849      let min_edge  = f64::max (min_a[edge], min_b[edge]);
850      debug_assert_ne!(min_edge, max_edge);
851      let mid_edge  = 0.5 * (min_edge + max_edge);
852      let a_to_b = position_b - position_a;
853      let a_to_b_others = {
854        let mut a_to_b_others = a_to_b;
855        a_to_b_others[edge] = 0.0;
856        a_to_b_others
857      };
858      let a_to_b_others_signum = a_to_b_others.map (signum_or_zero);
859      debug_assert!(a_to_b_others_signum[edge].is_zero());
860      debug_assert!(!a_to_b_others_signum[other1].is_zero());
861      debug_assert!(!a_to_b_others_signum[other2].is_zero());
862      let nearest_a = {
863        let other_component1 =
864          cuboid_a.half_extents()[other1] * a_to_b_others_signum[other1];
865        let other_component2 =
866          cuboid_a.half_extents()[other2] * a_to_b_others_signum[other2];
867        let mut nearest_a = [0.0; 3];
868        nearest_a[edge]   = mid_edge;
869        nearest_a[other1] = position_a[other1] + other_component1;
870        nearest_a[other2] = position_a[other2] + other_component2;
871        nearest_a.into()
872      };
873      let nearest_b = {
874        let other_component1 =
875          -cuboid_b.half_extents()[other1] * a_to_b_others_signum[other1];
876        let other_component2 =
877          -cuboid_b.half_extents()[other2] * a_to_b_others_signum[other2];
878        let mut nearest_b = [0.0; 3];
879        nearest_b[edge]   = mid_edge;
880        nearest_b[other1] = position_b[other1] + other_component1;
881        nearest_b[other2] = position_b[other2] + other_component2;
882        nearest_b.into()
883      };
884      nearest_ab_to_distance (&nearest_a, &nearest_b, &a_to_b_others_signum)
885    }
886
887    fn nearest_ab_to_distance (
888      nearest_a     : &Point3 <f64>,
889      nearest_b     : &Point3 <f64>,
890      a_to_b_signum : &Vector3 <f64>
891    ) -> Proximity {
892      let axis      = nearest_b - nearest_a;
893      let distance  = axis.magnitude();
894      let half_axis = 0.5 * (nearest_b - nearest_a);
895      let midpoint  = nearest_a + half_axis;
896      let normal    = if axis.is_zero() {
897        -a_to_b_signum.normalize()
898      } else {
899        -axis / distance
900      };
901      Proximity { distance, half_axis, midpoint, normal }
902    }
903
904    let max_a = position_a + cuboid_a.max().0;
905    let min_a = position_a + cuboid_a.min().0;
906    let max_b = position_b + cuboid_b.max().0;
907    let min_b = position_b + cuboid_b.min().0;
908
909    let overlap_x = min_b.0.x < max_a.0.x && min_a.0.x < max_b.0.x;
910    let overlap_y = min_b.0.y < max_a.0.y && min_a.0.y < max_b.0.y;
911    let overlap_z = min_b.0.z < max_a.0.z && min_a.0.z < max_b.0.z;
912    match (overlap_x, overlap_y, overlap_z) {
913      (false, false, false) => {
914        use ElementWise;
915        // no overlaps: nearest features are corners
916        let a_to_b = position_b - position_a;
917        debug_assert!(!a_to_b.x.is_zero());
918        debug_assert!(!a_to_b.y.is_zero());
919        debug_assert!(!a_to_b.z.is_zero());
920        let a_to_b_signum = a_to_b.map (f64::signum);
921        let nearest_a = position_a +
922          cuboid_a.half_extents().mul_element_wise (a_to_b_signum);
923        let nearest_b = position_b +
924          cuboid_b.half_extents().mul_element_wise (-a_to_b_signum);
925        nearest_ab_to_distance (&nearest_a, &nearest_b, &a_to_b_signum)
926      }
927      (true, false, false)  => {
928        // overlap x only: nearest features are edges
929        distance_edge (0, 1, 2,
930          position_a, cuboid_a, position_b, cuboid_b,
931          &max_a, &min_a, &max_b, &min_b
932        )
933      }
934      (false, true, false)  => {
935        // overlap y only: nearest features are edges
936        distance_edge (1, 0, 2,
937          position_a, cuboid_a, position_b, cuboid_b,
938          &max_a, &min_a, &max_b, &min_b
939        )
940      }
941      (false, false, true)  => {
942        // overlap z only: nearest features are edges
943        distance_edge (2, 1, 2,
944          position_a, cuboid_a, position_b, cuboid_b,
945          &max_a, &min_a, &max_b, &min_b
946        )
947      }
948      (true, true, false)   => {
949        // overlap x and y: nearest features are faces
950        unimplemented!()
951      }
952      (true, false, true)   => {
953        // overlap x and z: nearest features are faces
954        unimplemented!()
955      }
956      (false, true, true)   => {
957        // overlap y and z: nearest features are faces
958        unimplemented!()
959      }
960      (true, true, true)    => {
961        // overlap on all axes: intersection
962        unimplemented!()
963      }
964    }
965  } // end query_cuboid_cuboid_refactor
966  */
967
968  pub fn query_sphere_capsule (
969    position_a : &Point3 <f64>, sphere_a  : &shape::Sphere <f64>,
970    position_b : &Point3 <f64>, capsule_b : &shape::Capsule <f64>
971  ) -> Self {
972    use num::Zero;
973    let half_height_b = *capsule_b.half_height;
974    let (radius_a, radius_b) = (*sphere_a.radius, *capsule_b.radius);
975    let shaft_max_b = position_b + vector3 (0.0, 0.0, half_height_b);
976    let shaft_min_b = position_b - vector3 (0.0, 0.0, half_height_b);
977    let distance_normal_half_axis = |axis : &Vector3 <f64>| {
978      let axis_distance = axis.magnitude();
979      let distance      = axis_distance - radius_a - radius_b;
980      let normal        = if axis.is_zero() {
981        Unit3::axis_x()
982      } else {
983        Unit3::unchecked_approx (-(axis / axis_distance))
984      };
985      let half_axis     = -0.5 * distance * *normal;
986      (distance, normal, half_axis)
987    };
988    if position_a.0.z <= shaft_min_b.0.z {
989      // cylinder shaft of B is above center of A
990      let axis     = shaft_min_b - position_a;
991      let (distance, normal, half_axis) = distance_normal_half_axis (&axis);
992      let midpoint = position_a - radius_a * *normal + half_axis;
993      Proximity { distance, half_axis, midpoint, normal }
994    } else if shaft_max_b.0.z <= position_a.0.z {
995      // cylinder shaft of B is below center of A
996      let axis     = shaft_max_b - position_a;
997      let (distance, normal, half_axis) = distance_normal_half_axis (&axis);
998      let midpoint = position_a - radius_a * *normal + half_axis;
999      Proximity { distance, half_axis, midpoint, normal }
1000    } else {
1001      // cylinder shaft of B is adjacent to center of A
1002      let axis     =
1003        Point3::from ([position_b.0.x, position_b.0.y, 0.0]) -
1004        Point3::from ([position_a.0.x, position_a.0.y, 0.0]);
1005      let (distance, normal, half_axis) = distance_normal_half_axis (&axis);
1006      let midpoint = {
1007        let max_z  = f64::min (position_a.0.z, shaft_max_b.0.z);
1008        let min_z  = f64::max (position_a.0.z, shaft_min_b.0.z);
1009        let mid_z  = 0.5 * (max_z + min_z);
1010        let mid_a  = position_a - radius_a * *normal + half_axis;
1011        [mid_a.0.x, mid_a.0.y, mid_z].into()
1012      };
1013      Proximity { distance, half_axis, midpoint, normal }
1014    }
1015  }
1016
1017  pub fn query_sphere_cuboid (
1018    position_a : &Point3 <f64>, sphere_a : &shape::Sphere <f64>,
1019    position_b : &Point3 <f64>, cuboid_b : &shape::Cuboid <f64>
1020  ) -> Self {
1021    use num::Zero;
1022    let cuboid_max_b = position_b + cuboid_b.max().0;
1023    let cuboid_min_b = position_b - cuboid_b.max().0;
1024
1025    let overlap_x = cuboid_min_b.0.x < position_a.0.x &&
1026      position_a.0.x < cuboid_max_b.0.x;
1027    let overlap_y = cuboid_min_b.0.y < position_a.0.y &&
1028      position_a.0.y < cuboid_max_b.0.y;
1029
1030    let sphere_a_radius   = *sphere_a.radius;
1031    let cuboid_b_extent_x = *cuboid_b.extents.x;
1032    let cuboid_b_extent_y = *cuboid_b.extents.y;
1033    let cuboid_b_extent_z = *cuboid_b.extents.z;
1034    if position_a.0.z >= cuboid_max_b.0.z {
1035      // sphere above cuboid
1036      match (overlap_x, overlap_y) {
1037        (false, false) => {
1038          // nearest/deepest point is a corner of the cuboid
1039          let b_to_a        = position_a - position_b;
1040          debug_assert!(!b_to_a.x.is_zero());
1041          debug_assert!(!b_to_a.y.is_zero());
1042          debug_assert!(!b_to_a.z.is_zero());
1043          let b_to_a_sigvec = b_to_a.map (f64::signum);
1044          let nearest_b     = position_b + cuboid_b.max().0 * b_to_a_sigvec;
1045          let a_to_b        = nearest_b - position_a;
1046          let nearest_a     = position_a + if !a_to_b.is_zero() {
1047            sphere_a_radius * a_to_b.normalized()
1048          } else {
1049            [-b_to_a_sigvec.x * sphere_a_radius, 0.0, 0.0].into()
1050          };
1051          let axis          = nearest_b - nearest_a;
1052          let distance      = axis.magnitude() * if !a_to_b.is_zero() {
1053            a_to_b.dot (axis).signum()
1054          } else {
1055            -1.0
1056          };
1057          let half_axis     = 0.5 * axis;
1058          let normal        = if axis.is_zero() {
1059            Unit3::normalize (b_to_a_sigvec)
1060          } else {
1061            Unit3::unchecked_approx (-axis / distance)
1062          };
1063          let midpoint      = nearest_a + half_axis;
1064          Proximity { distance, half_axis, midpoint, normal }
1065        }
1066        (true, false) => {
1067          // nearest/deepest point is on an edge parallel to the X axis
1068          let signum_y  = (position_a.0.y - position_b.0.y).signum();
1069          let nearest_b = point3 (
1070            position_a.0.x,
1071            //position_b.0.y + signum_y * cuboid_b_extent_y,
1072            signum_y.mul_add (cuboid_b_extent_y, position_b.0.y),
1073            cuboid_max_b.0.z
1074          );
1075          let a_to_b    = nearest_b - position_a;
1076          let nearest_a = position_a + if a_to_b.is_zero() {
1077            -signum_y * vector3 (0.0, sphere_a_radius, 0.0)
1078          } else {
1079            sphere_a_radius * a_to_b.normalized()
1080          };
1081          let axis      = nearest_b - nearest_a;
1082          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1083            a_to_b.dot (axis).signum()
1084          } else {
1085            -1.0
1086          };
1087          let half_axis = 0.5 * axis;
1088          let normal    = if axis.is_zero() {
1089            Unit3::normalize (vector3 (0.0, signum_y, 1.0))
1090          } else {
1091            Unit3::unchecked_approx (-axis / distance)
1092          };
1093          let midpoint  = nearest_a + half_axis;
1094          Proximity { distance, half_axis, midpoint, normal }
1095        }
1096        (false, true) => {
1097          // nearest/deepest point is on an edge parallel to the Y axis
1098          let signum_x  = (position_a.0.x - position_b.0.x).signum();
1099          let nearest_b = point3 (
1100            //position_b.0.x + signum_x * cuboid_b_extent_x,
1101            signum_x.mul_add (cuboid_b_extent_x, position_b.0.x),
1102            position_a.0.y,
1103            cuboid_max_b.0.z
1104          );
1105          let a_to_b    = nearest_b - position_a;
1106          let nearest_a = position_a + if a_to_b.is_zero() {
1107            -signum_x * vector3 (sphere_a_radius, 0.0, 0.0)
1108          } else {
1109            sphere_a_radius * a_to_b.normalized()
1110          };
1111          let axis      = nearest_b - nearest_a;
1112          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1113            a_to_b.dot (axis).signum()
1114          } else {
1115            -1.0
1116          };
1117          let half_axis = 0.5 * axis;
1118          let normal    = if axis.is_zero() {
1119            Unit3::normalize (vector3 (signum_x, 0.0, 1.0))
1120          } else {
1121            Unit3::unchecked_approx (-axis / distance)
1122          };
1123          let midpoint  = nearest_a + half_axis;
1124          Proximity { distance, half_axis, midpoint, normal }
1125        }
1126        (true, true) => {
1127          // nearest/deepest point is in the -Z direction
1128          let nearest_a : Point3 <f64> =
1129            position_a - vector3 (0.0, 0.0, sphere_a_radius);
1130          let nearest_b : Point3 <f64> =
1131            [position_a.0.x, position_a.0.y, cuboid_max_b.0.z].into();
1132          let axis      = nearest_b - nearest_a;
1133          let normal    = Unit3::axis_z();
1134          let half_axis = 0.5 * axis;
1135          let distance  = nearest_a.0.z - nearest_b.0.z;
1136          let midpoint  = nearest_a + half_axis;
1137          Proximity { distance, half_axis, midpoint, normal }
1138        }
1139      }
1140    } else if position_a.0.z <= cuboid_min_b.0.z {
1141      // sphere below cuboid
1142      match (overlap_x, overlap_y) {
1143        (false, false) => {
1144          // nearest/deepest point is a corner of the cuboid
1145          let b_to_a        = position_a - position_b;
1146          debug_assert!(!b_to_a.x.is_zero());
1147          debug_assert!(!b_to_a.y.is_zero());
1148          debug_assert!(!b_to_a.z.is_zero());
1149          let b_to_a_sigvec = b_to_a.map (f64::signum);
1150          let nearest_b     = position_b + cuboid_b.max().0 * b_to_a_sigvec;
1151          let a_to_b        = nearest_b - position_a;
1152          let nearest_a     = position_a + if !a_to_b.is_zero() {
1153            sphere_a_radius * a_to_b.normalized()
1154          } else {
1155            [-b_to_a_sigvec.x * sphere_a_radius, 0.0, 0.0].into()
1156          };
1157          let axis          = nearest_b - nearest_a;
1158          let distance      = axis.magnitude() * if !a_to_b.is_zero() {
1159            a_to_b.dot (axis).signum()
1160          } else {
1161            -1.0
1162          };
1163          let half_axis     = 0.5 * axis;
1164          let normal        = if axis.is_zero() {
1165            Unit3::normalize (b_to_a_sigvec)
1166          } else {
1167            Unit3::unchecked_approx (-axis / distance)
1168          };
1169          let midpoint      = nearest_a + half_axis;
1170          Proximity { distance, half_axis, midpoint, normal }
1171        }
1172        (true, false) => {
1173          // nearest/deepest point is on an edge parallel to the X axis
1174          let signum_y  = (position_a.0.y - position_b.0.y).signum();
1175          let nearest_b = point3 (
1176            position_a.0.x,
1177            //position_b.0.y + signum_y * cuboid_b_extent_y,
1178            signum_y.mul_add (cuboid_b_extent_y, position_b.0.y),
1179            cuboid_min_b.0.z
1180          );
1181          let a_to_b    = nearest_b - position_a;
1182          let nearest_a = position_a + if a_to_b.is_zero() {
1183            -signum_y * vector3 (0.0, sphere_a_radius, 0.0)
1184          } else {
1185            sphere_a_radius * a_to_b.normalized()
1186          };
1187          let axis      = nearest_b - nearest_a;
1188          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1189            a_to_b.dot (axis).signum()
1190          } else {
1191            -1.0
1192          };
1193          let half_axis = 0.5 * axis;
1194          let normal    = if axis.is_zero() {
1195            Unit3::normalize (vector3 (0.0, signum_y, -1.0))
1196          } else {
1197            Unit3::unchecked_approx (-axis / distance)
1198          };
1199          let midpoint  = nearest_a + half_axis;
1200          Proximity { distance, half_axis, midpoint, normal }
1201        }
1202        (false, true) => {
1203          // nearest/deepest point is on an edge parallel to the Y axis
1204          let signum_x  = (position_a.0.x - position_b.0.x).signum();
1205          let nearest_b = point3 (
1206            //position_b.0.x + signum_x * cuboid_b_extent_x,
1207            signum_x.mul_add (cuboid_b_extent_x, position_b.0.x),
1208            position_a.0.y,
1209            cuboid_min_b.0.z
1210          );
1211          let a_to_b    = nearest_b - position_a;
1212          let nearest_a = position_a + if a_to_b.is_zero() {
1213            -signum_x * vector3 (sphere_a_radius, 0.0, 0.0)
1214          } else {
1215            sphere_a_radius * a_to_b.normalized()
1216          };
1217          let axis      = nearest_b - nearest_a;
1218          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1219            a_to_b.dot (axis).signum()
1220          } else {
1221            -1.0
1222          };
1223          let half_axis = 0.5 * axis;
1224          let normal    = if axis.is_zero() {
1225            Unit3::normalize (vector3 (signum_x, 0.0, -1.0))
1226          } else {
1227            Unit3::unchecked_approx (-axis / distance)
1228          };
1229          let midpoint  = nearest_a + half_axis;
1230          Proximity { distance, half_axis, midpoint, normal }
1231        }
1232        (true, true) => {
1233          // nearest/deepest point is in the -Z direction
1234          let nearest_a : Point3 <f64> =
1235            position_a + vector3 (0.0, 0.0, sphere_a_radius);
1236          let nearest_b : Point3 <f64> =
1237            [position_a.0.x, position_a.0.y, cuboid_min_b.0.z].into();
1238          let axis      = nearest_b - nearest_a;
1239          let normal    = Unit3::axis_z().invert();
1240          let half_axis = 0.5 * axis;
1241          let distance  = nearest_b.0.z - nearest_a.0.z;
1242          let midpoint  = nearest_a + half_axis;
1243          Proximity { distance, half_axis, midpoint, normal }
1244        }
1245      }
1246    } else {
1247      // sphere center overlaps on z axis
1248      match (overlap_x, overlap_y) {
1249        (false, false) => {
1250          // nearest point is on a Z parallel edge of the cuboid
1251          let b_to_a_signum_x = (position_a.0.x - position_b.0.x).signum();
1252          let b_to_a_signum_y = (position_a.0.y - position_b.0.y).signum();
1253          let b_to_a_unit_sigvec = vector3 (b_to_a_signum_x, b_to_a_signum_y, 0.0)
1254            .normalized();
1255          let nearest_b = Point3::from ([
1256            //position_b.0.x + b_to_a_signum_x * cuboid_b_extent_x,
1257            b_to_a_signum_x.mul_add (cuboid_b_extent_x, position_b.0.x),
1258            //position_b.0.y + b_to_a_signum_y * cuboid_b_extent_y,
1259            b_to_a_signum_y.mul_add (cuboid_b_extent_y, position_b.0.y),
1260            position_a.0.z
1261          ]);
1262          let a_to_b    = vector3 (
1263            nearest_b.0.x - position_a.0.x,
1264            nearest_b.0.y - position_a.0.y,
1265            0.0
1266          );
1267          let nearest_a = point3 (position_a.0.x, position_a.0.y, position_a.0.z)
1268            + sphere_a_radius * if !a_to_b.is_zero() {
1269              a_to_b.normalized()
1270            } else {
1271              -b_to_a_unit_sigvec
1272            };
1273          let axis      = nearest_b - nearest_a;
1274          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1275            a_to_b.dot (axis).signum()
1276          } else {
1277            -1.0
1278          };
1279          let half_axis = 0.5 * axis;
1280          let normal    = Unit3::unchecked_approx (if axis.is_zero() {
1281            b_to_a_unit_sigvec
1282          } else {
1283            -axis / distance
1284          });
1285          let midpoint  = nearest_a + half_axis;
1286          Proximity { distance, half_axis, midpoint, normal }
1287        }
1288        (true, false)  => {
1289          // nearest point is on a Z/X parallel face of the cuboid
1290          let b_to_a_signum_y    = (position_a.0.y - position_b.0.y).signum();
1291          let b_to_a_unit_sigvec = vector3 (0.0, b_to_a_signum_y, 0.0);
1292          let nearest_b = Point3::from ([
1293            position_a.0.x,
1294            //position_b.0.y + b_to_a_signum_y * cuboid_b_extent_y,
1295            b_to_a_signum_y.mul_add (cuboid_b_extent_y, position_b.0.y),
1296            position_a.0.z
1297          ]);
1298          let a_to_b    = vector3 (0.0, nearest_b.0.y - position_a.0.y, 0.0);
1299          let nearest_a =
1300            point3 (position_a.0.x, position_a.0.y, position_a.0.z) +
1301            vector3 (0.0, -b_to_a_signum_y * sphere_a_radius, 0.0);
1302          let axis      = nearest_b - nearest_a;
1303          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1304            a_to_b.dot (axis).signum()
1305          } else {
1306            -1.0
1307          };
1308          let half_axis = 0.5 * axis;
1309          let normal    = Unit3::unchecked_approx (if axis.is_zero() {
1310            b_to_a_unit_sigvec
1311          } else {
1312            -axis / distance
1313          });
1314          let midpoint  = nearest_a + half_axis;
1315          Proximity { distance, half_axis, midpoint, normal }
1316        }
1317        (false, true)  => {
1318          // nearest point is on a Z/Y parallel face of the cuboid
1319          let b_to_a_signum_x    = (position_a.0.x - position_b.0.x).signum();
1320          let b_to_a_unit_sigvec = vector3 (b_to_a_signum_x, 0.0, 0.0);
1321          let nearest_b = Point3::from ([
1322            //position_b.0.x + b_to_a_signum_x * cuboid_b_extent_x,
1323            b_to_a_signum_x.mul_add (cuboid_b_extent_x, position_b.0.x),
1324            position_a.0.y,
1325            position_a.0.z
1326          ]);
1327          let a_to_b    = vector3 (nearest_b.0.x - position_a.0.x, 0.0, 0.0);
1328          let nearest_a =
1329            point3 (position_a.0.x, position_a.0.y, position_a.0.z) +
1330            vector3 (-b_to_a_signum_x * sphere_a_radius, 0.0, 0.0);
1331          let axis      = nearest_b - nearest_a;
1332          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1333            a_to_b.dot (axis).signum()
1334          } else {
1335            -1.0
1336          };
1337          let half_axis = 0.5 * axis;
1338          let normal    = Unit3::unchecked_approx (if axis.is_zero() {
1339            b_to_a_unit_sigvec
1340          } else {
1341            -axis / distance
1342          });
1343          let midpoint  = nearest_a + half_axis;
1344          Proximity { distance, half_axis, midpoint, normal }
1345        }
1346        (true, true) => {
1347          // center inside X/Y bounds
1348          if position_a == position_b {
1349            let depth_x = cuboid_b_extent_x + sphere_a_radius;
1350            let depth_y = cuboid_b_extent_y + sphere_a_radius;
1351            let depth_z = cuboid_b_extent_z + sphere_a_radius;
1352            if depth_x <= depth_y && depth_x <= depth_z {
1353              let distance  = -depth_x;
1354              let half_axis = [0.5 * depth_x, 0.0, 0.0].into();
1355              let normal    = Unit3::axis_x();
1356              let midpoint  = position_a + half_axis -
1357                vector3 (sphere_a_radius, 0.0, 0.0);
1358              Proximity { distance, half_axis, midpoint, normal }
1359            } else if depth_y <= depth_x && depth_y <= depth_z {
1360              let distance = -depth_y;
1361              let half_axis = [0.0, 0.5 * depth_y, 0.0].into();
1362              let normal    = Unit3::axis_y();
1363              let midpoint  = position_a + half_axis -
1364                vector3 (0.0, sphere_a_radius, 0.0);
1365              Proximity { distance, half_axis, midpoint, normal }
1366            } else {
1367              debug_assert!(depth_z < depth_x);
1368              debug_assert!(depth_z < depth_y);
1369              let distance = -depth_z;
1370              let half_axis = [0.0, 0.0, 0.5 * depth_z].into();
1371              let normal    = Unit3::axis_z();
1372              let midpoint  = position_a + half_axis -
1373                vector3 (0.0, 0.0, sphere_a_radius);
1374              Proximity { distance, half_axis, midpoint, normal }
1375            }
1376          } else {
1377            // NOTE: we want the following to map zero values to positive 1.0
1378            // so we use f64::signum instead of sigvec
1379            let b_to_a_sigvec = (position_a - position_b).map (f64::signum);
1380            let corner_b      = position_b + cuboid_b.max().0 * b_to_a_sigvec;
1381            let depth_x       = (corner_b.0.x - position_a.0.x).abs() + sphere_a_radius;
1382            let depth_y       = (corner_b.0.y - position_a.0.y).abs() + sphere_a_radius;
1383            let depth_z       = (corner_b.0.z - position_a.0.z).abs() + sphere_a_radius
1384              - if position_a.0.z.abs() > corner_b.0.z.abs() {
1385                2.0 * (position_a.0.z.abs() - corner_b.0.z.abs())
1386              } else {
1387                0.0
1388              };
1389
1390            if depth_x <= depth_y && depth_x <= depth_z {
1391              let distance  = -depth_x;
1392              let half_axis = [0.5 * b_to_a_sigvec.x * depth_x, 0.0, 0.0].into();
1393              let normal = Unit3::unchecked ([b_to_a_sigvec.x, 0.0, 0.0].into());
1394              let midpoint  = position_a + half_axis +
1395                vector3 (-b_to_a_sigvec.x * sphere_a_radius, 0.0, 0.0);
1396              Proximity { distance, half_axis, midpoint, normal }
1397            } else if depth_y <= depth_x && depth_y <= depth_z {
1398              let distance  = -depth_y;
1399              let half_axis = [0.0, 0.5 * b_to_a_sigvec.y * depth_y, 0.0].into();
1400              let normal = Unit3::unchecked ([0.0, b_to_a_sigvec.y, 0.0].into());
1401              let midpoint  = position_a + half_axis +
1402                vector3 (0.0, -b_to_a_sigvec.y * sphere_a_radius, 0.0);
1403              Proximity { distance, half_axis, midpoint, normal }
1404            } else {
1405              debug_assert!(depth_z < depth_x);
1406              debug_assert!(depth_z < depth_y);
1407              let nearest_a = point3 (
1408                position_a.0.x,
1409                position_a.0.y,
1410                //position_a.0.z - b_to_a_sigvec.z * sphere_a_radius
1411                b_to_a_sigvec.z.mul_add (-sphere_a_radius, position_a.0.z)
1412              );
1413              let nearest_b = point3 (
1414                position_a.0.x,
1415                position_a.0.y,
1416                //position_b.0.z + b_to_a_sigvec.z * cuboid_b_extent_z
1417                b_to_a_sigvec.z.mul_add (cuboid_b_extent_z, position_b.0.z)
1418              );
1419              let axis      = nearest_b - nearest_a;
1420              debug_assert_eq!(axis.x, 0.0);
1421              debug_assert_eq!(axis.y, 0.0);
1422              let distance  = -axis.z.abs();
1423              let half_axis = 0.5 * axis;
1424              let normal = Unit3::unchecked ([0.0, 0.0, b_to_a_sigvec.z].into());
1425              let midpoint  = nearest_a + half_axis;
1426              Proximity { distance, half_axis, midpoint, normal }
1427            }
1428          }
1429        }
1430      }
1431    }
1432  } // end query_sphere_cuboid
1433
1434  pub fn query_sphere_orthant (
1435    position_a : &Point3 <f64>, sphere_a : &shape::Sphere <f64>,
1436    position_b : &Point3 <f64>, orthant_b : &shape::Orthant
1437  ) -> Self {
1438    let radius_a    = *sphere_a.radius;
1439    let normal      = orthant_b.normal_axis.to_vec::<f64>();
1440    let normal_abs  = normal.map (f64::abs);
1441    let surface_sigvec = vector3 (1.0, 1.0, 1.0) - normal_abs;
1442    let nearest_a   = position_a - vector3 (radius_a, radius_a, radius_a) * normal;
1443    let nearest_b   =
1444      Point3 (position_a.0 * surface_sigvec + position_b.0 * normal_abs);
1445    let axis        = nearest_b - nearest_a;
1446    debug_assert_eq!(axis.sum().abs(), axis.magnitude());
1447    let distance    = -normal.dot (axis).signum() * axis.sum().abs();
1448    let half_axis   = 0.5 * axis;
1449    let midpoint    = nearest_a + half_axis;
1450    let normal      = Unit3::unchecked (normal);
1451    Proximity { distance, half_axis, midpoint, normal }
1452  } // end query_sphere_orthant
1453
1454  pub fn query_capsule_cuboid (
1455    position_a : &Point3 <f64>, capsule_a : &shape::Capsule <f64>,
1456    position_b : &Point3 <f64>, cuboid_b  : &shape::Cuboid  <f64>
1457  ) -> Self {
1458    use num::Zero;
1459    let shaft_max_a  = position_a + vector3 (0.0, 0.0, *capsule_a.half_height);
1460    let shaft_min_a  = position_a - vector3 (0.0, 0.0, *capsule_a.half_height);
1461    let cuboid_max_b = position_b + cuboid_b.max().0;
1462    let cuboid_min_b = position_b - cuboid_b.max().0;
1463
1464    let overlap_x = cuboid_min_b.0.x < position_a.0.x &&
1465      position_a.0.x < cuboid_max_b.0.x;
1466    let overlap_y = cuboid_min_b.0.y < position_a.0.y &&
1467      position_a.0.y < cuboid_max_b.0.y;
1468
1469    let capsule_a_radius      = *capsule_a.radius;
1470    let capsule_a_half_height = *capsule_a.half_height;
1471    let cuboid_b_extent_x     = *cuboid_b.extents.x;
1472    let cuboid_b_extent_y     = *cuboid_b.extents.y;
1473    let cuboid_b_extent_z     = *cuboid_b.extents.z;
1474    if shaft_min_a.0.z >= cuboid_max_b.0.z {
1475      // capsule above cuboid
1476      match (overlap_x, overlap_y) {
1477        (false, false) => {
1478          // nearest/deepest point is a corner of the cuboid
1479          let b_to_a        = shaft_min_a - position_b;
1480          debug_assert!(!b_to_a.x.is_zero());
1481          debug_assert!(!b_to_a.y.is_zero());
1482          debug_assert!(!b_to_a.z.is_zero());
1483          let b_to_a_sigvec = b_to_a.map (f64::signum);
1484          let nearest_b     = position_b + cuboid_b.max().0 * b_to_a_sigvec;
1485          let a_to_b        = nearest_b - shaft_min_a;
1486          let nearest_a     = shaft_min_a + if !a_to_b.is_zero() {
1487            capsule_a_radius * a_to_b.normalized()
1488          } else {
1489            [-b_to_a_sigvec.x * capsule_a_radius, 0.0, 0.0].into()
1490          };
1491          let axis          = nearest_b - nearest_a;
1492          let distance      = axis.magnitude() * if !a_to_b.is_zero() {
1493            a_to_b.dot (axis).signum()
1494          } else {
1495            -1.0
1496          };
1497          let half_axis     = 0.5 * axis;
1498          let normal        = if axis.is_zero() {
1499            Unit3::normalize (b_to_a_sigvec)
1500          } else {
1501            Unit3::unchecked_approx (-axis / distance)
1502          };
1503          let midpoint      = nearest_a + half_axis;
1504          Proximity { distance, half_axis, midpoint, normal }
1505        }
1506        (true, false) => {
1507          // nearest/deepest point is on an edge parallel to the X axis
1508          let signum_y  = (shaft_min_a.0.y - position_b.0.y).signum();
1509          let nearest_b = point3 (
1510            shaft_min_a.0.x,
1511            position_b.0.y + signum_y * cuboid_b_extent_y,
1512            cuboid_max_b.0.z
1513          );
1514          let a_to_b    = nearest_b - shaft_min_a;
1515          let nearest_a = shaft_min_a + if a_to_b.is_zero() {
1516            -signum_y * vector3 (0.0, capsule_a_radius, 0.0)
1517          } else {
1518            capsule_a_radius * a_to_b.normalized()
1519          };
1520          let axis      = nearest_b - nearest_a;
1521          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1522            a_to_b.dot (axis).signum()
1523          } else {
1524            -1.0
1525          };
1526          let half_axis = 0.5 * axis;
1527          let normal    = if axis.is_zero() {
1528            Unit3::normalize (vector3 (0.0, signum_y, 1.0))
1529          } else {
1530            Unit3::unchecked_approx (-axis / distance)
1531          };
1532          let midpoint  = nearest_a + half_axis;
1533          Proximity { distance, half_axis, midpoint, normal }
1534        }
1535        (false, true) => {
1536          // nearest/deepest point is on an edge parallel to the Y axis
1537          let signum_x  = (shaft_min_a.0.x - position_b.0.x).signum();
1538          let nearest_b = point3 (
1539            position_b.0.x + signum_x * cuboid_b_extent_x,
1540            shaft_min_a.0.y,
1541            cuboid_max_b.0.z
1542          );
1543          let a_to_b    = nearest_b - shaft_min_a;
1544          let nearest_a = shaft_min_a + if a_to_b.is_zero() {
1545            -signum_x * vector3 (capsule_a_radius, 0.0, 0.0)
1546          } else {
1547            capsule_a_radius * a_to_b.normalized()
1548          };
1549          let axis      = nearest_b - nearest_a;
1550          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1551            a_to_b.dot (axis).signum()
1552          } else {
1553            -1.0
1554          };
1555          let half_axis = 0.5 * axis;
1556          let normal    = if axis.is_zero() {
1557            Unit3::normalize (vector3 (signum_x, 0.0, 1.0))
1558          } else {
1559            Unit3::unchecked_approx (-axis / distance)
1560          };
1561          let midpoint  = nearest_a + half_axis;
1562          Proximity { distance, half_axis, midpoint, normal }
1563        }
1564        (true, true) => {
1565          // nearest/deepest point is in the -Z direction
1566          let nearest_a : Point3 <f64> =
1567            shaft_min_a - vector3 (0.0, 0.0, capsule_a_radius);
1568          let nearest_b : Point3 <f64> =
1569            [position_a.0.x, position_a.0.y, cuboid_max_b.0.z].into();
1570          let axis      = nearest_b - nearest_a;
1571          let normal    = Unit3::axis_z();
1572          let half_axis = 0.5 * axis;
1573          let distance  = nearest_a.0.z - nearest_b.0.z;
1574          let midpoint  = nearest_a + half_axis;
1575          Proximity { distance, half_axis, midpoint, normal }
1576        }
1577      }
1578    } else if shaft_max_a.0.z <= cuboid_min_b.0.z {
1579      // capsule below cuboid
1580      match (overlap_x, overlap_y) {
1581        (false, false) => {
1582          // nearest/deepest point is a corner of the cuboid
1583          let b_to_a        = shaft_max_a - position_b;
1584          debug_assert!(!b_to_a.x.is_zero());
1585          debug_assert!(!b_to_a.y.is_zero());
1586          debug_assert!(!b_to_a.z.is_zero());
1587          let b_to_a_sigvec = b_to_a.map (f64::signum);
1588          let nearest_b     = position_b + cuboid_b.max().0 * b_to_a_sigvec;
1589          let a_to_b        = nearest_b - shaft_max_a;
1590          let nearest_a     = shaft_max_a + if !a_to_b.is_zero() {
1591            capsule_a_radius * a_to_b.normalized()
1592          } else {
1593            [-b_to_a_sigvec.x * capsule_a_radius, 0.0, 0.0].into()
1594          };
1595          let axis          = nearest_b - nearest_a;
1596          let distance      = axis.magnitude() * if !a_to_b.is_zero() {
1597            a_to_b.dot (axis).signum()
1598          } else {
1599            -1.0
1600          };
1601          let half_axis     = 0.5 * axis;
1602          let normal        = if axis.is_zero() {
1603            Unit3::normalize (b_to_a_sigvec)
1604          } else {
1605            Unit3::unchecked_approx (-axis / distance)
1606          };
1607          let midpoint      = nearest_a + half_axis;
1608          Proximity { distance, half_axis, midpoint, normal }
1609        }
1610        (true, false) => {
1611          // nearest/deepest point is on an edge parallel to the X axis
1612          let signum_y  = (shaft_max_a.0.y - position_b.0.y).signum();
1613          let nearest_b = point3 (
1614            shaft_max_a.0.x,
1615            position_b.0.y + signum_y * cuboid_b_extent_y,
1616            cuboid_min_b.0.z
1617          );
1618          let a_to_b    = nearest_b - shaft_max_a;
1619          let nearest_a = shaft_max_a + if a_to_b.is_zero() {
1620            -signum_y * vector3 (0.0, capsule_a_radius, 0.0)
1621          } else {
1622            capsule_a_radius * a_to_b.normalized()
1623          };
1624          let axis      = nearest_b - nearest_a;
1625          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1626            a_to_b.dot (axis).signum()
1627          } else {
1628            -1.0
1629          };
1630          let half_axis = 0.5 * axis;
1631          let normal    = if axis.is_zero() {
1632            Unit3::normalize (vector3 (0.0, signum_y, -1.0))
1633          } else {
1634            Unit3::unchecked_approx (-axis / distance)
1635          };
1636          let midpoint  = nearest_a + half_axis;
1637          Proximity { distance, half_axis, midpoint, normal }
1638        }
1639        (false, true) => {
1640          // nearest/deepest point is on an edge parallel to the Y axis
1641          let signum_x  = (shaft_max_a.0.x - position_b.0.x).signum();
1642          let nearest_b = point3 (
1643            position_b.0.x + signum_x * cuboid_b_extent_x,
1644            shaft_max_a.0.y,
1645            cuboid_min_b.0.z
1646          );
1647          let a_to_b    = nearest_b - shaft_max_a;
1648          let nearest_a = shaft_max_a + if a_to_b.is_zero() {
1649            -signum_x * vector3 (capsule_a_radius, 0.0, 0.0)
1650          } else {
1651            capsule_a_radius * a_to_b.normalized()
1652          };
1653          let axis      = nearest_b - nearest_a;
1654          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1655            a_to_b.dot (axis).signum()
1656          } else {
1657            -1.0
1658          };
1659          let half_axis = 0.5 * axis;
1660          let normal    = if axis.is_zero() {
1661            Unit3::normalize (vector3 (signum_x, 0.0, -1.0))
1662          } else {
1663            Unit3::unchecked_approx (-axis / distance)
1664          };
1665          let midpoint  = nearest_a + half_axis;
1666          Proximity { distance, half_axis, midpoint, normal }
1667        }
1668        (true, true) => {
1669          // nearest/deepest point is in the -Z direction
1670          let nearest_a : Point3 <f64> =
1671            shaft_max_a + vector3 (0.0, 0.0, capsule_a_radius);
1672          let nearest_b : Point3 <f64> =
1673            [position_a.0.x, position_a.0.y, cuboid_min_b.0.z].into();
1674          let axis      = nearest_b - nearest_a;
1675          let normal    = Unit3::axis_z().invert();
1676          let half_axis = 0.5 * axis;
1677          let distance  = nearest_b.0.z - nearest_a.0.z;
1678          let midpoint  = nearest_a + half_axis;
1679          Proximity { distance, half_axis, midpoint, normal }
1680        }
1681      }
1682    } else {
1683      // capsule cylinder shaft overlaps on z axis
1684      let max_z = f64::min (shaft_max_a.0.z, cuboid_max_b.0.z);
1685      let min_z = f64::max (shaft_min_a.0.z, cuboid_min_b.0.z);
1686      debug_assert_ne!(min_z, max_z);
1687      let mid_z = 0.5 * (max_z + min_z);
1688      match (overlap_x, overlap_y) {
1689        (false, false) => {
1690          // nearest point is on a Z parallel edge of the cuboid
1691          let b_to_a_signum_x = (position_a.0.x - position_b.0.x).signum();
1692          let b_to_a_signum_y = (position_a.0.y - position_b.0.y).signum();
1693          let b_to_a_unit_sigvec = vector3 (b_to_a_signum_x, b_to_a_signum_y, 0.0)
1694            .normalized();
1695          let nearest_b = Point3::from ([
1696            //position_b.0.x + b_to_a_signum_x * cuboid_b_extent_x,
1697            b_to_a_signum_x.mul_add (cuboid_b_extent_x, position_b.0.x),
1698            //position_b.0.y + b_to_a_signum_y * cuboid_b_extent_y,
1699            b_to_a_signum_y.mul_add (cuboid_b_extent_y, position_b.0.y),
1700            mid_z
1701          ]);
1702          let a_to_b    = vector3 (
1703            nearest_b.0.x - position_a.0.x,
1704            nearest_b.0.y - position_a.0.y,
1705            0.0
1706          );
1707          let nearest_a = point3 (position_a.0.x, position_a.0.y, mid_z)
1708            + capsule_a_radius * if !a_to_b.is_zero() {
1709              a_to_b.normalized()
1710            } else {
1711              -b_to_a_unit_sigvec
1712            };
1713          let axis      = nearest_b - nearest_a;
1714          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1715            a_to_b.dot (axis).signum()
1716          } else {
1717            -1.0
1718          };
1719          let half_axis = 0.5 * axis;
1720          let normal    = Unit3::unchecked_approx (if axis.is_zero() {
1721            b_to_a_unit_sigvec
1722          } else {
1723            -axis / distance
1724          });
1725          let midpoint  = nearest_a + half_axis;
1726          Proximity { distance, half_axis, midpoint, normal }
1727        }
1728        (true, false)  => {
1729          // nearest point is on a Z/X parallel face of the cuboid
1730          let b_to_a_signum_y    = (position_a.0.y - position_b.0.y).signum();
1731          let b_to_a_unit_sigvec = vector3 (0.0, b_to_a_signum_y, 0.0);
1732          let nearest_b = Point3::from ([
1733            position_a.0.x,
1734            //position_b.0.y + b_to_a_signum_y * cuboid_b_extent_y,
1735            b_to_a_signum_y.mul_add (cuboid_b_extent_y, position_b.0.y),
1736            mid_z
1737          ]);
1738          let a_to_b    = vector3 (0.0, nearest_b.0.y - position_a.0.y, 0.0);
1739          let nearest_a = point3 (position_a.0.x, position_a.0.y, mid_z) +
1740            vector3 (0.0, -b_to_a_signum_y * capsule_a_radius, 0.0);
1741          let axis      = nearest_b - nearest_a;
1742          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1743            a_to_b.dot (axis).signum()
1744          } else {
1745            -1.0
1746          };
1747          let half_axis = 0.5 * axis;
1748          let normal    = Unit3::unchecked_approx (if axis.is_zero() {
1749            b_to_a_unit_sigvec
1750          } else {
1751            -axis / distance
1752          });
1753          let midpoint  = nearest_a + half_axis;
1754          Proximity { distance, half_axis, midpoint, normal }
1755        }
1756        (false, true)  => {
1757          // nearest point is on a Z/Y parallel face of the cuboid
1758          let b_to_a_signum_x    = (position_a.0.x - position_b.0.x).signum();
1759          let b_to_a_unit_sigvec = vector3 (b_to_a_signum_x, 0.0, 0.0);
1760          let nearest_b = Point3::from ([
1761            //position_b.0.x + b_to_a_signum_x * cuboid_b_extent_x,
1762            b_to_a_signum_x.mul_add (cuboid_b_extent_x, position_b.0.x),
1763            position_a.0.y,
1764            mid_z
1765          ]);
1766          let a_to_b    = vector3 (nearest_b.0.x - position_a.0.x, 0.0, 0.0);
1767          let nearest_a = point3 (position_a.0.x, position_a.0.y, mid_z) +
1768            vector3 (-b_to_a_signum_x * capsule_a_radius, 0.0, 0.0);
1769          let axis      = nearest_b - nearest_a;
1770          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1771            a_to_b.dot (axis).signum()
1772          } else {
1773            -1.0
1774          };
1775          let half_axis = 0.5 * axis;
1776          let normal    = Unit3::unchecked_approx (if axis.is_zero() {
1777            b_to_a_unit_sigvec
1778          } else {
1779            -axis / distance
1780          });
1781          let midpoint  = nearest_a + half_axis;
1782          Proximity { distance, half_axis, midpoint, normal }
1783        }
1784        (true, true) => {
1785          // cylinder shaft inside X/Y bounds
1786          if position_a == position_b {
1787            let depth_x = cuboid_b_extent_x + capsule_a_radius;
1788            let depth_y = cuboid_b_extent_y + capsule_a_radius;
1789            let depth_z = cuboid_b_extent_z + capsule_a_half_height +
1790              capsule_a_radius;
1791            if depth_x <= depth_y && depth_x <= depth_z {
1792              let distance  = -depth_x;
1793              let half_axis = [0.5 * depth_x, 0.0, 0.0].into();
1794              let normal    = Unit3::axis_x();
1795              let midpoint  = position_a + half_axis -
1796                vector3 (capsule_a_radius, 0.0, 0.0);
1797              Proximity { distance, half_axis, midpoint, normal }
1798            } else if depth_y <= depth_x && depth_y <= depth_z {
1799              let distance = -depth_y;
1800              let half_axis = [0.0, 0.5 * depth_y, 0.0].into();
1801              let normal    = Unit3::axis_y();
1802              let midpoint  = position_a + half_axis -
1803                vector3 (0.0, capsule_a_radius, 0.0);
1804              Proximity { distance, half_axis, midpoint, normal }
1805            } else {
1806              debug_assert!(depth_z < depth_x);
1807              debug_assert!(depth_z < depth_y);
1808              let distance = -depth_z;
1809              let half_axis = [0.0, 0.0, 0.5 * depth_z].into();
1810              let normal    = Unit3::axis_z();
1811              let midpoint  = position_a + half_axis -
1812                vector3 (0.0, 0.0, capsule_a_radius + capsule_a_half_height);
1813              Proximity { distance, half_axis, midpoint, normal }
1814            }
1815          } else {
1816            // NOTE: we want the following to map zero values to positive 1.0
1817            // so we use f64::signum instead of sigvec
1818            let b_to_a_sigvec = (position_a - position_b).map (f64::signum);
1819            let corner_b      = position_b + cuboid_b.max().0 * b_to_a_sigvec;
1820            let depth_x       = (corner_b.0.x - position_a.0.x).abs() + capsule_a_radius;
1821            let depth_y       = (corner_b.0.y - position_a.0.y).abs() + capsule_a_radius;
1822            let depth_z       = (corner_b.0.z - position_a.0.z).abs() +
1823              capsule_a_radius + capsule_a_half_height -
1824              if position_a.0.z.abs() > corner_b.0.z.abs() {
1825                2.0 * (position_a.0.z.abs() - corner_b.0.z.abs())
1826              } else {
1827                0.0
1828              };
1829
1830            if depth_x <= depth_y && depth_x <= depth_z {
1831              let distance  = -depth_x;
1832              let half_axis = [0.5 * b_to_a_sigvec.x * depth_x, 0.0, 0.0].into();
1833              let normal = Unit3::unchecked ([b_to_a_sigvec.x, 0.0, 0.0].into());
1834              let midpoint  = position_a + half_axis +
1835                vector3 (-b_to_a_sigvec.x * capsule_a_radius, 0.0, 0.0);
1836              Proximity { distance, half_axis, midpoint, normal }
1837            } else if depth_y <= depth_x && depth_y <= depth_z {
1838              let distance  = -depth_y;
1839              let half_axis = [0.0, 0.5 * b_to_a_sigvec.y * depth_y, 0.0].into();
1840              let normal = Unit3::unchecked ([0.0, b_to_a_sigvec.y, 0.0].into());
1841              let midpoint  = position_a + half_axis +
1842                vector3 (0.0, -b_to_a_sigvec.y * capsule_a_radius, 0.0);
1843              Proximity { distance, half_axis, midpoint, normal }
1844            } else {
1845              debug_assert!(depth_z < depth_x);
1846              debug_assert!(depth_z < depth_y);
1847              let nearest_a = point3 (
1848                position_a.0.x,
1849                position_a.0.y,
1850                //position_a.0.z - b_to_a_sigvec.z * (capsule_a_radius + capsule_a_half_height)
1851                b_to_a_sigvec.z
1852                  .mul_add (-(capsule_a_radius + capsule_a_half_height), position_a.0.z)
1853              );
1854              let nearest_b = point3 (
1855                position_a.0.x,
1856                position_a.0.y,
1857                //position_b.0.z + b_to_a_sigvec.z * cuboid_b_extent_z
1858                b_to_a_sigvec.z.mul_add (cuboid_b_extent_z, position_b.0.z)
1859              );
1860              let axis      = nearest_b - nearest_a;
1861              debug_assert_eq!(axis.x, 0.0);
1862              debug_assert_eq!(axis.y, 0.0);
1863              let distance  = -axis.z.abs();
1864              let half_axis = 0.5 * axis;
1865              let normal = Unit3::unchecked ([0.0, 0.0, b_to_a_sigvec.z].into());
1866              let midpoint  = nearest_a + half_axis;
1867              Proximity { distance, half_axis, midpoint, normal }
1868            }
1869          }
1870        }
1871      }
1872    }
1873  } // end query_capsule_cuboid
1874
1875  pub fn query_capsule_orthant (
1876    position_a : &Point3 <f64>, capsule_a : &shape::Capsule <f64>,
1877    position_b : &Point3 <f64>, orthant_b : &shape::Orthant
1878  ) -> Self {
1879    let radius_a    = *capsule_a.radius;
1880    let half_height = *capsule_a.half_height;
1881    let normal      = orthant_b.normal_axis.to_vec::<f64>();
1882    let normal_abs  = normal.map (f64::abs);
1883    let surface_sigvec = vector3 (1.0, 1.0, 1.0) - normal_abs;
1884    let nearest_a   = position_a -
1885      vector3 (radius_a, radius_a, radius_a + half_height) * normal;
1886    let nearest_b   =
1887      Point3 (position_a.0 * surface_sigvec + position_b.0 * normal_abs);
1888    let axis        = nearest_b - nearest_a;
1889    debug_assert_eq!(axis.sum().abs(), axis.magnitude());
1890    let distance    = -normal.dot (axis).signum() * axis.sum().abs();
1891    let half_axis   = 0.5 * axis;
1892    let midpoint    = nearest_a + half_axis;
1893    let normal      = Unit3::unchecked (normal);
1894    Proximity { distance, half_axis, midpoint, normal }
1895  } // end query_capsule_orthant
1896
1897  pub fn query_cuboid_orthant (
1898    position_a : &Point3 <f64>, cuboid_a  : &shape::Cuboid <f64>,
1899    position_b : &Point3 <f64>, orthant_b : &shape::Orthant
1900  ) -> Self {
1901    let normal      = orthant_b.normal_axis.to_vec::<f64>();
1902    let normal_abs  = normal.map (f64::abs);
1903    let surface_sigvec = vector3 (1.0, 1.0, 1.0) - normal_abs;
1904    let nearest_a   = position_a - cuboid_a.extents.map (|e| *e) * normal;
1905    let nearest_b   =
1906      Point3 (position_a.0 * surface_sigvec + position_b.0 * normal_abs);
1907    let axis        = nearest_b - nearest_a;
1908    debug_assert_eq!(axis.sum().abs(), axis.magnitude());
1909    let distance    = -normal.dot (axis).signum() * axis.sum().abs();
1910    let half_axis   = 0.5 * axis;
1911    let midpoint    = nearest_a + half_axis;
1912    let normal      = Unit3::unchecked (normal);
1913    Proximity { distance, half_axis, midpoint, normal }
1914  } // end query_cuboid_orthant
1915
1916  pub fn query_triangle_triangle (
1917    triangle_a : &geometry::Triangle3 <f64>,
1918    triangle_b : &geometry::Triangle3 <f64>
1919  ) -> Self {
1920    fn query_triangle_triangle_recursive (
1921      triangle_a : &geometry::Triangle3 <f64>,
1922      triangle_b : &geometry::Triangle3 <f64>,
1923      is_recurse : bool
1924    ) -> Proximity {
1925      let mut out = None;
1926      let mut min_distance = f64::MAX;
1927      for edge in triangle_a.edges() {
1928        let proximity = Proximity::query_triangle_segment (triangle_b, &edge);
1929        if proximity.distance < -f64::EPSILON * 2.0.powi (12) {
1930          out = None;
1931          break
1932        }
1933        if proximity.distance < min_distance {
1934          min_distance = proximity.distance;
1935          // we swap the roles of object A and B
1936          out = Some (proximity.flip());
1937        }
1938      }
1939      if out.is_some() {
1940        // only check the other triangle if the first triangle didn't overlap
1941        for edge in triangle_b.edges() {
1942          let proximity = Proximity::query_triangle_segment (triangle_a, &edge);
1943          if proximity.distance < -f64::EPSILON * 2.0.powi (12) {
1944            out = None;
1945            break
1946          }
1947          if proximity.distance < min_distance {
1948            min_distance = proximity.distance;
1949            out = Some (proximity);
1950          }
1951        }
1952      }
1953      if out.is_none() {
1954        // triangles overlap
1955        if is_recurse {
1956          log::error!("triangle/triangle proximity query can't recurse more than once");
1957          panic!("triangle/triangle proximity query can't recurse more than once");
1958        }
1959        // edge intersected: construct minkowski hull A - B
1960        let points : [Point3 <f64>; 9] = std::array::from_fn (|i|{
1961          let p = triangle_a.points()[i / 3];
1962          let q = triangle_b.points()[i % 3];
1963          Point3 (p.0 - q.0)
1964        });
1965        // brute force search: there are 9 choose 3 = 84 unique triangles, for each
1966        // triangle, calculate the distance and whether or not it is exterior; discard
1967        // any triangles that are further away than the nearest exterior triangle
1968        let get_triangle = |a, b, c|
1969          geometry::Triangle3::new (points[a], points[b], points[c]);
1970        let mut nearest_distance : Option <geometry::Distance3 <f64>> = None;
1971        let tolerance = f64::EPSILON * 2.0.powi (10);
1972        for i in 0..9 {
1973          for j in (i + 1)..9 {
1974            for k in (j + 1)..9 {
1975              if let Some (triangle) = get_triangle (i, j, k) {
1976                let distance =
1977                  geometry::Distance3::triangle_point (triangle, Point3::origin());
1978                if distance.distance_squared() < nearest_distance.as_ref().map_or (
1979                  NonNegative::unchecked (f64::MAX),
1980                  geometry::Distance3::distance_squared)
1981                {
1982                  // check if it is exterior
1983                  let normal = {
1984                    let mut normal = triangle.normal();
1985                    if normal.dot (triangle.point_a().0) < 0.0 {
1986                      normal = -normal
1987                    }
1988                    normal
1989                  };
1990                  let mut exterior = true;
1991                  for point in points.iter() {
1992                    if normal.dot (point - triangle.point_a()) > tolerance {
1993                      exterior = false;
1994                      break
1995                    }
1996                  }
1997                  if exterior {
1998                    nearest_distance = Some (distance);
1999                  }
2000                }
2001              }
2002            }
2003          }
2004        }
2005        let mut nearest = nearest_distance.unwrap();
2006        let distance    = -*nearest.distance();
2007        let half_axis   = -nearest.nearest_a().0 * 0.5;
2008        let resolved_proximity = {
2009          let translate = |mut triangle : geometry::Triangle3 <f64>, displacement| {
2010            triangle.translate (displacement);
2011            triangle
2012          };
2013          let resolved_a = translate (*triangle_a, half_axis);
2014          let resolved_b = translate (*triangle_b, -half_axis);
2015          // TODO: instead of recursing can we query nearest features ?
2016          query_triangle_triangle_recursive (&resolved_a, &resolved_b, true)
2017        };
2018        out = Some(Proximity { distance, half_axis, .. resolved_proximity });
2019      }
2020      out.unwrap()
2021    }
2022    query_triangle_triangle_recursive (triangle_a, triangle_b, false)
2023  }
2024
2025  pub fn query_triangle_line (
2026    triangle : &geometry::Triangle3 <f64>,
2027    line     : &geometry::Segment3 <f64>
2028  ) -> Self {
2029    use approx::{AbsDiffEq, RelativeEq};
2030    let (([s, t], nearest_a), (_, nearest_b)) =
2031      geometry::distance::nearest_triangle3_line3 (*triangle, *line);
2032    if approx::relative_eq!(nearest_a, nearest_b,
2033      max_relative = f64::default_max_relative() * 256.0,
2034      epsilon      = f64::default_epsilon() * 256.0)
2035    {
2036      // line and triangle intersect
2037      let distance;
2038      let half_axis;
2039      let midpoint;
2040      let normal;
2041      let triangle_normal = triangle.normal();
2042      if approx::abs_diff_eq!(triangle_normal.dot (*line.vector()), 0.0) {
2043        // line and triangle are parallel:
2044        // normal is perpendicular to triangle face
2045        let a_to_b = nearest_b - nearest_a;
2046        distance   = a_to_b.magnitude();
2047        half_axis  = a_to_b * 0.5;
2048        midpoint   = nearest_a + half_axis;
2049        normal     = triangle_normal.normalize();
2050      } else {
2051        let y = nearest_a;
2052        let (d0, d1, d2) = triangle_distance_to_edges (*triangle, *s, *t);
2053        // project to nearest edge
2054        let edge0 = triangle.point_c() - triangle.point_b();
2055        let edge1 = triangle.point_b() - triangle.point_a();
2056        let edge2 = triangle.point_c() - triangle.point_a();
2057        let len2_e0 = edge0.magnitude_squared();
2058        let len2_e1 = edge1.magnitude_squared();
2059        let len2_e2 = edge2.magnitude_squared();
2060        let mut e0_is_nearest = false;
2061        let mut e1_is_nearest = false;
2062        let mut e2_is_nearest = false;
2063        let nearest_a = if d0 <= d1 && d0 <= d2 {
2064          e0_is_nearest = true;
2065          let t = (y - triangle.point_b()).dot (edge0) / len2_e0;
2066          triangle.point_b() + edge0 * t
2067        } else if d1 <= d2 {
2068          e1_is_nearest = true;
2069          let t = (y - triangle.point_a()).dot (edge1) / len2_e1;
2070          triangle.point_a() + edge1 * t
2071        } else {
2072          debug_assert!(d2 < d0);
2073          debug_assert!(d2 < d1);
2074          e2_is_nearest = true;
2075          let t = (y - triangle.point_a()).dot (edge2) / len2_e2;
2076          triangle.point_a() + edge2 * t
2077        };
2078        let nearest_b = y;
2079        let a_to_b    = nearest_b - nearest_a;
2080        distance      = -a_to_b.magnitude();
2081        half_axis     = a_to_b * 0.5;
2082        midpoint      = nearest_a + half_axis;
2083        // TODO: would it be better to check distance approx eq 0.0 here? in theory
2084        // there are less comparisons, but if nearest_a and nearest_b are far from the
2085        // origin the distance may be large
2086        normal        = if approx::relative_eq!(nearest_a, nearest_b,
2087          max_relative = f64::default_max_relative() * 256.0,
2088          epsilon      = f64::default_epsilon() * 256.0)
2089        {
2090          let perp = if e0_is_nearest {
2091            // e0 is edge BC
2092            -triangle.perpendicular_bc()
2093          } else if e1_is_nearest {
2094            // e1 is edge AB
2095            -triangle.perpendicular_ab()
2096          } else {
2097            debug_assert!(e2_is_nearest);
2098            // e2 is edge AC
2099            -triangle.perpendicular_ca()
2100          };
2101          *perp
2102        } else {
2103          -a_to_b
2104        }.normalize();
2105      }
2106      return Proximity { distance, half_axis, midpoint, normal }
2107    }
2108    let a_to_b    = nearest_b - nearest_a;
2109    let distance  = a_to_b.magnitude();
2110    let half_axis = a_to_b * 0.5;
2111    let midpoint  = nearest_a + half_axis;
2112    let normal    = Unit3::normalize (-a_to_b);
2113    Proximity { distance, half_axis, midpoint, normal }
2114  }
2115
2116  pub fn query_triangle_segment (
2117    triangle : &geometry::Triangle3 <f64>,
2118    segment  : &geometry::Segment3 <f64>
2119  ) -> Self {
2120    use approx::{AbsDiffEq, RelativeEq};
2121    let (([s, t], nearest_a), (_r, nearest_b)) =
2122      geometry::distance::nearest_triangle3_segment3 (*triangle, *segment);
2123    if approx::relative_eq!(nearest_a, nearest_b,
2124      max_relative = f64::default_max_relative() * 2.0.powi (17),
2125      epsilon      = f64::default_epsilon() * 2.0.powi (17))
2126    {
2127      // segment and triangle intersect
2128      // we can check edge intersection by looking at s and t
2129      if let Some((mut proximity, edge_perp)) = if approx::abs_diff_eq!(*s, 0.0,
2130          epsilon = f64::default_epsilon() * 2.0.powi (23))
2131        {
2132          // intersect edge AC
2133          Some (( Proximity::query_segment_segment (&triangle.edge_ca(), segment),
2134            triangle.perpendicular_ca() ))
2135        } else if approx::abs_diff_eq!(*t, 0.0,
2136          epsilon = f64::default_epsilon() * 2.0.powi (23))
2137        {
2138          // intersect edge AB
2139          Some (( Proximity::query_segment_segment (&triangle.edge_ab(), segment),
2140            triangle.perpendicular_ab() ))
2141        } else if approx::relative_eq!(*s + *t, 1.0,
2142          max_relative = f64::default_max_relative() * 2.0.powi (23),
2143          epsilon = f64::default_epsilon() * 2.0.powi (23))
2144        {
2145          // intersect edge BC
2146          Some (( Proximity::query_segment_segment (&triangle.edge_bc(), segment),
2147            triangle.perpendicular_bc() ))
2148        } else {
2149          None
2150        }
2151      {
2152        if edge_perp.dot (*proximity.normal) > 0.0 {
2153          // ensure normal points from segment towards triangle
2154          proximity.normal = -proximity.normal;
2155        }
2156        return proximity
2157      }
2158      // the penetration depth is the shortest distance to move the segment to one of
2159      // the triangle edges, or else to move the segment perpendicularly to the triangle
2160      // surface
2161      let distance;
2162      let half_axis;
2163      let midpoint;
2164      let normal;
2165      let triangle_normal          = triangle.normal();
2166      let mut triangle_normal_unit = None;
2167      let segment_vec              = segment.vector();
2168      if approx::abs_diff_eq!(triangle_normal.dot (*segment_vec), 0.0) {
2169        // segment and triangle are parallel:
2170        // penetration depth is zero and direction is perpendicular to triangle face
2171        distance  = 0.0;
2172        half_axis = Vector3::zero();
2173        midpoint  = nearest_a;
2174        normal    = triangle_normal.normalize();
2175      } else {
2176        // segment intersects triangle at a single point
2177        let (seg_depth_sq, seg_depth_vec) = if triangle_normal.cross (*segment_vec)
2178          == Vector3::zero()
2179        {
2180          // segment is perpendicular to triangle: just use endpoint distances
2181          let depth_vec_a = segment.point_a() - nearest_b;
2182          let depth_vec_b = segment.point_b() - nearest_b;
2183          let dsq_a = depth_vec_a.magnitude_squared();
2184          let dsq_b = depth_vec_b.magnitude_squared();
2185          if dsq_a < dsq_b {
2186            (dsq_a, depth_vec_a)
2187          } else {
2188            (dsq_b, depth_vec_b)
2189          }
2190        } else {
2191          // segment intersects at an angle
2192          // project onto triangle plane
2193          let normal_unit      = triangle_normal.normalize();
2194          triangle_normal_unit = Some (normal_unit);
2195          let tri0_to_seg0     = segment.point_a() - triangle.point_a();
2196          let tri0_to_seg1     = segment.point_b() - triangle.point_a();
2197          let normal_dot_seg0  = normal_unit.dot (tri0_to_seg0);
2198          let normal_dot_seg1  = normal_unit.dot (tri0_to_seg1);
2199          let proj_seg0        = segment.point_a() - *normal_unit * normal_dot_seg0;
2200          let proj_seg1        = segment.point_b() - *normal_unit * normal_dot_seg1;
2201          // convert projected points to barycentric coordinates
2202          let ab               = triangle.point_b() - triangle.point_a();
2203          let ac               = triangle.point_c() - triangle.point_a();
2204          let ab_mag_sq        = ab.magnitude_squared();
2205          let ab_dot_ac        = ab.dot (ac);
2206          let ac_mag_sq        = ac.magnitude_squared();
2207          //let determinant     = ab_mag_sq * ac_mag_sq - ab_dot_ac.squared();
2208          let determinant      = ab_mag_sq.mul_add (ac_mag_sq,  -ab_dot_ac.squared());
2209          // barycentric coords: b0 = 1 - s - t, b1 = s, b2 = t
2210          let barycentric_to_cartesian = |s, t| triangle.point_a() + ab * s + ac * t;
2211          let barycentric_coords = |p_proj : Point3 <f64>| {
2212            let to_p = p_proj - triangle.point_a();
2213            let to_p_dot_ab = to_p.dot (ab);
2214            let to_p_dot_ac = to_p.dot (ac);
2215            //let s = (ac_mag_sq * to_p_dot_ab - ab_dot_ac * to_p_dot_ac) / determinant;
2216            let s = ac_mag_sq.mul_add (to_p_dot_ab,  -ab_dot_ac * to_p_dot_ac)
2217              / determinant;
2218            //let t = (ab_mag_sq * to_p_dot_ac - ab_dot_ac * to_p_dot_ab) / determinant;
2219            let t = ab_mag_sq.mul_add (to_p_dot_ac, -ab_dot_ac * to_p_dot_ab)
2220              / determinant;
2221            if cfg!(debug_assertions) {
2222              approx::assert_relative_eq!(p_proj, barycentric_to_cartesian (s, t),
2223                epsilon = f64::default_epsilon() * 2.0.powi (40),
2224                max_relative = f64::default_max_relative() * 2.0.powi (40));
2225            }
2226            [s, t]
2227          };
2228          let [s0, t0] = barycentric_coords (proj_seg0);
2229          let [s1, t1] = barycentric_coords (proj_seg1);
2230          let (base_s, base_t) = (s0, t0);
2231          let (vec_s, vec_t)   = (s1 - s0, t1 - t0);
2232          // returns clipped barycentric coordinates and parameter along segment
2233          let clip_seg_endpoint = |s, t| {
2234            // clip against each edge in barycentric space
2235            // describe the projected line segment in barycentric space as parameterized
2236            // line `base + u * vec`
2237            // edge AB: t
2238            // edge BC: 1 - s - t
2239            // edge CA: s
2240            // clip s == 0
2241            let (mut s, mut t) = (s, t);
2242            let mut u = 0.0;
2243            #[expect(clippy::useless_let_if_seq)]
2244            let mut clipped = false;
2245            if s < 0.0 {
2246              // solve: 0 = base_s + u * vec_s
2247              u = -base_s / vec_s;
2248              s = 0.0;
2249              t = base_t + u * vec_t;
2250              clipped = true;
2251            }
2252            if t < 0.0 {
2253              // solve: 0 = base_t + u * vec_t
2254              u = -base_t / vec_t;
2255              s = base_s + u * vec_s;
2256              t = 0.0;
2257              clipped = true;
2258            }
2259            if s + t > 1.0 {
2260              // solve: for u such that s + t == 1
2261              u = (1.0 - base_s - base_t) / (vec_s + vec_t);
2262              s = base_s + u * vec_s;
2263              t = base_t + u * vec_t;
2264              if cfg!(debug_assertions) {
2265                approx::assert_relative_eq!(s + t, 1.0,
2266                  max_relative = f64::default_max_relative() * 2.0.powi (16),
2267                  epsilon = f64::default_epsilon() * 2.0.powi (16));
2268              }
2269              debug_assert!(s >= 0.0);
2270              debug_assert!(t >= 0.0);
2271              clipped = true;
2272            }
2273            if clipped {
2274              Some (([s, t], u))
2275            } else {
2276              None
2277            }
2278          };
2279          let (dsq_a, depth_vec_a) =
2280            if let Some (([s, t], u)) = clip_seg_endpoint (s0, t0) {
2281              let clipped   = segment.point_a() + *segment_vec * u;
2282              let proj      = barycentric_to_cartesian (s, t);
2283              let depth_vec = clipped - proj;
2284              (depth_vec.magnitude_squared(), depth_vec)
2285            } else {
2286              let depth_vec = segment.point_a() - proj_seg0;
2287              (depth_vec.magnitude_squared(), depth_vec)
2288            };
2289          let (dsq_b, depth_vec_b) =
2290            if let Some (([s, t], u)) = clip_seg_endpoint (s1, t1) {
2291              let clipped   = segment.point_a() + *segment_vec * u;
2292              let proj      = barycentric_to_cartesian (s, t);
2293              let depth_vec = clipped - proj;
2294              (depth_vec.magnitude_squared(), depth_vec)
2295            } else {
2296              let depth_vec = segment.point_b() - proj_seg1;
2297              (depth_vec.magnitude_squared(), depth_vec)
2298            };
2299          if dsq_a < dsq_b {
2300            (dsq_a, depth_vec_a)
2301          } else {
2302            (dsq_b, depth_vec_b)
2303          }
2304        };
2305        // now compute the distance to nearest edge
2306        let (d0, d1, d2) = triangle_distance_to_edges (*triangle, *s, *t);
2307        let edge_dist_sq = f64::min (d0 * d0, f64::min (d1 * d1, d2 * d2));
2308        // choose minimum between perpendicular depth and edge distance
2309        if seg_depth_sq <= edge_dist_sq {
2310          // perpendicular movement is shorter
2311          let unit_normal = triangle_normal_unit
2312            .unwrap_or_else (|| triangle_normal.normalize());
2313          if unit_normal.dot (seg_depth_vec) < 0.0 {
2314            normal = -unit_normal;
2315          } else {
2316            normal = unit_normal;
2317          }
2318          distance  = -seg_depth_sq.sqrt();
2319          half_axis = *normal * -distance * 0.5;
2320          midpoint  = nearest_a + half_axis;
2321        } else {
2322          // moving to edge is shorter
2323          let edge_dist = edge_dist_sq.sqrt();
2324          distance      = -edge_dist;
2325          // find which edge is nearest
2326          let edge0     = triangle.point_b() - triangle.point_a();
2327          let edge1     = triangle.point_c() - triangle.point_a();
2328          let edge2     = triangle.point_c() - triangle.point_b();
2329          let len2_e0   = edge0.magnitude_squared();
2330          let len2_e1   = edge1.magnitude_squared();
2331          let len2_e2   = edge2.magnitude_squared();
2332          let (nearest_edge_point, nearest_edge_perp) = if d0 <= d1 && d0 <= d2 {
2333            let t = (nearest_b - triangle.point_b()).dot (edge0) / len2_e0;
2334            (triangle.point_b() + edge0 * t, -triangle.perpendicular_ab())
2335          } else if d1 <= d2 {
2336            let t = (nearest_b - triangle.point_a()).dot (edge1) / len2_e1;
2337            (triangle.point_a() + edge1 * t, -triangle.perpendicular_ca())
2338          } else {
2339            let t = (nearest_b - triangle.point_a()).dot (edge2) / len2_e2;
2340            (triangle.point_a() + edge2 * t, -triangle.perpendicular_bc())
2341          };
2342          half_axis = (nearest_b - nearest_edge_point) * 0.5;
2343          midpoint  = nearest_a + half_axis;
2344          normal    = nearest_edge_perp.normalize();
2345        }
2346      }
2347      return Proximity { distance, half_axis, midpoint, normal }
2348    }
2349    let a_to_b    = nearest_b - nearest_a;
2350    let distance  = a_to_b.magnitude();
2351    let half_axis = a_to_b * 0.5;
2352    let midpoint  = nearest_a + half_axis;
2353    let normal    = -a_to_b.normalize();
2354    Proximity { distance, half_axis, midpoint, normal }
2355  }
2356
2357  pub fn query_triangle_point (
2358    triangle : &geometry::Triangle3 <f64>,
2359    point    : &Point3 <f64>
2360  ) -> Self {
2361    let ([s, t], nearest_a) = triangle.nearest_point (*point);
2362    let a_to_b    = point - nearest_a;
2363    let distance  = a_to_b.magnitude();
2364    let half_axis = a_to_b * 0.5;
2365    let midpoint  = nearest_a + half_axis;
2366    let normal    = if *point == nearest_a {
2367      // TODO: for efficiency should we just return the triangle normal in all cases ?
2368      let perp = if *t == 0.0 {
2369        // on edge ab
2370        -triangle.perpendicular_ab()
2371      } else if *s == 0.0 {
2372        // on edge ac
2373        -triangle.perpendicular_ca()
2374      } else if *s + *t == 1.0 {
2375        // on edge bc
2376        -triangle.perpendicular_bc()
2377      } else {
2378        // use the triangle normal
2379        triangle.normal()
2380      };
2381      *perp
2382    } else {
2383      -a_to_b
2384    }.normalize();
2385    Proximity { distance, half_axis, midpoint, normal }
2386  }
2387
2388  pub fn query_line_line (
2389    line_a : &geometry::Segment3 <f64>,
2390    line_b : &geometry::Segment3 <f64>
2391  ) -> Self {
2392    use approx::{AbsDiffEq, RelativeEq};
2393    use num::Zero;
2394    let ((_, nearest_a), (_, nearest_b)) =
2395      geometry::distance::nearest_line3_line3 (*line_a, *line_b);
2396    let a_to_b    = nearest_b - nearest_a;
2397    let distance  = a_to_b.magnitude();
2398    let half_axis = a_to_b * 0.5;
2399    let midpoint  = nearest_a + half_axis;
2400    let normal    = if approx::relative_eq!(nearest_a, nearest_b,
2401      max_relative = f64::default_max_relative() * 256.0,
2402      epsilon      = f64::default_epsilon() * 256.0)
2403    {
2404      // intersection
2405      let line_a_dir = line_a.vector();
2406      let line_b_dir = line_b.vector();
2407      let mut perp   = line_a_dir.cross (*line_b_dir);
2408      if perp.is_zero() {
2409        perp = line_a_dir.orthogonal();
2410      }
2411      perp
2412    } else {
2413      -a_to_b
2414    }.normalize();
2415    Proximity { distance, half_axis, midpoint, normal }
2416  }
2417
2418  pub fn query_line_segment (
2419    line    : &geometry::Segment3 <f64>,
2420    segment : &geometry::Segment3 <f64>
2421  ) -> Self {
2422    use approx::{AbsDiffEq, RelativeEq};
2423    let seg_dir   = segment.vector();
2424    let line_dir  = line.vector();
2425    let ((_, nearest_a), (s1, nearest_b)) =
2426      geometry::distance::nearest_line3_segment3 (*line, *segment);
2427    let a_to_b    = nearest_b - nearest_a;
2428    let distance  = a_to_b.magnitude();
2429    let half_axis = a_to_b * 0.5;
2430    let midpoint  = nearest_a + half_axis;
2431    let normal    = if approx::relative_eq!(nearest_a, nearest_b,
2432      max_relative = f64::default_max_relative() * 256.0,
2433      epsilon      = f64::default_epsilon() * 256.0)
2434    {
2435      // intersection
2436      if 0.0 < *s1 && *s1 < 1.0 {
2437        // intersection internal to segment
2438        let mut perp = seg_dir.cross (*line_dir);
2439        if perp.is_approx_zero() {
2440          // line and segment are parallel
2441          perp = seg_dir.orthogonal();
2442        }
2443        debug_assert!(!perp.is_approx_zero());
2444        perp
2445      } else {
2446        // when an endpoint is touching, choose the normal to point away from the
2447        // segment
2448        debug_assert!(*s1 == 0.0 || *s1 == 1.0);
2449        let mut perp = seg_dir.cross (*line_dir).cross (*line_dir);
2450        if perp.is_approx_zero() {
2451          // parallel
2452          perp = seg_dir.orthogonal()
2453        } else if *s1 == 1.0 {
2454          perp = -perp
2455        }
2456        debug_assert!((nearest_a - segment.point_a()).dot (perp) >= 0.0);
2457        debug_assert!((nearest_a - segment.point_b()).dot (perp) >= 0.0);
2458        perp
2459      }
2460    } else {
2461      -a_to_b
2462    }.normalize();
2463    Proximity { distance, half_axis, midpoint, normal }
2464  }
2465
2466  pub fn query_line_point (
2467    line  : &geometry::Segment3 <f64>,
2468    point : &Point3 <f64>
2469  ) -> Self {
2470    let (_, nearest_a) = line.nearest_point (*point);
2471    let nearest_b      = *point;
2472    let a_to_b         = nearest_b - nearest_a;
2473    let distance       = a_to_b.magnitude();
2474    let half_axis      = a_to_b * 0.5;
2475    let midpoint       = nearest_a + half_axis;
2476    let normal         = if approx::relative_eq!(nearest_a, nearest_b) {
2477      *line.perpendicular()
2478    } else {
2479      -a_to_b
2480    }.normalize();
2481    Proximity { distance, half_axis, midpoint, normal }
2482  }
2483
2484  pub fn query_segment_segment (
2485    segment_a : &geometry::Segment3 <f64>,
2486    segment_b : &geometry::Segment3 <f64>
2487  ) -> Self {
2488    use approx::{AbsDiffEq, RelativeEq};
2489    // method from Eberly GeometricTools:
2490    // <https://github.com/davideberly/GeometricTools/blob/a9a2b149485857273ea1f1cfa5416b392f495882/GTE/Mathematics/DistSegmentSegment.h>
2491    let vec_a = segment_a.vector();
2492    let vec_b = segment_b.vector();
2493    let vec_b0a0 = segment_a.point_a() - segment_b.point_a();
2494    let a = Positive::unchecked (*vec_a.self_dot());
2495    let b = vec_a.dot (*vec_b);
2496    let c = Positive::unchecked (*vec_b.self_dot());
2497    let d = vec_a.dot (vec_b0a0);
2498    let e = vec_b.dot (vec_b0a0);
2499    let f00 = d;
2500    let f10 = f00 + *a;
2501    let f01 = f00 - b;
2502    let f11 = f10 - b;
2503    let g00 = -e;
2504    let g10 = g00 - b;
2505    let g01 = g00 + *c;
2506    let g11 = g10 + *c;
2507    let svalue = [
2508      clamped_root (*a, f00, f10),
2509      clamped_root (*a, f01, f11)
2510    ];
2511    let mut classify = [i8::MAX; 2];
2512    for i in 0..2 {
2513      if svalue[i] <= 0.0 {
2514        classify[i] = -1
2515      } else if svalue[i] >= 1.0 {
2516        classify[i] = 1
2517      } else {
2518        classify[i] = 0
2519      }
2520    }
2521    let param_a;
2522    let param_b;
2523    if classify[0] == -1 && classify[1] == -1 {
2524      param_a = 0.0;
2525      param_b = clamped_root (*c, g00, g01);
2526    } else if classify[0] == 1 && classify[1] == 1 {
2527      param_a = 1.0;
2528      param_b = clamped_root (*c, g10, g11);
2529    } else {
2530      let mut edge = [u8::MAX; 2];
2531      let mut end  = [[f64::NAN; 2]; 2];
2532      if classify[0] == -1 {
2533        edge[0] = 0;
2534        end[0][0] = 0.0;
2535        end[0][1] = f00 / b;
2536        if end[0][1] < 0.0 || end[0][1] > 1.0 {
2537          end[0][1] = 0.5
2538        }
2539        if classify[1] == 0 {
2540          edge[1] = 3;
2541          end[1][0] = svalue[1];
2542          end[1][1] = 1.0;
2543        } else {
2544          debug_assert_eq!(classify[1], 1);
2545          edge[1] = 1;
2546          end[1][0] = 1.0;
2547          end[1][1] = f10 / b;
2548          if end[1][1] < 0.0 || end[1][1] > 1.0 {
2549            end[1][1] = 0.5
2550          }
2551        }
2552      } else if classify[0] == 0 {
2553        edge[0] = 2;
2554        end[0][0] = svalue[0];
2555        end[0][1] = 0.0;
2556        if classify[1] == -1 {
2557          edge[1] = 0;
2558          end[1][0] = 0.0;
2559          end[1][1] = f00 / b;
2560          if end[1][1] < 0.0 || end[1][1] > 1.0 {
2561            end[1][1] = 0.5
2562          }
2563        } else if classify[1] == 0 {
2564          edge[1] = 3;
2565          end[1][0] = svalue[1];
2566          end[1][1] = 1.0;
2567        } else {
2568          debug_assert_eq!(classify[1], 1);
2569          edge[1] = 1;
2570          end[1][0] = 1.0;
2571          end[1][1] = f10 / b;
2572          if end[1][1] < 0.0 || end[1][1] > 1.0 {
2573            end[1][1] = 0.5
2574          }
2575        }
2576      } else {
2577        debug_assert_eq!(classify[0], 1);
2578        edge[0] = 1;
2579        end[0][0] = 1.0;
2580        end[0][1] = f10 / b;
2581        if end[0][1] < 0.0 || end[0][1] > 1.0 {
2582          end[0][1] = 0.5
2583        }
2584        if classify[1] == 0 {
2585          edge[1] = 3;
2586          end[1][0] = svalue[1];
2587          end[1][1] = 1.0;
2588        } else {
2589          edge[1] = 0;
2590          end[1][0] = 0.0;
2591          end[1][1] = f00 / b;
2592          if end[1][1] < 0.0 || end[1][1] > 1.0 {
2593            end[1][1] = 0.5
2594          }
2595        }
2596      }
2597      // compute minimum parameters
2598      let delta = end[1][1] - end[0][1];
2599      //let h0 = delta * (-b * end[0][0] + c * end[0][1] - e);
2600      let h0 = delta * ((-b).mul_add (end[0][0], c.mul_add (end[0][1], -e)));
2601      if h0 >= 0.0 {
2602        param_a = (end[0][0] + end[1][0]) / 2.0;
2603        param_b = (end[0][1] + end[1][1]) / 2.0;
2604      } else {
2605        //let h1 = delta * (-b * end[1][0] + c * end[1][1] - e);
2606        let h1 = delta * ((-b).mul_add (end[1][0], c.mul_add (end[1][1], -e)));
2607        if h1 <= 0.0 {
2608          if edge[1] == 0 {
2609            param_a = 0.0;
2610            param_b = clamped_root (*c, g00, g01);
2611          } else if edge[1] == 1 {
2612            param_a = 1.0;
2613            param_b = clamped_root (*c, g10, g11);
2614          } else {
2615            param_a = end[1][0];
2616            param_b = end[1][1];
2617          }
2618        } else {
2619          debug_assert!(h0 < 0.0);
2620          debug_assert!(h1 > 0.0);
2621          let z = (h0 / (h0 - h1)).clamp (0.0, 1.0);
2622          let omz = 1.0 - z;
2623          param_a = omz * end[0][0] + z * end[1][0];
2624          param_b = omz * end[0][1] + z * end[1][1];
2625        }
2626      }
2627      debug_assert_ne!(edge[0], u8::MAX);
2628      debug_assert_ne!(edge[1], u8::MAX);
2629      debug_assert_ne!(end[0][0], f64::NAN);
2630      debug_assert_ne!(end[0][1], f64::NAN);
2631      debug_assert_ne!(end[1][0], f64::NAN);
2632      debug_assert_ne!(end[1][1], f64::NAN);
2633    }
2634    let nearest_a = Point3::from (segment_a.point_a().0 * (1.0 - param_a)
2635      + segment_a.point_b().0 * param_a);
2636    let nearest_b = Point3::from (segment_b.point_a().0 * (1.0 - param_b)
2637      + segment_b.point_b().0 * param_b);
2638    let a_to_b    = nearest_b - nearest_a;
2639    let distance  = a_to_b.magnitude();
2640    let half_axis = a_to_b * 0.5;
2641    let midpoint  = nearest_a + half_axis;
2642    let normal    = if approx::relative_eq!(nearest_a, nearest_b,
2643      max_relative = f64::default_max_relative() * 2.0.powi (20),
2644      epsilon      = f64::default_epsilon() * 2.0.powi (20))
2645    {
2646      // generate a normal vector perpendicular to the edges
2647      let mut perp = vec_a.cross (*vec_b);
2648      if perp == Vector3::zero() {
2649        // edges are parallel
2650        perp = vec_a.cross (Vector3::unit_z());
2651        if perp == Vector3::zero() {
2652          // edges are parallel to the Z axis, use the X axis instead
2653          perp = vec_a.cross (Vector3::unit_x());
2654        }
2655      }
2656      Unit3::normalize (perp)
2657    } else {
2658      Unit3::normalize (-a_to_b)
2659    };
2660    Proximity { distance, half_axis, midpoint, normal }
2661  }
2662
2663  pub fn query_segment_point (
2664    segment : &geometry::Segment3 <f64>,
2665    point   : &Point3 <f64>
2666  ) -> Self {
2667    let (s, nearest_a) = segment.nearest_point (*point);
2668    let nearest_b      = *point;
2669    let a_to_b         = nearest_b - nearest_a;
2670    let distance       = a_to_b.magnitude();
2671    let half_axis      = a_to_b * 0.5;
2672    let midpoint       = nearest_a + half_axis;
2673    let normal         = if approx::relative_eq!(nearest_a, nearest_b) {
2674      let perp = if *s == 0.0 {
2675        debug_assert_eq!(nearest_a, segment.point_a());
2676        -segment.vector()
2677      } else if *s == 1.0 {
2678        segment.vector()
2679      } else {
2680        segment.perpendicular()
2681      };
2682      *perp
2683    } else {
2684      -a_to_b
2685    }.normalize();
2686    Proximity { distance, half_axis, midpoint, normal }
2687  }
2688}
2689
2690/// Given a point on a triangle defined as s(b - a) + t(c - a), compute the distance to
2691/// each edge of the triangle
2692fn triangle_distance_to_edges (triangle : geometry::Triangle3 <f64>, s : f64, t : f64)
2693  -> (f64, f64, f64)
2694{
2695  use approx::{AbsDiffEq, RelativeEq};
2696  debug_assert!(s + t <= 1.0);
2697  // barycentric coordinates of each vertex
2698  let b0        = 1.0 - s - t;
2699  let b1        = s;
2700  let b2        = t;
2701  // edges opposite to each vertex
2702  let edge0     = triangle.point_c() - triangle.point_b();
2703  let edge1     = triangle.point_c() - triangle.point_a();
2704  let edge2     = triangle.point_b() - triangle.point_a();
2705  let len_e0_sq = edge0.magnitude_squared();
2706  let len_e1_sq = edge1.magnitude_squared();
2707  let len_e2_sq = edge2.magnitude_squared();
2708  // length of triangle normal is 2 * area
2709  let area_2_sq = triangle.normal().magnitude_squared();
2710  // convert barycentric to trilinear coordinates
2711  let d0        = b0 * (area_2_sq / len_e0_sq).sqrt();
2712  let d1        = b1 * (area_2_sq / len_e1_sq).sqrt();
2713  let d2        = b2 * (area_2_sq / len_e2_sq).sqrt();
2714  approx::assert_relative_eq!(
2715    triangle.barycentric ([b0, b1, b2].into()),
2716    triangle.trilinear ([d0, d1, d2].into()),
2717    max_relative = f64::default_max_relative() * 32.0,
2718    epsilon = f64::default_epsilon() * 32.0);
2719  (d0, d1, d2)
2720}
2721
2722fn clamped_root (slope : f64, h0 : f64, h1 : f64) -> f64 {
2723  // helper function for Eberly GeometricTools methods
2724  if h0 < 0.0 {
2725    if h1 > 0.0 {
2726      let mut root = -h0 / slope;
2727      if root > 1.0 {
2728        root = 0.5
2729      }
2730      root
2731    } else {
2732      1.0
2733    }
2734  } else {
2735    0.0
2736  }
2737}
2738
2739#[cfg(test)]
2740mod tests {
2741  use approx::AbsDiffEq;
2742  use rand;
2743  use rand_distr;
2744  use rand_xorshift::XorShiftRng;
2745  use std::f64::consts::*;
2746  use super::*;
2747
2748  #[test]
2749  fn segment_point() {
2750    use rand::SeedableRng;
2751    use rand_distr::Distribution;
2752    // point above segment
2753    let segment = geometry::Segment3::noisy (
2754      [-1.0, 0.0, 0.0].into(),
2755      [ 1.0, 0.0, 0.0].into());
2756    let point = point3 (0.0, 1.0, 0.0);
2757    let proximity = Proximity::query_segment_point (&segment, &point);
2758    assert_eq!(proximity.nearest_a(), [0.0, 0.0, 0.0].into());
2759    assert_eq!(proximity.nearest_b(), [0.0, 1.0, 0.0].into());
2760    assert_eq!(proximity.distance, 1.0);
2761    assert_eq!(proximity.half_axis, [0.0, 0.5, 0.0].into());
2762    assert_eq!(proximity.midpoint, [0.0, 0.5, 0.0].into());
2763    assert_eq!(proximity.normal, -Unit3::axis_y());
2764    // point on segment: middle
2765    let segment = geometry::Segment3::noisy (
2766      [-1.0, 0.0, 0.0].into(),
2767      [ 1.0, 0.0, 0.0].into());
2768    let point = point3 (0.0, 0.0, 0.0);
2769    let proximity = Proximity::query_segment_point (&segment, &point);
2770    assert_eq!(proximity.nearest_a(), [0.0, 0.0, 0.0].into());
2771    assert_eq!(proximity.nearest_b(), [0.0, 0.0, 0.0].into());
2772    assert_eq!(proximity.distance, 0.0);
2773    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
2774    assert_eq!(proximity.midpoint, [0.0, 0.0, 0.0].into());
2775    assert_eq!(proximity.normal, Unit3::axis_z());
2776    // point on segment: end
2777    let segment = geometry::Segment3::noisy (
2778      [-1.0, 0.0, 0.0].into(),
2779      [ 1.0, 0.0, 0.0].into());
2780    let point = point3 (1.0, 0.0, 0.0);
2781    let proximity = Proximity::query_segment_point (&segment, &point);
2782    assert_eq!(proximity.nearest_a(), [1.0, 0.0, 0.0].into());
2783    assert_eq!(proximity.nearest_b(), [1.0, 0.0, 0.0].into());
2784    assert_eq!(proximity.distance, 0.0);
2785    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
2786    assert_eq!(proximity.midpoint, [1.0, 0.0, 0.0].into());
2787    assert_eq!(proximity.normal, segment.vector().normalize());
2788    // test that normal is invariant under translation
2789    let segment = geometry::Segment3::noisy (
2790      [-1.0, 0.0, 0.0].into(),
2791      [ 1.0, 0.0, 0.0].into());
2792    let point = point3 (0.0, 0.0, 0.0);
2793    let mut rng = XorShiftRng::seed_from_u64 (0);
2794    let std_normal = rand_distr::StandardNormal;
2795    // cauchy sample
2796    let randf = |rng : &mut XorShiftRng| {
2797      let x : f64 = std_normal.sample (rng);
2798      let y : f64 = std_normal.sample (rng);
2799      x / y
2800    };
2801    for _ in 0..10000 {
2802      let translation = vector3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
2803      let mut segment = segment;
2804      segment.translate (translation);
2805      let point = point + translation;
2806      let proximity = Proximity::query_segment_point (&segment, &point);
2807      assert_eq!(proximity.normal, Unit3::axis_z());
2808    }
2809  }
2810
2811  #[test]
2812  fn segment_segment() {
2813    use approx::RelativeEq;
2814    use rand::SeedableRng;
2815    use rand_distr::Distribution;
2816    // parallel
2817    let segment1 = geometry::Segment3::noisy (
2818      [-2.0, 2.0, 0.0].into(),
2819      [ 2.0, 2.0, 0.0].into());
2820    let segment2 = geometry::Segment3::noisy (
2821      [-1.0, 0.0, 0.0].into(),
2822      [ 0.0, 0.0, 0.0].into());
2823    let proximity = Proximity::query_segment_segment (&segment1, &segment2);
2824    assert_eq!(proximity.nearest_a(), [-0.5, 2.0, 0.0].into());
2825    assert_eq!(proximity.nearest_b(), [-0.5, 0.0, 0.0].into());
2826    assert_eq!(proximity.distance, 2.0);
2827    assert_eq!(proximity.half_axis, [0.0, -1.0, 0.0].into());
2828    assert_eq!(proximity.midpoint, [-0.5, 1.0, 0.0].into());
2829    assert_eq!(proximity.normal, Unit3::noisy ([0.0, 1.0, 0.0].into()));
2830    let segment1 = geometry::Segment3::noisy (
2831      [-2.0, 2.0, 0.0].into(),
2832      [ 2.0, 2.0, 0.0].into());
2833    let segment2 = geometry::Segment3::noisy (
2834      [ 0.0, 0.0, 0.0].into(),
2835      [-3.0, 0.0, 0.0].into());
2836    let proximity = Proximity::query_segment_segment (&segment1, &segment2);
2837    assert_eq!(proximity.nearest_a(), [-1.0, 2.0, 0.0].into());
2838    assert_eq!(proximity.nearest_b(), [-1.0, 0.0, 0.0].into());
2839    assert_eq!(proximity.distance, 2.0);
2840    assert_eq!(proximity.half_axis, [0.0, -1.0, 0.0].into());
2841    assert_eq!(proximity.midpoint, [-1.0, 1.0, 0.0].into());
2842    assert_eq!(proximity.normal, Unit3::noisy ([0.0, 1.0, 0.0].into()));
2843    let segment1 = geometry::Segment3::noisy (
2844      [-2.0, 2.0, 0.0].into(),
2845      [ 2.0, 2.0, 0.0].into());
2846    let segment2 = geometry::Segment3::noisy (
2847      [ 0.0, 0.0, 0.0].into(),
2848      [ 3.0, 0.0, 0.0].into());
2849    let proximity = Proximity::query_segment_segment (&segment1, &segment2);
2850    assert_eq!(proximity.nearest_a(), [ 1.0, 2.0, 0.0].into());
2851    assert_eq!(proximity.nearest_b(), [ 1.0, 0.0, 0.0].into());
2852    assert_eq!(proximity.distance, 2.0);
2853    assert_eq!(proximity.half_axis, [0.0, -1.0, 0.0].into());
2854    assert_eq!(proximity.midpoint, [ 1.0, 1.0, 0.0].into());
2855    assert_eq!(proximity.normal, Unit3::noisy ([0.0, 1.0, 0.0].into()));
2856    let segment1 = geometry::Segment3::noisy (
2857      [-2.0, 2.0, 0.0].into(),
2858      [ 2.0, 2.0, 0.0].into());
2859    let segment2 = geometry::Segment3::noisy (
2860      [ 3.0, 0.0, 0.0].into(),
2861      [ 0.0, 0.0, 0.0].into());
2862    let proximity = Proximity::query_segment_segment (&segment1, &segment2);
2863    assert_eq!(proximity.nearest_a(), [ 1.0, 2.0, 0.0].into());
2864    assert_eq!(proximity.nearest_b(), [ 1.0, 0.0, 0.0].into());
2865    assert_eq!(proximity.distance, 2.0);
2866    assert_eq!(proximity.half_axis, [0.0, -1.0, 0.0].into());
2867    assert_eq!(proximity.midpoint, [ 1.0, 1.0, 0.0].into());
2868    assert_eq!(proximity.normal, Unit3::noisy ([0.0, 1.0, 0.0].into()));
2869    let segment1 = geometry::Segment3::noisy (
2870      [-2.0, 2.0, 0.0].into(),
2871      [ 2.0, 2.0, 0.0].into());
2872    let segment2 = geometry::Segment3::noisy (
2873      [-3.0, 0.0, 0.0].into(),
2874      [ 0.0, 0.0, 0.0].into());
2875    let proximity = Proximity::query_segment_segment (&segment1, &segment2);
2876    assert_eq!(proximity.nearest_a(), [0.0, 2.0, 0.0].into());
2877    assert_eq!(proximity.nearest_b(), [0.0, 0.0, 0.0].into());
2878    assert_eq!(proximity.distance, 2.0);
2879    assert_eq!(proximity.half_axis, [0.0, -1.0, 0.0].into());
2880    assert_eq!(proximity.midpoint, [0.0, 1.0, 0.0].into());
2881    assert_eq!(proximity.normal, Unit3::noisy ([0.0, 1.0, 0.0].into()));
2882    let segment1 = geometry::Segment3::noisy (
2883      [-2.0, 2.0, 0.0].into(),
2884      [ 2.0, 2.0, 0.0].into());
2885    let segment2 = geometry::Segment3::noisy (
2886      [ 1.0, -1.0, 0.0].into(),
2887      [ 1.0,  1.0, 0.0].into());
2888    let proximity = Proximity::query_segment_segment (&segment1, &segment2);
2889    assert_eq!(proximity.nearest_a(), [ 1.0, 2.0, 0.0].into());
2890    assert_eq!(proximity.nearest_b(), [ 1.0, 1.0, 0.0].into());
2891    assert_eq!(proximity.distance, 1.0);
2892    assert_eq!(proximity.half_axis, [0.0, -0.5, 0.0].into());
2893    assert_eq!(proximity.midpoint, [ 1.0, 1.5, 0.0].into());
2894    assert_eq!(proximity.normal, Unit3::noisy ([0.0, 1.0, 0.0].into()));
2895    let segment1 = geometry::Segment3::noisy (
2896      [-2.0, 2.0, 0.0].into(),
2897      [ 2.0, 2.0, 0.0].into());
2898    let segment2 = geometry::Segment3::noisy (
2899      [-3.0, 0.0, 0.0].into(),
2900      [-3.0, 3.0, 0.0].into());
2901    let proximity = Proximity::query_segment_segment (&segment1, &segment2);
2902    assert_eq!(proximity.nearest_a(), [-2.0, 2.0, 0.0].into());
2903    assert_eq!(proximity.nearest_b(), [-3.0, 2.0, 0.0].into());
2904    assert_eq!(proximity.distance, 1.0);
2905    assert_eq!(proximity.half_axis, [-0.5, 0.0, 0.0].into());
2906    assert_eq!(proximity.midpoint, [-2.5, 2.0, 0.0].into());
2907    assert_eq!(proximity.normal, Unit3::noisy ([1.0, 0.0, 0.0].into()));
2908    let segment1 = geometry::Segment3::noisy (
2909      [-2.0, 2.0, 0.0].into(),
2910      [ 2.0, 2.0, 0.0].into());
2911    let segment2 = geometry::Segment3::noisy (
2912      [ 3.0, 0.0, 0.0].into(),
2913      [ 3.0, 3.0, 0.0].into());
2914    let proximity = Proximity::query_segment_segment (&segment1, &segment2);
2915    assert_eq!(proximity.nearest_a(), [2.0, 2.0, 0.0].into());
2916    assert_eq!(proximity.nearest_b(), [3.0, 2.0, 0.0].into());
2917    assert_eq!(proximity.distance, 1.0);
2918    assert_eq!(proximity.half_axis, [0.5, 0.0, 0.0].into());
2919    assert_eq!(proximity.midpoint, [2.5, 2.0, 0.0].into());
2920    assert_eq!(proximity.normal, Unit3::noisy ([-1.0, 0.0, 0.0].into()));
2921    // normal should be invariant under translation
2922    let segment1 = geometry::Segment3::noisy (
2923      [ 0.0,  1.0, 0.0].into(),
2924      [ 0.0, -1.0, 0.0].into());
2925    let segment2 = geometry::Segment3::noisy (
2926      [ -0.5, -0.5, -0.5].into(),
2927      [  0.5,  0.5,  0.5].into());
2928    let proximity1 = Proximity::query_segment_segment (&segment1, &segment2);
2929    let mut rng = XorShiftRng::seed_from_u64 (0);
2930    let std_normal = rand_distr::StandardNormal;
2931    // cauchy sample
2932    let randf = |rng : &mut XorShiftRng| {
2933      let x : f64 = std_normal.sample (rng);
2934      let y : f64 = std_normal.sample (rng);
2935      x / y
2936    };
2937    for _ in 0..10000 {
2938      let translation = vector3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
2939      let mut segment1 = segment1;
2940      let mut segment2 = segment2;
2941      segment1.translate (translation);
2942      segment2.translate (translation);
2943      let proximity = Proximity::query_segment_segment (&segment1, &segment2);
2944      approx::assert_relative_eq!(*proximity.normal, *proximity1.normal,
2945        max_relative = f64::default_max_relative() * 2.0.powi (10))
2946    }
2947  }
2948
2949  #[test]
2950  fn line_point() {
2951    use rand::SeedableRng;
2952    use rand_distr::Distribution;
2953    // point above line
2954    let line = geometry::Segment3::noisy (
2955      [-1.0, 0.0, 0.0].into(),
2956      [ 1.0, 0.0, 0.0].into());
2957    let point = point3 (0.0, 1.0, 0.0);
2958    let proximity = Proximity::query_line_point (&line, &point);
2959    assert_eq!(proximity.nearest_a(), [0.0, 0.0, 0.0].into());
2960    assert_eq!(proximity.nearest_b(), [0.0, 1.0, 0.0].into());
2961    assert_eq!(proximity.distance, 1.0);
2962    assert_eq!(proximity.half_axis, [0.0, 0.5, 0.0].into());
2963    assert_eq!(proximity.midpoint, [0.0, 0.5, 0.0].into());
2964    assert_eq!(proximity.normal, -Unit3::axis_y());
2965    // point on line: middle
2966    let line = geometry::Segment3::noisy (
2967      [-1.0, 0.0, 0.0].into(),
2968      [ 1.0, 0.0, 0.0].into());
2969    let point = point3 (0.0, 0.0, 0.0);
2970    let proximity = Proximity::query_line_point (&line, &point);
2971    assert_eq!(proximity.nearest_a(), [0.0, 0.0, 0.0].into());
2972    assert_eq!(proximity.nearest_b(), [0.0, 0.0, 0.0].into());
2973    assert_eq!(proximity.distance, 0.0);
2974    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
2975    assert_eq!(proximity.midpoint, [0.0, 0.0, 0.0].into());
2976    assert_eq!(proximity.normal, Unit3::axis_z());
2977    // point on line: end
2978    let line = geometry::Segment3::noisy (
2979      [-1.0, 0.0, 0.0].into(),
2980      [ 1.0, 0.0, 0.0].into());
2981    let point = point3 (1.0, 0.0, 0.0);
2982    let proximity = Proximity::query_line_point (&line, &point);
2983    assert_eq!(proximity.nearest_a(), [1.0, 0.0, 0.0].into());
2984    assert_eq!(proximity.nearest_b(), [1.0, 0.0, 0.0].into());
2985    assert_eq!(proximity.distance, 0.0);
2986    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
2987    assert_eq!(proximity.midpoint, [1.0, 0.0, 0.0].into());
2988    assert_eq!(proximity.normal, Unit3::axis_z());
2989    // test that normal is invariant under translation
2990    let line = geometry::Segment3::noisy (
2991      [-1.0, 0.0, 0.0].into(),
2992      [ 1.0, 0.0, 0.0].into());
2993    let point = point3 (0.0, 0.0, 0.0);
2994    let mut rng = XorShiftRng::seed_from_u64 (0);
2995    let std_normal = rand_distr::StandardNormal;
2996    // cauchy sample
2997    let randf = |rng : &mut XorShiftRng| {
2998      let x : f64 = std_normal.sample (rng);
2999      let y : f64 = std_normal.sample (rng);
3000      x / y
3001    };
3002    for _ in 0..10000 {
3003      let translation = vector3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3004      let mut line = line;
3005      line.translate (translation);
3006      let point = point + translation;
3007      let proximity = Proximity::query_line_point (&line, &point);
3008      assert_eq!(proximity.normal, Unit3::axis_z());
3009    }
3010  }
3011
3012  #[test]
3013  fn line_segment() {
3014    use approx::RelativeEq;
3015    use rand::SeedableRng;
3016    use rand_distr::Distribution;
3017    // parallel
3018    let line    = geometry::Segment3::noisy (
3019      [-2.0, 0.0, 0.0].into(),
3020      [ 2.0, 0.0, 0.0].into());
3021    let segment = geometry::Segment3::noisy (
3022      [-2.0, 2.0, 0.0].into(),
3023      [ 2.0, 2.0, 0.0].into());
3024    let proximity = Proximity::query_line_segment (&line, &segment);
3025    assert_eq!(proximity.nearest_a(), [-2.0, 0.0, 0.0].into());
3026    assert_eq!(proximity.nearest_b(), [-2.0, 2.0, 0.0].into());
3027    assert_eq!(proximity.distance, 2.0);
3028    assert_eq!(proximity.half_axis, [0.0, 1.0, 0.0].into());
3029    assert_eq!(proximity.midpoint, [-2.0, 1.0, 0.0].into());
3030    assert_eq!(proximity.normal, -Unit3::axis_y());
3031    let line    = geometry::Segment3::noisy (
3032      [-5.0, 0.0, 0.0].into(),
3033      [-2.0, 0.0, 0.0].into());
3034    let segment = geometry::Segment3::noisy (
3035      [-2.0, 2.0, 0.0].into(),
3036      [ 2.0, 2.0, 0.0].into());
3037    let proximity = Proximity::query_line_segment (&line, &segment);
3038    assert_eq!(proximity.nearest_a(), [-2.0, 0.0, 0.0].into());
3039    assert_eq!(proximity.nearest_b(), [-2.0, 2.0, 0.0].into());
3040    assert_eq!(proximity.distance, 2.0);
3041    assert_eq!(proximity.half_axis, [0.0, 1.0, 0.0].into());
3042    assert_eq!(proximity.midpoint, [-2.0, 1.0, 0.0].into());
3043    assert_eq!(proximity.normal, -Unit3::axis_y());
3044    // perpendicular
3045    let line    = geometry::Segment3::noisy (
3046      [0.0, -1.0, 0.0].into(),
3047      [0.0,  1.0, 0.0].into());
3048    let segment = geometry::Segment3::noisy (
3049      [1.0,  1.0, 0.0].into(),
3050      [3.0,  1.0, 0.0].into());
3051    let proximity = Proximity::query_line_segment (&line, &segment);
3052    assert_eq!(proximity.nearest_a(), [0.0, 1.0, 0.0].into());
3053    assert_eq!(proximity.nearest_b(), [1.0, 1.0, 0.0].into());
3054    assert_eq!(proximity.distance, 1.0);
3055    assert_eq!(proximity.half_axis, [0.5, 0.0, 0.0].into());
3056    assert_eq!(proximity.midpoint, [0.5, 1.0, 0.0].into());
3057    assert_eq!(proximity.normal, -Unit3::axis_x());
3058    let line    = geometry::Segment3::noisy (
3059      [ 0.0, -1.0, 0.0].into(),
3060      [ 0.0,  1.0, 0.0].into());
3061    let segment = geometry::Segment3::noisy (
3062      [-3.0,  1.0, 0.0].into(),
3063      [-1.0,  1.0, 0.0].into());
3064    let proximity = Proximity::query_line_segment (&line, &segment);
3065    assert_eq!(proximity.nearest_a(), [ 0.0, 1.0, 0.0].into());
3066    assert_eq!(proximity.nearest_b(), [-1.0, 1.0, 0.0].into());
3067    assert_eq!(proximity.distance, 1.0);
3068    assert_eq!(proximity.half_axis, [-0.5, 0.0, 0.0].into());
3069    assert_eq!(proximity.midpoint, [-0.5, 1.0, 0.0].into());
3070    assert_eq!(proximity.normal, Unit3::axis_x());
3071    // in front
3072    let line    = geometry::Segment3::noisy (
3073      [ 0.0, -1.0, 0.0].into(),
3074      [ 0.0,  1.0, 0.0].into());
3075    let segment = geometry::Segment3::noisy (
3076      [-2.0,  2.0, 1.0].into(),
3077      [ 2.0,  2.0, 1.0].into());
3078    let proximity = Proximity::query_line_segment (&line, &segment);
3079    assert_eq!(proximity.nearest_a(), [ 0.0, 2.0, 0.0].into());
3080    assert_eq!(proximity.nearest_b(), [ 0.0, 2.0, 1.0].into());
3081    assert_eq!(proximity.distance, 1.0);
3082    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.5].into());
3083    assert_eq!(proximity.midpoint, [0.0, 2.0, 0.5].into());
3084    assert_eq!(proximity.normal, -Unit3::axis_z());
3085    // intersection: middle
3086    let line    = geometry::Segment3::noisy (
3087      [ 0.0, -1.0, 0.0].into(),
3088      [ 0.0,  1.0, 0.0].into());
3089    let segment = geometry::Segment3::noisy (
3090      [-2.0,  2.0, 0.0].into(),
3091      [ 2.0,  2.0, 0.0].into());
3092    let proximity = Proximity::query_line_segment (&line, &segment);
3093    assert_eq!(proximity.nearest_a(), [ 0.0, 2.0, 0.0].into());
3094    assert_eq!(proximity.nearest_b(), [ 0.0, 2.0, 0.0].into());
3095    assert_eq!(proximity.distance, 0.0);
3096    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3097    assert_eq!(proximity.midpoint, [0.0, 2.0, 0.0].into());
3098    assert_eq!(proximity.normal, Unit3::axis_z());
3099    // intersection: endpoint A
3100    let line    = geometry::Segment3::noisy (
3101      [-2.0, -1.0, 0.0].into(),
3102      [-2.0,  1.0, 0.0].into());
3103    let segment = geometry::Segment3::noisy (
3104      [-2.0,  2.0, 0.0].into(),
3105      [ 2.0,  2.0, 0.0].into());
3106    let proximity = Proximity::query_line_segment (&line, &segment);
3107    assert_eq!(proximity.nearest_a(), [-2.0, 2.0, 0.0].into());
3108    assert_eq!(proximity.nearest_b(), [-2.0, 2.0, 0.0].into());
3109    assert_eq!(proximity.distance, 0.0);
3110    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3111    assert_eq!(proximity.midpoint, [-2.0, 2.0, 0.0].into());
3112    assert_eq!(proximity.normal, -Unit3::axis_x());
3113    let line    = geometry::Segment3::noisy (
3114      [-2.0, -1.0, 0.0].into(),
3115      [-2.0,  1.0, 0.0].into());
3116    let segment = geometry::Segment3::noisy (
3117      [-2.0,   2.0, 0.0].into(),
3118      [ 2.0,  -2.0, 0.0].into());
3119    let proximity = Proximity::query_line_segment (&line, &segment);
3120    assert_eq!(proximity.nearest_a(), [-2.0, 2.0, 0.0].into());
3121    assert_eq!(proximity.nearest_b(), [-2.0, 2.0, 0.0].into());
3122    assert_eq!(proximity.distance, 0.0);
3123    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3124    assert_eq!(proximity.midpoint, [-2.0, 2.0, 0.0].into());
3125    assert_eq!(proximity.normal, -Unit3::axis_x());
3126    // intersection: endpoint B
3127    let line    = geometry::Segment3::noisy (
3128      [ 2.0, -1.0, 0.0].into(),
3129      [ 2.0,  1.0, 0.0].into());
3130    let segment = geometry::Segment3::noisy (
3131      [-2.0,  2.0, 0.0].into(),
3132      [ 2.0,  2.0, 0.0].into());
3133    let proximity = Proximity::query_line_segment (&line, &segment);
3134    assert_eq!(proximity.nearest_a(), [2.0, 2.0, 0.0].into());
3135    assert_eq!(proximity.nearest_b(), [2.0, 2.0, 0.0].into());
3136    assert_eq!(proximity.distance, 0.0);
3137    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3138    assert_eq!(proximity.midpoint, [2.0, 2.0, 0.0].into());
3139    assert_eq!(proximity.normal, Unit3::axis_x());
3140    let line    = geometry::Segment3::noisy (
3141      [ 2.0, -1.0, 0.0].into(),
3142      [ 2.0,  1.0, 0.0].into());
3143    let segment = geometry::Segment3::noisy (
3144      [-2.0,  2.0, 0.0].into(),
3145      [ 2.0,  0.0, 0.0].into());
3146    let proximity = Proximity::query_line_segment (&line, &segment);
3147    assert_eq!(proximity.nearest_a(), [2.0, 0.0, 0.0].into());
3148    assert_eq!(proximity.nearest_b(), [2.0, 0.0, 0.0].into());
3149    assert_eq!(proximity.distance, 0.0);
3150    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3151    assert_eq!(proximity.midpoint, [2.0, 0.0, 0.0].into());
3152    assert_eq!(proximity.normal, Unit3::axis_x());
3153    // intersection: parallel
3154    let line    = geometry::Segment3::noisy (
3155      [-1.0, 2.0, 0.0].into(),
3156      [ 0.0, 2.0, 0.0].into());
3157    let segment = geometry::Segment3::noisy (
3158      [-2.0, 2.0, 0.0].into(),
3159      [ 2.0, 2.0, 0.0].into());
3160    let proximity = Proximity::query_line_segment (&line, &segment);
3161    assert_eq!(proximity.nearest_a(), [-2.0, 2.0, 0.0].into());
3162    assert_eq!(proximity.nearest_b(), [-2.0, 2.0, 0.0].into());
3163    assert_eq!(proximity.distance, 0.0);
3164    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3165    assert_eq!(proximity.midpoint, [-2.0, 2.0, 0.0].into());
3166    assert_eq!(proximity.normal, Unit3::axis_z());
3167    // normal should be invariant under translation
3168    let line = geometry::Segment3::noisy (
3169      [ -0.5, -0.5, -0.5].into(),
3170      [  0.5,  0.5,  0.5].into());
3171    let segment = geometry::Segment3::noisy (
3172      [ 0.0,  1.0, 0.0].into(),
3173      [ 0.0, -1.0, 0.0].into());
3174    let proximity1 = Proximity::query_line_segment (&line, &segment);
3175    let mut rng = XorShiftRng::seed_from_u64 (0);
3176    let std_normal = rand_distr::StandardNormal;
3177    // cauchy sample
3178    let randf = |rng : &mut XorShiftRng| {
3179      let x : f64 = std_normal.sample (rng);
3180      let y : f64 = std_normal.sample (rng);
3181      x / y
3182    };
3183    for _ in 0..10000 {
3184      let translation = vector3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3185      let mut line    = line;
3186      let mut segment = segment;
3187      line.translate (translation);
3188      segment.translate (translation);
3189      let proximity = Proximity::query_line_segment (&line, &segment);
3190      approx::assert_relative_eq!(*proximity.normal, *proximity1.normal,
3191        max_relative = f64::default_max_relative() * 2.0.powi (10))
3192    }
3193  }
3194
3195  #[test]
3196  fn line_line() {
3197    use approx::RelativeEq;
3198    use rand::SeedableRng;
3199    use rand_distr::Distribution;
3200    // parallel
3201    let line_a    = geometry::Segment3::noisy (
3202      [-2.0, 0.0, 0.0].into(),
3203      [ 2.0, 0.0, 0.0].into());
3204    let line_b = geometry::Segment3::noisy (
3205      [-2.0, 2.0, 0.0].into(),
3206      [ 2.0, 2.0, 0.0].into());
3207    let proximity = Proximity::query_line_line (&line_a, &line_b);
3208    assert_eq!(proximity.nearest_a(), [-2.0, 0.0, 0.0].into());
3209    assert_eq!(proximity.nearest_b(), [-2.0, 2.0, 0.0].into());
3210    assert_eq!(proximity.distance, 2.0);
3211    assert_eq!(proximity.half_axis, [0.0, 1.0, 0.0].into());
3212    assert_eq!(proximity.midpoint, [-2.0, 1.0, 0.0].into());
3213    assert_eq!(proximity.normal, -Unit3::axis_y());
3214    let line_a    = geometry::Segment3::noisy (
3215      [-5.0, 0.0, 0.0].into(),
3216      [-2.0, 0.0, 0.0].into());
3217    let line_b = geometry::Segment3::noisy (
3218      [-2.0, 2.0, 0.0].into(),
3219      [ 2.0, 2.0, 0.0].into());
3220    let proximity = Proximity::query_line_line (&line_a, &line_b);
3221    assert_eq!(proximity.nearest_a(), [-2.0, 0.0, 0.0].into());
3222    assert_eq!(proximity.nearest_b(), [-2.0, 2.0, 0.0].into());
3223    assert_eq!(proximity.distance, 2.0);
3224    assert_eq!(proximity.half_axis, [0.0, 1.0, 0.0].into());
3225    assert_eq!(proximity.midpoint, [-2.0, 1.0, 0.0].into());
3226    assert_eq!(proximity.normal, -Unit3::axis_y());
3227    // perpendicular
3228    let line_a    = geometry::Segment3::noisy (
3229      [0.0, -1.0, 0.0].into(),
3230      [0.0,  1.0, 0.0].into());
3231    let line_b = geometry::Segment3::noisy (
3232      [1.0,  1.0, 0.0].into(),
3233      [3.0,  1.0, 0.0].into());
3234    let proximity = Proximity::query_line_line (&line_a, &line_b);
3235    assert_eq!(proximity.nearest_a(), [0.0, 1.0, 0.0].into());
3236    assert_eq!(proximity.nearest_b(), [0.0, 1.0, 0.0].into());
3237    assert_eq!(proximity.distance, 0.0);
3238    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3239    assert_eq!(proximity.midpoint, [0.0, 1.0, 0.0].into());
3240    assert_eq!(proximity.normal, -Unit3::axis_z());
3241    let line_a    = geometry::Segment3::noisy (
3242      [ 0.0, -1.0, 0.0].into(),
3243      [ 0.0,  1.0, 0.0].into());
3244    let line_b = geometry::Segment3::noisy (
3245      [-3.0,  1.0, 0.0].into(),
3246      [-1.0,  1.0, 0.0].into());
3247    let proximity = Proximity::query_line_line (&line_a, &line_b);
3248    assert_eq!(proximity.nearest_a(), [0.0, 1.0, 0.0].into());
3249    assert_eq!(proximity.nearest_b(), [0.0, 1.0, 0.0].into());
3250    assert_eq!(proximity.distance, 0.0);
3251    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3252    assert_eq!(proximity.midpoint, [0.0, 1.0, 0.0].into());
3253    assert_eq!(proximity.normal, -Unit3::axis_z());
3254    // in front
3255    let line_a    = geometry::Segment3::noisy (
3256      [ 0.0, -1.0, 0.0].into(),
3257      [ 0.0,  1.0, 0.0].into());
3258    let line_b = geometry::Segment3::noisy (
3259      [-2.0,  2.0, 1.0].into(),
3260      [ 2.0,  2.0, 1.0].into());
3261    let proximity = Proximity::query_line_line (&line_a, &line_b);
3262    assert_eq!(proximity.nearest_a(), [ 0.0, 2.0, 0.0].into());
3263    assert_eq!(proximity.nearest_b(), [ 0.0, 2.0, 1.0].into());
3264    assert_eq!(proximity.distance, 1.0);
3265    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.5].into());
3266    assert_eq!(proximity.midpoint, [0.0, 2.0, 0.5].into());
3267    assert_eq!(proximity.normal, -Unit3::axis_z());
3268    // intersection: middle
3269    let line_a    = geometry::Segment3::noisy (
3270      [ 0.0, -1.0, 0.0].into(),
3271      [ 0.0,  1.0, 0.0].into());
3272    let line_b = geometry::Segment3::noisy (
3273      [-2.0,  2.0, 0.0].into(),
3274      [ 2.0,  2.0, 0.0].into());
3275    let proximity = Proximity::query_line_line (&line_a, &line_b);
3276    assert_eq!(proximity.nearest_a(), [ 0.0, 2.0, 0.0].into());
3277    assert_eq!(proximity.nearest_b(), [ 0.0, 2.0, 0.0].into());
3278    assert_eq!(proximity.distance, 0.0);
3279    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3280    assert_eq!(proximity.midpoint, [0.0, 2.0, 0.0].into());
3281    assert_eq!(proximity.normal, -Unit3::axis_z());
3282    // intersection: endpoint A
3283    let line_a    = geometry::Segment3::noisy (
3284      [-2.0, -1.0, 0.0].into(),
3285      [-2.0,  1.0, 0.0].into());
3286    let line_b = geometry::Segment3::noisy (
3287      [-2.0,  2.0, 0.0].into(),
3288      [ 2.0,  2.0, 0.0].into());
3289    let proximity = Proximity::query_line_line (&line_a, &line_b);
3290    assert_eq!(proximity.nearest_a(), [-2.0, 2.0, 0.0].into());
3291    assert_eq!(proximity.nearest_b(), [-2.0, 2.0, 0.0].into());
3292    assert_eq!(proximity.distance, 0.0);
3293    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3294    assert_eq!(proximity.midpoint, [-2.0, 2.0, 0.0].into());
3295    assert_eq!(proximity.normal, -Unit3::axis_z());
3296    let line_a    = geometry::Segment3::noisy (
3297      [-2.0, -1.0, 0.0].into(),
3298      [-2.0,  1.0, 0.0].into());
3299    let line_b = geometry::Segment3::noisy (
3300      [-2.0,   2.0, 0.0].into(),
3301      [ 2.0,  -2.0, 0.0].into());
3302    let proximity = Proximity::query_line_line (&line_a, &line_b);
3303    assert_eq!(proximity.nearest_a(), [-2.0, 2.0, 0.0].into());
3304    assert_eq!(proximity.nearest_b(), [-2.0, 2.0, 0.0].into());
3305    assert_eq!(proximity.distance, 0.0);
3306    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3307    assert_eq!(proximity.midpoint, [-2.0, 2.0, 0.0].into());
3308    assert_eq!(proximity.normal, -Unit3::axis_z());
3309    // intersection: endpoint B
3310    let line_a    = geometry::Segment3::noisy (
3311      [ 2.0, -1.0, 0.0].into(),
3312      [ 2.0,  1.0, 0.0].into());
3313    let line_b = geometry::Segment3::noisy (
3314      [-2.0,  2.0, 0.0].into(),
3315      [ 2.0,  2.0, 0.0].into());
3316    let proximity = Proximity::query_line_line (&line_a, &line_b);
3317    assert_eq!(proximity.nearest_a(), [2.0, 2.0, 0.0].into());
3318    assert_eq!(proximity.nearest_b(), [2.0, 2.0, 0.0].into());
3319    assert_eq!(proximity.distance, 0.0);
3320    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3321    assert_eq!(proximity.midpoint, [2.0, 2.0, 0.0].into());
3322    assert_eq!(proximity.normal, -Unit3::axis_z());
3323    let line_a    = geometry::Segment3::noisy (
3324      [ 2.0, -1.0, 0.0].into(),
3325      [ 2.0,  1.0, 0.0].into());
3326    let line_b = geometry::Segment3::noisy (
3327      [-2.0,  2.0, 0.0].into(),
3328      [ 2.0,  0.0, 0.0].into());
3329    let proximity = Proximity::query_line_line (&line_a, &line_b);
3330    assert_eq!(proximity.nearest_a(), [2.0, 0.0, 0.0].into());
3331    assert_eq!(proximity.nearest_b(), [2.0, 0.0, 0.0].into());
3332    assert_eq!(proximity.distance, 0.0);
3333    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3334    assert_eq!(proximity.midpoint, [2.0, 0.0, 0.0].into());
3335    assert_eq!(proximity.normal, -Unit3::axis_z());
3336    // intersection: parallel
3337    let line_a    = geometry::Segment3::noisy (
3338      [-1.0, 2.0, 0.0].into(),
3339      [ 0.0, 2.0, 0.0].into());
3340    let line_b = geometry::Segment3::noisy (
3341      [-2.0, 2.0, 0.0].into(),
3342      [ 2.0, 2.0, 0.0].into());
3343    let proximity = Proximity::query_line_line (&line_a, &line_b);
3344    assert_eq!(proximity.nearest_a(), [-2.0, 2.0, 0.0].into());
3345    assert_eq!(proximity.nearest_b(), [-2.0, 2.0, 0.0].into());
3346    assert_eq!(proximity.distance, 0.0);
3347    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3348    assert_eq!(proximity.midpoint, [-2.0, 2.0, 0.0].into());
3349    assert_eq!(proximity.normal, Unit3::axis_z());
3350    // normal should be invariant under translation
3351    let line_a = geometry::Segment3::noisy (
3352      [ -0.5, -0.5, -0.5].into(),
3353      [  0.5,  0.5,  0.5].into());
3354    let line_b = geometry::Segment3::noisy (
3355      [ 0.0,  1.0, 0.0].into(),
3356      [ 0.0, -1.0, 0.0].into());
3357    let proximity1 = Proximity::query_line_line (&line_a, &line_b);
3358    let mut rng = XorShiftRng::seed_from_u64 (0);
3359    let std_normal = rand_distr::StandardNormal;
3360    // cauchy sample
3361    let randf = |rng : &mut XorShiftRng| {
3362      let x : f64 = std_normal.sample (rng);
3363      let y : f64 = std_normal.sample (rng);
3364      x / y
3365    };
3366    for _ in 0..10000 {
3367      let translation = vector3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3368      let mut line_a = line_a;
3369      let mut line_b = line_b;
3370      line_a.translate (translation);
3371      line_b.translate (translation);
3372      let proximity = Proximity::query_line_line (&line_a, &line_b);
3373      approx::assert_relative_eq!(*proximity.normal, *proximity1.normal,
3374        max_relative = f64::default_max_relative() * 2.0.powi (10))
3375    }
3376  }
3377
3378  #[test]
3379  fn triangle_point() {
3380    use approx::RelativeEq;
3381    use rand::SeedableRng;
3382    use rand_distr::Distribution;
3383    let triangle = geometry::Triangle3::noisy (
3384      [-1.0, -1.0, 0.0].into(),
3385      [ 1.0, -1.0, 0.0].into(),
3386      [ 0.0,  1.0, 0.0].into());
3387    let point = point3 (0.0, 0.0, 0.0);
3388    let proximity = Proximity::query_triangle_point (&triangle, &point);
3389    assert_eq!(proximity.nearest_a(), [0.0, 0.0, 0.0].into());
3390    assert_eq!(proximity.nearest_b(), [0.0, 0.0, 0.0].into());
3391    assert_eq!(proximity.distance, 0.0);
3392    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3393    assert_eq!(proximity.midpoint, [0.0, 0.0, 0.0].into());
3394    assert_eq!(proximity.normal, Unit3::axis_z());
3395    let triangle = geometry::Triangle3::noisy (
3396      [-1.0, -1.0, 0.0].into(),
3397      [ 1.0, -1.0, 0.0].into(),
3398      [ 0.0,  1.0, 0.0].into());
3399    let point = point3 (0.5, 0.0, 0.0);
3400    let proximity = Proximity::query_triangle_point (&triangle, &point);
3401    assert_eq!(proximity.nearest_a(), [0.5, 0.0, 0.0].into());
3402    assert_eq!(proximity.nearest_b(), [0.5, 0.0, 0.0].into());
3403    assert_eq!(proximity.distance, 0.0);
3404    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3405    assert_eq!(proximity.midpoint, [0.5, 0.0, 0.0].into());
3406    assert_eq!(proximity.normal, Unit3::normalize ([-2.0, -1.0, 0.0].into()));
3407    let triangle = geometry::Triangle3::noisy (
3408      [ 0.0, -2.0, 3.5].into(),
3409      [ 2.0,  2.0, 3.5].into(),
3410      [-2.0,  2.0, 3.5].into());
3411    let point = point3 (0.0, 0.0, 2.0);
3412    let proximity = Proximity::query_triangle_point (&triangle, &point);
3413    assert_eq!(proximity.distance, 1.5);
3414    assert_eq!(proximity.half_axis, [0.0, 0.0, -0.75].into());
3415    assert_eq!(proximity.midpoint, [0.0, 0.0, 2.75].into());
3416    assert_eq!(proximity.normal, Unit3::axis_z());
3417    assert_eq!(proximity.nearest_a(), [0.0, 0.0, 3.5].into());
3418    assert_eq!(proximity.nearest_b(), [0.0, 0.0, 2.0].into());
3419    // test that normal is invariant under translation
3420    let triangle = geometry::Triangle3::noisy (
3421      [-1.0, -1.0, 0.0].into(),
3422      [ 1.0, -1.0, 0.0].into(),
3423      [ 0.0,  1.0, 0.0].into());
3424    let point = point3 (1.0, 0.0, 0.0);
3425    let proximity1 = Proximity::query_triangle_point (&triangle, &point);
3426    let mut rng = XorShiftRng::seed_from_u64 (0);
3427    let std_normal = rand_distr::StandardNormal;
3428    // cauchy sample
3429    let randf = |rng : &mut XorShiftRng| {
3430      let x : f64 = std_normal.sample (rng);
3431      let y : f64 = std_normal.sample (rng);
3432      x / y
3433    };
3434    for _ in 0..10000 {
3435      let translation = vector3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3436      let mut triangle = triangle;
3437      triangle.translate (translation);
3438      let point = point + translation;
3439      let proximity = Proximity::query_triangle_point (&triangle, &point);
3440      approx::assert_relative_eq!(*proximity.normal, *proximity1.normal,
3441        max_relative = f64::default_max_relative() * 2.0.powi (12));
3442    }
3443  }
3444
3445  #[test]
3446  fn triangle_segment() {
3447    use approx::RelativeEq;
3448    use rand::SeedableRng;
3449    use rand_distr::Distribution;
3450    let mut triangle = geometry::Triangle3::noisy (
3451      [-1.0, -1.0, 0.0].into(),
3452      [ 1.0, -1.0, 0.0].into(),
3453      [ 0.0,  1.0, 0.0].into());
3454    let mut segment = geometry::Segment3::noisy (
3455      [-2.0, -2.0, -0.01].into(),
3456      [ 2.0,  2.0,  0.01].into());
3457    let proximity = Proximity::query_triangle_segment (&triangle, &segment);
3458    triangle.translate (proximity.half_axis);
3459    segment.translate (-proximity.half_axis);
3460    let proximity = Proximity::query_triangle_segment (&triangle, &segment);
3461    approx::assert_abs_diff_eq!(proximity.distance, 0.0,
3462      epsilon = f64::default_epsilon() * 2.0);
3463    let mut triangle = geometry::Triangle3::noisy (
3464      [-1.0, -1.0, 0.0].into(),
3465      [ 1.0, -1.0, 0.0].into(),
3466      [ 0.0,  1.0, 0.0].into());
3467    let mut segment = geometry::Segment3::noisy (
3468      [-2.0,  2.0, -10.0].into(),
3469      [ 2.0, -2.0,  10.0].into());
3470    let proximity = Proximity::query_triangle_segment (&triangle, &segment);
3471    triangle.translate (proximity.half_axis);
3472    segment.translate (-proximity.half_axis);
3473    let proximity = Proximity::query_triangle_segment (&triangle, &segment);
3474    approx::assert_abs_diff_eq!(proximity.distance, 0.0,
3475      epsilon = f64::default_epsilon() * 2.0);
3476    // normal should be invariant under translation
3477    let triangle = geometry::Triangle3::noisy (
3478      [-1.0, -1.0, 0.0].into(),
3479      [ 1.0, -1.0, 0.0].into(),
3480      [ 0.0,  1.0, 0.0].into());
3481    let segment = geometry::Segment3::noisy (
3482      [-1.5, 0.0, -2.0].into(),
3483      [ 2.0, 0.0,  2.0].into());
3484    let proximity1 = Proximity::query_triangle_segment (&triangle, &segment);
3485    println!("normal: {}", *proximity1.normal);
3486    let mut rng = XorShiftRng::seed_from_u64 (0);
3487    let std_normal = rand_distr::StandardNormal;
3488    // cauchy sample
3489    let randf = |rng : &mut XorShiftRng| {
3490      let x : f64 = std_normal.sample (rng);
3491      let y : f64 = std_normal.sample (rng);
3492      x / y
3493    };
3494    for _ in 0..10000 {
3495      let translation  = vector3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3496      let mut triangle = triangle;
3497      let mut segment  = segment;
3498      triangle.translate (translation);
3499      segment.translate (translation);
3500      let proximity = Proximity::query_triangle_segment (&triangle, &segment);
3501      approx::assert_relative_eq!(*proximity.normal, *proximity1.normal,
3502        max_relative = f64::default_max_relative() * 2.0.powi (10))
3503    }
3504  }
3505
3506  #[test]
3507  fn triangle_line() {
3508    use approx::RelativeEq;
3509    use rand::SeedableRng;
3510    use rand_distr::Distribution;
3511    let triangle = geometry::Triangle3::noisy (
3512      [-1.0, -1.0, 0.0].into(),
3513      [ 1.0, -1.0, 0.0].into(),
3514      [ 0.0,  1.0, 0.0].into()
3515    );
3516    let line = geometry::Segment3::noisy (
3517      [-2.0, 2.0, 0.0].into(),
3518      [ 2.0, 2.0, 0.0].into());
3519    let proximity = Proximity::query_triangle_line (&triangle, &line);
3520    assert_eq!(proximity.nearest_a(), [0.0, 1.0, 0.0].into());
3521    assert_eq!(proximity.nearest_b(), [0.0, 2.0, 0.0].into());
3522    assert_eq!(proximity.distance, 1.0);
3523    assert_eq!(proximity.half_axis, [0.0, 0.5, 0.0].into());
3524    assert_eq!(proximity.midpoint, [0.0, 1.5, 0.0].into());
3525    assert_eq!(proximity.normal, -Unit3::axis_y());
3526    // normal should be invariant under translation
3527    let triangle = geometry::Triangle3::noisy (
3528      [-1.0, -1.0, 0.0].into(),
3529      [ 1.0, -1.0, 0.0].into(),
3530      [ 0.0,  1.0, 0.0].into());
3531    let line = geometry::Segment3::noisy (
3532      [-2.0, 0.0, 0.0].into(),
3533      [ 2.0, 0.0, 0.0].into());
3534    let proximity1 = Proximity::query_triangle_line (&triangle, &line);
3535    let mut rng = XorShiftRng::seed_from_u64 (0);
3536    let std_normal = rand_distr::StandardNormal;
3537    // cauchy sample
3538    let randf = |rng : &mut XorShiftRng| {
3539      let x : f64 = std_normal.sample (rng);
3540      let y : f64 = std_normal.sample (rng);
3541      x / y
3542    };
3543    for _ in 0..10000 {
3544      let translation  = vector3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3545      let mut triangle = triangle;
3546      let mut line     = line;
3547      triangle.translate (translation);
3548      line.translate (translation);
3549      let proximity = Proximity::query_triangle_line (&triangle, &line);
3550      approx::assert_relative_eq!(*proximity.normal, *proximity1.normal,
3551        max_relative = f64::default_max_relative() * 2.0.powi (10))
3552    }
3553  }
3554
3555  #[test]
3556  #[expect(clippy::unreadable_literal)]
3557  fn triangle_triangle() {
3558    use approx::RelativeEq;
3559    use rand::SeedableRng;
3560    use rand_distr::Distribution;
3561    let triangle_a = geometry::Triangle3::noisy (
3562      [-3.0, 0.0, 0.0].into(),
3563      [-1.0, 0.0, 0.0].into(),
3564      [-2.0, 2.0, 0.0].into());
3565    let triangle_b = geometry::Triangle3::noisy (
3566      [ 3.0,  0.0, 0.0].into(),
3567      [ 1.0,  0.0, 0.0].into(),
3568      [ 2.0,  2.0, 0.0].into());
3569    let proximity = Proximity::query_triangle_triangle (&triangle_a, &triangle_b);
3570    assert_eq!(proximity, Proximity {
3571      distance:  2.0,
3572      half_axis: [1.0, 0.0, 0.0].into(),
3573      midpoint:  [0.0, 0.0, 0.0].into(),
3574      normal:    -Unit3::axis_x()
3575    });
3576    let triangle_a = geometry::Triangle3::noisy (
3577      [-2.0, -2.0, 0.0].into(),
3578      [ 2.0, -2.0, 0.0].into(),
3579      [ 0.0,  2.0, 0.0].into());
3580    let triangle_b = geometry::Triangle3::noisy (
3581      [-0.5, 0.0, -0.1].into(),
3582      [ 0.5, 0.0, -0.1].into(),
3583      [ 0.0, 0.0,  2.0].into());
3584    let proximity = Proximity::query_triangle_triangle (&triangle_a, &triangle_b);
3585    assert_eq!(proximity.distance, -0.1);
3586    approx::assert_relative_eq!(proximity.half_axis, [0.0, 0.0, -0.05].into());
3587    let triangle_a = geometry::Triangle3::noisy (
3588      [-2.0, -2.0, 0.0].into(),
3589      [ 2.0, -2.0, 0.0].into(),
3590      [ 0.0,  2.0, 0.0].into());
3591    let triangle_b = geometry::Triangle3::noisy (
3592      [-1.0, 0.0, -0.1].into(),
3593      [ 1.0, 0.0, -0.1].into(),
3594      [ 0.0, 0.0,  2.0].into());
3595    let proximity = Proximity::query_triangle_triangle (&triangle_a, &triangle_b);
3596    assert_eq!(proximity.distance, -0.1);
3597    assert_eq!(proximity.half_axis, [0.0, 0.0, -0.05].into());
3598    let mut triangle_a = geometry::Triangle3::noisy (
3599      [-2.0, -2.0, 0.0].into(),
3600      [ 2.0, -2.0, 0.0].into(),
3601      [ 0.0,  2.0, 0.0].into());
3602    let mut triangle_b = geometry::Triangle3::noisy (
3603      [ 0.0, 0.0, -2.0].into(),
3604      [-1.0, 0.0,  2.0].into(),
3605      [ 1.0, 0.0,  2.0].into());
3606    let proximity = Proximity::query_triangle_triangle (&triangle_a, &triangle_b);
3607    assert_eq!(proximity, Proximity {
3608      distance:  -1.3093073414159542,
3609      half_axis: [0.5714285714285714, -0.2857142857142857, -0.1428571428571429].into(),
3610      midpoint:  [-0.14285714285714285, 0.28571428571428564, -0.1428571428571429].into(),
3611      normal:    Unit3::noisy (
3612        [0.8728715609439696, -0.4364357804719848, -0.2182178902359924].into())
3613    });
3614    // make the arrangement asymmetrical so that the result is consistent
3615    triangle_a.translate (proximity.half_axis * 0.5);
3616    triangle_b.translate (-proximity.half_axis * 0.5);
3617    let proximity1 = Proximity::query_triangle_triangle (&triangle_a, &triangle_b);
3618    approx::assert_relative_eq!(proximity1.distance * 2.0, proximity.distance,
3619      max_relative = f64::default_max_relative() * 2.0);
3620    approx::assert_relative_eq!(proximity1.half_axis * 2.0, proximity.half_axis,
3621      max_relative = f64::default_max_relative() * 16.0);
3622    approx::assert_relative_eq!(proximity1.midpoint, proximity.midpoint,
3623      max_relative = f64::default_max_relative() * 8.0);
3624    approx::assert_relative_eq!(*proximity1.normal, *proximity.normal);
3625    // normal should be invariant under translation
3626    let mut rng = XorShiftRng::seed_from_u64 (0);
3627    let std_normal = rand_distr::StandardNormal;
3628    // cauchy sample
3629    let randf = |rng : &mut XorShiftRng| {
3630      let x : f64 = std_normal.sample (rng);
3631      let y : f64 = std_normal.sample (rng);
3632      x / y
3633    };
3634    for _i in 0..5000 {
3635      let translation  = vector3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3636      let mut triangle_a = triangle_a;
3637      let mut triangle_b = triangle_b;
3638      triangle_a.translate (translation);
3639      triangle_b.translate (translation);
3640      let proximity = Proximity::query_triangle_triangle (&triangle_a, &triangle_b);
3641      approx::assert_relative_eq!(*proximity.normal, *proximity1.normal,
3642        max_relative = f64::default_max_relative() * 2.0.powi (10))
3643    }
3644  }
3645
3646  #[test]
3647  #[expect(clippy::suboptimal_flops)]
3648  fn distance_query() {
3649    use rand::{RngExt, SeedableRng};
3650    use approx::{assert_relative_eq, assert_ulps_eq};
3651
3652    //
3653    //  capsule v. capsule
3654    //
3655
3656    // intersection: positions of capsules A and B are identical-- half_axis and
3657    // normal parallel to +X
3658    let a = object::Static {
3659      position: component::Position ([0.0, 0.0, 0.0].into()),
3660      bound:    component::Bound::from (shape::Capsule::noisy (2.0, 3.0)),
3661      material: component::MATERIAL_STONE,
3662      collidable: true
3663    };
3664    let b = object::Static {
3665      position: component::Position ([0.0, 0.0, 0.0].into()),
3666      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
3667      material: component::MATERIAL_STONE,
3668      collidable: true
3669    };
3670    assert_eq!(
3671      Proximity::query (&a, &b),
3672      Proximity {
3673        distance:  -3.0,
3674        half_axis: [ 1.5, 0.0, 0.0].into(),
3675        normal:    Unit3::axis_x(),
3676        midpoint:  [-0.5, 0.0, 0.0].into()
3677      }
3678    );
3679
3680    // intersection: cylinder shafts of A and B overlap-- half_axis and normal
3681    // parallel to +X
3682    let a = object::Static {
3683      position: component::Position ([0.0, 0.0, 1.0].into()),  .. a
3684    };
3685    let b = object::Static {
3686      position: component::Position ([0.0, 0.0, -1.0].into()), .. b
3687    };
3688    assert_eq!(
3689      Proximity::query (&a, &b),
3690      Proximity {
3691        distance:  -3.0,
3692        half_axis: [ 1.5, 0.0,  0.0].into(),
3693        normal:    Unit3::axis_x(),
3694        midpoint:  [-0.5, 0.0, -0.5].into()
3695      }
3696    );
3697
3698    // intersection: cylinder shaft max of A coincides with cylinder shaft min
3699    // of B
3700    let a = object::Static {
3701      position: component::Position ([0.0, 0.0, -3.0].into()), .. a
3702    };
3703    let b = object::Static {
3704      position: component::Position ([0.0, 0.0, 2.0].into()),  .. b
3705    };
3706    assert_eq!(
3707      Proximity::query (&a, &b),
3708      Proximity {
3709        distance:  -3.0,
3710        half_axis: [ 1.5, 0.0, 0.0].into(),
3711        normal:    Unit3::axis_x(),
3712        midpoint:  [-0.5, 0.0, 0.0].into()
3713      }
3714    );
3715
3716    // intersection: cylinder shaft min of A coincides with cylinder shaft max of B
3717    let a = object::Static {
3718      position: component::Position ([0.0, 0.0, 3.0].into()),  .. a
3719    };
3720    let b = object::Static {
3721      position: component::Position ([0.0, 0.0, -2.0].into()), .. b
3722    };
3723    assert_eq!(
3724      Proximity::query (&a, &b),
3725      Proximity {
3726        distance:  -3.0,
3727        half_axis: [ 1.5, 0.0, 0.0].into(),
3728        normal:    Unit3::axis_x(),
3729        midpoint:  [-0.5, 0.0, 0.0].into()
3730      }
3731    );
3732
3733    // intersection: cylinder shaft of A adjacent to cylinder shaft of B
3734    let a = object::Static {
3735      position: component::Position ([-1.0, 0.0, 1.0].into()), .. a
3736    };
3737    let b = object::Static {
3738      position: component::Position ([0.0, 0.0, -1.0].into()), .. b
3739    };
3740    assert_eq!(
3741      Proximity::query (&a, &b),
3742      Proximity {
3743        distance:  -2.0,
3744        half_axis: [-1.0, 0.0,  0.0].into(),
3745        normal:    Unit3::axis_x().invert(),
3746        midpoint:  [ 0.0, 0.0, -0.5].into()
3747      }
3748    );
3749
3750    // intersection: cylinder shaft of B above cylinder shaft of A
3751    let a = object::Static {
3752      position: component::Position ([0.0, 0.0, -3.0].into()), .. a
3753    };
3754    let b = object::Static {
3755      position: component::Position ([0.0, 0.0,  3.0].into()), .. b
3756    };
3757    assert_eq!(
3758      Proximity::query (&a, &b),
3759      Proximity {
3760        distance:  -2.0,
3761        half_axis: [ 0.0, 0.0, -1.0].into(),
3762        normal:    Unit3::axis_z().invert(),
3763        midpoint:  [ 0.0, 0.0,  1.0].into()
3764      }
3765    );
3766
3767    // intersection: cylinder shaft of A above cylinder shaft of B
3768    let a = object::Static {
3769      position: component::Position ([0.0, 0.0,  3.0].into()), .. a
3770    };
3771    let b = object::Static {
3772      position: component::Position ([0.0, 0.0, -3.0].into()), .. b
3773    };
3774    assert_eq!(
3775      Proximity::query (&a, &b),
3776      Proximity {
3777        distance:  -2.0,
3778        half_axis: [ 0.0, 0.0,  1.0].into(),
3779        normal:    Unit3::axis_z(),
3780        midpoint:  [ 0.0, 0.0, -1.0].into()
3781      }
3782    );
3783
3784    // intersection: cylinder shafts of A and B are adjacent
3785    let a = object::Static {
3786      position: component::Position ([ 1.0, 0.0, 0.0].into()), .. a
3787    };
3788    let b = object::Static {
3789      position: component::Position ([-1.0, 0.0, 0.0].into()), .. b
3790    };
3791    assert_eq!(
3792      Proximity::query (&a, &b),
3793      Proximity {
3794        distance:  -1.0,
3795        half_axis: [ 0.5, 0.0, 0.0].into(),
3796        normal:    Unit3::axis_x(),
3797        midpoint:  [-0.5, 0.0, 0.0].into()
3798      }
3799    );
3800
3801    // disjoint: capsule A above and to the right (+X) of capsule B
3802    let a = object::Static {
3803      position: component::Position ([ 2.0, 0.0,  5.0].into()), .. a
3804    };
3805    let b = object::Static {
3806      position: component::Position ([-1.0, 0.0, -3.0].into()), .. b
3807    };
3808
3809    let proximity = Proximity::query (&a, &b);
3810    let distance_manual = {
3811      let distance  = vector3 (3.0, 0.0, 3.0).magnitude() - 3.0;
3812      let half_axis : Vector3 <_> =
3813        0.5 * distance * vector3 (-1.0, 0.0, -1.0).normalized();
3814      let normal    = Unit3::normalize ([1.0, 0.0, 1.0].into());
3815      let midpoint  = Point3::from ([-1.0, 0.0, -1.0]) + *normal - half_axis;
3816      Proximity { distance, half_axis, midpoint, normal }
3817    };
3818    assert_eq!(proximity.distance, distance_manual.distance);
3819    assert_relative_eq!(proximity.half_axis, distance_manual.half_axis);
3820    assert_relative_eq!(*proximity.normal,   *distance_manual.normal);
3821    assert_relative_eq!(proximity.midpoint,  distance_manual.midpoint,
3822      epsilon = 2.0 * f64::EPSILON);
3823
3824    // disjoint: cylinder shafts of A and B are adjacent
3825    let a = object::Static {
3826      position: component::Position ([ 3.0, 0.0, 3.0].into()), .. a
3827    };
3828    let b = object::Static {
3829      position: component::Position ([-1.0, 0.0, 0.0].into()), .. b
3830    };
3831    assert_eq!(
3832      Proximity::query (&a, &b),
3833      Proximity {
3834        distance:  1.0,
3835        half_axis: [-0.5, 0.0, 0.0].into(),
3836        normal:    Unit3::axis_x(),
3837        midpoint:  [ 0.5, 0.0, 1.0].into()
3838      }
3839    );
3840
3841    //
3842    //  cuboid v. cuboid
3843    //
3844
3845    // disjoint: nearest points are corners
3846    let a = object::Static {
3847      position: component::Position ([-5.0, -5.0, -5.0].into()),
3848      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 2.0, 3.0])),
3849      .. a
3850    };
3851    let b = object::Static {
3852      position: component::Position ([5.0, 5.0, 5.0].into()),
3853      bound:    component::Bound::from (shape::Cuboid::noisy ([3.0, 1.0, 2.0])),
3854      .. b
3855    };
3856    let proximity = Proximity::query (&a, &b);
3857    assert_eq!(proximity.distance, f64::sqrt (110.0));
3858    assert_eq!(proximity.half_axis, [3.0, 3.5, 2.5].into());
3859    assert_ulps_eq!(*proximity.normal, -vector3 (3.0, 3.5, 2.5).normalized());
3860    assert_eq!(proximity.midpoint,  [-1.0, 0.5, 0.5].into());
3861
3862    // disjoint: overlap on X axis only
3863    let a = object::Static {
3864      position: component::Position ([-5.0, 0.0, -5.0].into()), .. a
3865    };
3866    let b = object::Static {
3867      position: component::Position ([5.0, 0.0, 5.0].into()), .. b
3868    };
3869    let proximity = Proximity::query (&a, &b);
3870    assert_eq!(proximity.distance, f64::sqrt (61.0));
3871    assert_eq!(proximity.half_axis, [3.0, 0.0, 2.5].into());
3872    assert_ulps_eq!(*proximity.normal, -vector3 (3.0, 0.0, 2.5).normalized());
3873    assert_eq!(proximity.midpoint,  [-1.0, 0.0, 0.5].into());
3874
3875    //
3876    //  capsule v. cuboid
3877    //
3878
3879    // disjoint: capsule nearest cuboid max corner
3880    let a = object::Static {
3881      position: component::Position ([5.0, 5.0, 3.0].into()),
3882      bound:    component::Bound::from (shape::Capsule::noisy (SQRT_2, 2.0)),
3883      .. a
3884    };
3885    let b = object::Static {
3886      position: component::Position ([0.0, 0.0, 0.0].into()),
3887      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
3888      .. b
3889    };
3890    let proximity = Proximity::query (&a, &b);
3891    assert_ulps_eq!(proximity.distance, 3.0 * SQRT_2);
3892    assert_eq!(proximity.half_axis, [-1.5, -1.5, 0.0].into());
3893    assert_ulps_eq!(*proximity.normal, vector3 (1.0, 1.0, 0.0).normalized());
3894    assert_eq!(proximity.midpoint,  [ 2.5, 2.5, 1.0].into());
3895
3896    // disjoint: capsule nearest cuboid min corner
3897    let a = object::Static {
3898      position: component::Position ([-5.0, -5.0, -3.0].into()),
3899      bound:    component::Bound::from (shape::Capsule::noisy (SQRT_2, 2.0)),
3900      .. a
3901    };
3902    let b = object::Static {
3903      position: component::Position ([0.0, 0.0, 0.0].into()),
3904      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
3905      .. b
3906    };
3907    let proximity = Proximity::query (&a, &b);
3908    assert_ulps_eq!(proximity.distance, 3.0 * SQRT_2);
3909    assert_eq!(proximity.half_axis, [ 1.5,  1.5, 0.0].into());
3910    assert_ulps_eq!(*proximity.normal, vector3 (-1.0, -1.0, 0.0).normalized());
3911    assert_eq!(proximity.midpoint,  [-2.5,-2.5,-1.0].into());
3912
3913    // disjoint: capsule nearest cuboid max Y edge
3914    let a = object::Static {
3915      position: component::Position ([4.0, 0.5, 4.0].into()),
3916      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
3917      .. a
3918    };
3919    let b = object::Static {
3920      position: component::Position ([0.0, 0.0, 0.0].into()),
3921      bound:    component::Bound::from (shape::Cuboid::noisy ([2.0, 2.0, 2.0])),
3922      .. b
3923    };
3924    assert_eq!(
3925      Proximity::query (&a, &b),
3926      Proximity {
3927        distance:  1.0,
3928        half_axis: [-0.5, 0.0, 0.0].into(),
3929        normal:    Unit3::axis_x(),
3930        midpoint:  [ 2.5, 0.5, 2.0].into()
3931      }
3932    );
3933
3934    // disjoint: capsule nearest cuboid min Y edge
3935    let a = object::Static {
3936      position: component::Position ([4.0, 0.5, -4.0].into()), .. a
3937    };
3938    let b = object::Static {
3939      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
3940    };
3941    assert_eq!(
3942      Proximity::query (&a, &b),
3943      Proximity {
3944        distance:  1.0,
3945        half_axis: [-0.5, 0.0,  0.0].into(),
3946        normal:    Unit3::axis_x(),
3947        midpoint:  [ 2.5, 0.5, -2.0].into()
3948      }
3949    );
3950
3951    // disjoint: capsule nearest cuboid max X edge
3952    let a = object::Static {
3953      position: component::Position ([0.5, 4.0, 4.0].into()), .. a
3954    };
3955    let b = object::Static {
3956      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
3957    };
3958    assert_eq!(
3959      Proximity::query (&a, &b),
3960      Proximity {
3961        distance:  1.0,
3962        half_axis: [ 0.0, -0.5, 0.0].into(),
3963        normal:    Unit3::axis_y(),
3964        midpoint:  [ 0.5,  2.5, 2.0].into()
3965      }
3966    );
3967
3968    // disjoint: capsule nearest cuboid min X edge
3969    let a = object::Static {
3970      position: component::Position ([0.5, 4.0, -4.0].into()), .. a
3971    };
3972    let b = object::Static {
3973      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
3974    };
3975    assert_eq!(
3976      Proximity::query (&a, &b),
3977      Proximity {
3978        distance:  1.0,
3979        half_axis: [ 0.0, -0.5,  0.0].into(),
3980        normal:    Unit3::axis_y(),
3981        midpoint:  [ 0.5,  2.5, -2.0].into()
3982      }
3983    );
3984
3985    // disjoint: capsule A above cuboid B
3986    let a = object::Static {
3987      position: component::Position ([0.5, 0.5, 6.0].into()), .. a
3988    };
3989    let b = object::Static {
3990      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
3991    };
3992    assert_eq!(
3993      Proximity::query (&a, &b),
3994      Proximity {
3995        distance:  1.0,
3996        half_axis: [ 0.0, 0.0, -0.5].into(),
3997        normal:    Unit3::axis_z(),
3998        midpoint:  [ 0.5, 0.5,  2.5].into()
3999      }
4000    );
4001
4002    // disjoint: capsule A below cuboid B
4003    let a = object::Static {
4004      position: component::Position ([0.5, 0.5, -6.0].into()), .. a
4005    };
4006    let b = object::Static {
4007      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4008    };
4009    assert_eq!(
4010      Proximity::query (&a, &b),
4011      Proximity {
4012        distance:  1.0,
4013        half_axis: [ 0.0, 0.0,  0.5].into(),
4014        normal:    Unit3::axis_z().invert(),
4015        midpoint:  [ 0.5, 0.5, -2.5].into()
4016      }
4017    );
4018
4019    // disjoint: capsule A cylinder nearest max Z edge of cuboid B
4020    let a = object::Static {
4021      position: component::Position ([3.0, 3.0, 2.0].into()), .. a
4022    };
4023    let b = object::Static {
4024      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4025    };
4026    let proximity = Proximity::query (&a, &b);
4027    assert_eq!(proximity.distance, SQRT_2 - 1.0);
4028    assert_eq!(proximity.half_axis, [
4029      -0.5 * f64::sqrt (0.5 * (SQRT_2 - 1.0).powi (2)),
4030      -0.5 * f64::sqrt (0.5 * (SQRT_2 - 1.0).powi (2)),
4031       0.0
4032    ].into());
4033    assert_ulps_eq!(*proximity.normal,
4034      [FRAC_1_SQRT_2, FRAC_1_SQRT_2, 0.0].into());
4035    assert_eq!(proximity.midpoint, [
4036      2.0 + 0.5 * f64::sqrt (0.5 * (SQRT_2 - 1.0).powi (2)),
4037      2.0 + 0.5 * f64::sqrt (0.5 * (SQRT_2 - 1.0).powi (2)),
4038      1.0
4039    ].into());
4040
4041    // disjoint: capsule A cylinder nearest min Z edge of cuboid B
4042    let a = object::Static {
4043      position: component::Position ([-3.0, -3.0, -2.0].into()), .. a
4044    };
4045    let b = object::Static {
4046      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4047    };
4048    let proximity = Proximity::query (&a, &b);
4049    assert_eq!(proximity.distance, SQRT_2 - 1.0);
4050    assert_eq!(proximity.half_axis, [
4051       0.5 * f64::sqrt (0.5 * (SQRT_2 - 1.0).powi (2)),
4052       0.5 * f64::sqrt (0.5 * (SQRT_2 - 1.0).powi (2)),
4053       0.0
4054    ].into());
4055    assert_ulps_eq!(*proximity.normal, [-FRAC_1_SQRT_2, -FRAC_1_SQRT_2, 0.0].into());
4056    assert_eq!(proximity.midpoint, [
4057      -2.0 - 0.5 * f64::sqrt (0.5 * (SQRT_2 - 1.0).powi (2)),
4058      -2.0 - 0.5 * f64::sqrt (0.5 * (SQRT_2 - 1.0).powi (2)),
4059      -1.0
4060    ].into());
4061
4062    // disjoint: capsule A cylinder nearest max X/Z face of cuboid B
4063    let a = object::Static {
4064      position: component::Position ([1.0, 4.0, 2.0].into()), .. a
4065    };
4066    let b = object::Static {
4067      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4068    };
4069    assert_eq!(
4070      Proximity::query (&a, &b),
4071      Proximity {
4072        distance:  1.0,
4073        half_axis: [ 0.0, -0.5,  0.0].into(),
4074        normal:    Unit3::axis_y(),
4075        midpoint:  [ 1.0,  2.5,  1.0].into()
4076      }
4077    );
4078
4079    // disjoint: capsule A cylinder nearest min X/Z face of cuboid B
4080    let a = object::Static {
4081      position: component::Position ([-1.0, -4.0, -2.0].into()), .. a
4082    };
4083    let b = object::Static {
4084      position: component::Position ([0.0, 0.0, 0.0].into()),    .. b
4085    };
4086    assert_eq!(
4087      Proximity::query (&a, &b),
4088      Proximity {
4089        distance:  1.0,
4090        half_axis: [ 0.0,  0.5,  0.0].into(),
4091        normal:    Unit3::axis_y().invert(),
4092        midpoint:  [-1.0, -2.5, -1.0].into()
4093      }
4094    );
4095
4096    // disjoint: capsule A cylinder nearest max Y/Z face of cuboid B
4097    let a = object::Static {
4098      position: component::Position ([4.0, 1.0, 2.0].into()), .. a
4099    };
4100    let b = object::Static {
4101      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4102    };
4103    assert_eq!(
4104      Proximity::query (&a, &b),
4105      Proximity {
4106        distance:  1.0,
4107        half_axis: [-0.5, 0.0, 0.0].into(),
4108        normal:    Unit3::axis_x(),
4109        midpoint:  [ 2.5, 1.0, 1.0].into()
4110      }
4111    );
4112
4113    // disjoint: capsule A cylinder nearest min Y/Z face of cuboid B
4114    let a = object::Static {
4115      position: component::Position ([-4.0, -1.0, -2.0].into()), .. a
4116    };
4117    let b = object::Static {
4118      position: component::Position ([0.0, 0.0, 0.0].into()),    .. b
4119    };
4120    assert_eq!(
4121      Proximity::query (&a, &b),
4122      Proximity {
4123        distance:  1.0,
4124        half_axis: [ 0.5,  0.0,  0.0].into(),
4125        normal:    Unit3::axis_x().invert(),
4126        midpoint:  [-2.5, -1.0, -1.0].into()
4127      }
4128    );
4129
4130    // intersect: capsule cylinder shaft min equal to cuboid max corner
4131    let a = object::Static {
4132      position: component::Position ([2.0, 2.0, 4.0].into()),
4133      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4134      .. a
4135    };
4136    let b = object::Static {
4137      position: component::Position ([0.0, 0.0, 0.0].into()),
4138      bound:    component::Bound::from (shape::Cuboid::noisy ([2.0, 2.0, 2.0])),
4139      .. b
4140    };
4141    assert_eq!(
4142      Proximity::query (&a, &b),
4143      Proximity {
4144        distance:  -1.0,
4145        half_axis: [ 0.5, 0.0, 0.0].into(),
4146        normal:    Unit3::axis_x(),
4147        midpoint:  [ 1.5, 2.0, 2.0].into()
4148      }
4149    );
4150
4151    // intersect: capsule cylinder shaft max equal to cuboid min corner
4152    let a = object::Static {
4153      position: component::Position ([-2.0, -2.0, -4.0].into()), .. a
4154    };
4155    let b = object::Static {
4156      position: component::Position ([0.0, 0.0, 0.0].into()),  .. b
4157    };
4158    assert_eq!(
4159      Proximity::query (&a, &b),
4160      Proximity {
4161        distance:  -1.0,
4162        half_axis: [-0.5,  0.0, 0.0].into(),
4163        normal:    Unit3::axis_x().invert(),
4164        midpoint:  [-1.5, -2.0,-2.0].into()
4165      }
4166    );
4167
4168    // intersect: capsule cylinder shaft min equal to cuboid max Y edge
4169    let a = object::Static {
4170      position: component::Position ([2.0, 1.0, 4.0].into()), .. a
4171    };
4172    let b = object::Static {
4173      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4174    };
4175    assert_eq!(
4176      Proximity::query (&a, &b),
4177      Proximity {
4178        distance:  -1.0,
4179        half_axis: [ 0.5, 0.0, 0.0].into(),
4180        normal:    Unit3::axis_x(),
4181        midpoint:  [ 1.5, 1.0, 2.0].into()
4182      }
4183    );
4184
4185    // intersect: capsule cylinder shaft max equal to cuboid min Y edge
4186    let a = object::Static {
4187      position: component::Position ([-2.0, 1.0, -4.0].into()), .. a
4188    };
4189    let b = object::Static {
4190      position: component::Position ([0.0, 0.0, 0.0].into()),  .. b
4191    };
4192    assert_eq!(
4193      Proximity::query (&a, &b),
4194      Proximity {
4195        distance:  -1.0,
4196        half_axis: [-0.5, 0.0,  0.0].into(),
4197        normal:    Unit3::axis_x().invert(),
4198        midpoint:  [-1.5, 1.0, -2.0].into()
4199      }
4200    );
4201
4202    // intersect: capsule cylinder shaft min equal to cuboid max X edge
4203    let a = object::Static {
4204      position: component::Position ([1.0, 2.0, 4.0].into()), .. a
4205    };
4206    let b = object::Static {
4207      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4208    };
4209    assert_eq!(
4210      Proximity::query (&a, &b),
4211      Proximity {
4212        distance:  -1.0,
4213        half_axis: [ 0.0, 0.5, 0.0].into(),
4214        normal:    Unit3::axis_y(),
4215        midpoint:  [ 1.0, 1.5, 2.0].into()
4216      }
4217    );
4218
4219    // intersect: capsule cylinder shaft max equal to cuboid min X edge
4220    let a = object::Static {
4221      position: component::Position ([ 1.0, -2.0, -4.0].into()), .. a
4222    };
4223    let b = object::Static {
4224      position: component::Position ([0.0, 0.0, 0.0].into()),  .. b
4225    };
4226    assert_eq!(
4227      Proximity::query (&a, &b),
4228      Proximity {
4229        distance:  -1.0,
4230        half_axis: [0.0, -0.5,  0.0].into(),
4231        normal:    Unit3::axis_y().invert(),
4232        midpoint:  [1.0, -1.5, -2.0].into()
4233      }
4234    );
4235
4236    // intersect: capsule cylinder shaft min equal to cuboid max Z face
4237    let a = object::Static {
4238      position: component::Position ([1.0, 1.0, 4.0].into()), .. a
4239    };
4240    let b = object::Static {
4241      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4242    };
4243    assert_eq!(
4244      Proximity::query (&a, &b),
4245      Proximity {
4246        distance:  -1.0,
4247        half_axis: [ 0.0, 0.0, 0.5].into(),
4248        normal:    Unit3::axis_z(),
4249        midpoint:  [ 1.0, 1.0, 1.5].into()
4250      }
4251    );
4252
4253    // intersect: capsule cylinder shaft max equal to cuboid min Z face
4254    let a = object::Static {
4255      position: component::Position ([-1.0, -1.0, -4.0].into()), .. a
4256    };
4257    let b = object::Static {
4258      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4259    };
4260    assert_eq!(
4261      Proximity::query (&a, &b),
4262      Proximity {
4263        distance:  -1.0,
4264        half_axis: [ 0.0,  0.0, -0.5].into(),
4265        normal:    Unit3::axis_z().invert(),
4266        midpoint:  [-1.0, -1.0, -1.5].into()
4267      }
4268    );
4269
4270    // intersect: capsule and cuboid position coincide
4271    let a = object::Static {
4272      position: component::Position ([0.0, 0.0, 0.0].into()), .. a
4273    };
4274    let b = object::Static {
4275      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4276    };
4277    assert_eq!(
4278      Proximity::query (&a, &b),
4279      Proximity {
4280        distance:  -3.0,
4281        half_axis: [ 1.5,  0.0,  0.0].into(),
4282        normal:    Unit3::axis_x(),
4283        midpoint:  [ 0.5,  0.0,  0.0].into()
4284      }
4285    );
4286
4287    // intersect: capsule nearest positive X face of cuboid
4288    let a = object::Static {
4289      position: component::Position ([1.0, 0.0, 0.0].into()), .. a
4290    };
4291    let b = object::Static {
4292      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4293    };
4294    assert_eq!(
4295      Proximity::query (&a, &b),
4296      Proximity {
4297        distance:  -2.0,
4298        half_axis: [ 1.0,  0.0,  0.0].into(),
4299        normal:    Unit3::axis_x(),
4300        midpoint:  [ 1.0,  0.0,  0.0].into()
4301      }
4302    );
4303
4304    // intersect: capsule nearest negative X face of cuboid
4305    let a = object::Static {
4306      position: component::Position ([-1.0, 0.0, 0.0].into()), .. a
4307    };
4308    let b = object::Static {
4309      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4310    };
4311    assert_eq!(
4312      Proximity::query (&a, &b),
4313      Proximity {
4314        distance:  -2.0,
4315        half_axis: [-1.0,  0.0,  0.0].into(),
4316        normal:    Unit3::axis_x().invert(),
4317        midpoint:  [-1.0,  0.0,  0.0].into()
4318      }
4319    );
4320
4321    // intersect: capsule nearest positive Y face of cuboid
4322    let a = object::Static {
4323      position: component::Position ([0.0, 1.0, 0.0].into()), .. a
4324    };
4325    let b = object::Static {
4326      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4327    };
4328    assert_eq!(
4329      Proximity::query (&a, &b),
4330      Proximity {
4331        distance:  -2.0,
4332        half_axis: [0.0, 1.0, 0.0].into(),
4333        normal:    Unit3::axis_y(),
4334        midpoint:  [0.0, 1.0, 0.0].into()
4335      }
4336    );
4337
4338    // intersect: capsule nearest negative Y face of cuboid
4339    let a = object::Static {
4340      position: component::Position ([0.0, -1.0, 0.0].into()), .. a
4341    };
4342    let b = object::Static {
4343      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4344    };
4345    assert_eq!(
4346      Proximity::query (&a, &b),
4347      Proximity {
4348        distance:  -2.0,
4349        half_axis: [0.0, -1.0, 0.0].into(),
4350        normal:    Unit3::axis_y().invert(),
4351        midpoint:  [0.0, -1.0, 0.0].into()
4352      }
4353    );
4354
4355    // intersect: capsule nearest positive Z face of cuboid
4356    let a = object::Static {
4357      position: component::Position ([0.0, 0.0, 3.0].into()),
4358      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4359      .. a
4360    };
4361    let b = object::Static {
4362      position: component::Position ([0.0, 0.0, 0.0].into()),
4363      bound:    component::Bound::from (shape::Cuboid::noisy ([4.0, 4.0, 2.0])),
4364      .. b
4365    };
4366    assert_eq!(
4367      Proximity::query (&a, &b),
4368      Proximity {
4369        distance:  -2.0,
4370        half_axis: [0.0, 0.0, 1.0].into(),
4371        normal:    Unit3::axis_z(),
4372        midpoint:  [0.0, 0.0, 1.0].into()
4373      }
4374    );
4375
4376    // intersect: capsule nearest positive Z face of cuboid
4377    let a = object::Static {
4378      position: component::Position ([0.0, 0.0, 1.0].into()),
4379      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4380      .. a
4381    };
4382    let b = object::Static {
4383      position: component::Position ([0.0, 0.0, 0.0].into()),
4384      bound:    component::Bound::from (shape::Cuboid::noisy ([4.0, 4.0, 2.0])),
4385      .. b
4386    };
4387    assert_eq!(
4388      Proximity::query (&a, &b),
4389      Proximity {
4390        distance:  -4.0,
4391        half_axis: [0.0, 0.0, 2.0].into(),
4392        normal:    Unit3::axis_z(),
4393        midpoint:  [0.0, 0.0, 0.0].into()
4394      }
4395    );
4396
4397    // intersect: capsule nearest negative Z face of cuboid
4398    let a = object::Static {
4399      position: component::Position ([0.0, 0.0, -3.0].into()),
4400      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4401      .. a
4402    };
4403    let b = object::Static {
4404      position: component::Position ([0.0, 0.0, 0.0].into()),
4405      bound:    component::Bound::from (shape::Cuboid::noisy ([4.0, 4.0, 2.0])),
4406      .. b
4407    };
4408    assert_eq!(
4409      Proximity::query (&a, &b),
4410      Proximity {
4411        distance:  -2.0,
4412        half_axis: [0.0, 0.0, -1.0].into(),
4413        normal:    Unit3::axis_z().invert(),
4414        midpoint:  [0.0, 0.0, -1.0].into()
4415      }
4416    );
4417
4418    // intersect: capsule nearest negative Z face of cuboid
4419    let a = object::Static {
4420      position: component::Position ([0.0, 0.0, -1.0].into()),
4421      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4422      .. a
4423    };
4424    let b = object::Static {
4425      position: component::Position ([0.0, 0.0, 0.0].into()),
4426      bound:    component::Bound::from (shape::Cuboid::noisy ([4.0, 4.0, 2.0])),
4427      .. b
4428    };
4429    assert_eq!(
4430      Proximity::query (&a, &b),
4431      Proximity {
4432        distance:  -4.0,
4433        half_axis: [0.0, 0.0, -2.0].into(),
4434        normal:    Unit3::axis_z().invert(),
4435        midpoint:  [0.0, 0.0,  0.0].into()
4436      }
4437    );
4438
4439    // random queries
4440    let mut rng = XorShiftRng::seed_from_u64 (0);
4441    let a = object::Static {
4442      position: component::Position ([0.0, 0.0, 0.0].into()),
4443      bound:    component::Bound::from (shape::Capsule::noisy (1.5, 1.5)),
4444      .. a
4445    };
4446    for _ in 0..100 {
4447      let position = component::Position ([
4448        rng.random_range (-40.0..40.0),
4449        rng.random_range (-40.0..40.0),
4450        rng.random_range (-40.0..40.0)
4451      ].into());
4452      let bound    = shape::Cuboid::noisy ([
4453        rng.random_range (0.5..5.0),
4454        rng.random_range (0.5..5.0),
4455        rng.random_range (0.5..5.0)
4456      ]).into();
4457      let b = object::Static {
4458        position, bound, material: component::MATERIAL_STONE, collidable: true
4459      };
4460      let _ = Proximity::query (&a, &b);
4461    }
4462
4463    //
4464    //  capsule v. orthant
4465    //
4466    use SignedAxis3;
4467
4468    // disjoint: capsule outside of orthant +Z
4469    let a = object::Static {
4470      position: component::Position ([0.0, 0.0, 4.0].into()),
4471      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4472      .. a
4473    };
4474    let b = object::Static {
4475      position: component::Position ([0.0, 0.0, 0.0].into()),
4476      bound:    shape::Orthant { normal_axis: SignedAxis3::PosZ }.into(),
4477      .. b
4478    };
4479    assert_eq!(
4480      Proximity::query (&a, &b),
4481      Proximity {
4482        distance:  1.0,
4483        half_axis: [0.0, 0.0, -0.5].into(),
4484        normal:    Unit3::axis_z(),
4485        midpoint:  [0.0, 0.0,  0.5].into()
4486      }
4487    );
4488
4489    // disjoint: capsule outside of orthant -Z
4490    let a = object::Static {
4491      position: component::Position ([0.0, 0.0, -4.0].into()),
4492      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4493      .. a
4494    };
4495    let b = object::Static {
4496      position: component::Position ([0.0, 0.0, 0.0].into()),
4497      bound:    shape::Orthant { normal_axis: SignedAxis3::NegZ }.into(),
4498      .. b
4499    };
4500    assert_eq!(
4501      Proximity::query (&a, &b),
4502      Proximity {
4503        distance:  1.0,
4504        half_axis: [0.0, 0.0,  0.5].into(),
4505        normal:    Unit3::axis_z().invert(),
4506        midpoint:  [0.0, 0.0, -0.5].into()
4507      }
4508    );
4509
4510    // disjoint: capsule outside orthant +Y
4511    let a = object::Static {
4512      position: component::Position ([0.0, 2.0, 0.0].into()),
4513      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4514      .. a
4515    };
4516    let b = object::Static {
4517      position: component::Position ([0.0, 0.0, 0.0].into()),
4518      bound:    shape::Orthant { normal_axis: SignedAxis3::PosY }.into(),
4519      .. b
4520    };
4521    assert_eq!(
4522      Proximity::query (&a, &b),
4523      Proximity {
4524        distance:  1.0,
4525        half_axis: [0.0, -0.5, 0.0].into(),
4526        normal:    Unit3::axis_y(),
4527        midpoint:  [0.0,  0.5, 0.0].into()
4528      }
4529    );
4530
4531    // disjoint: capsule outside orthant -Y
4532    let a = object::Static {
4533      position: component::Position ([0.0, -2.0, 0.0].into()),
4534      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4535      .. a
4536    };
4537    let b = object::Static {
4538      position: component::Position ([0.0, 0.0, 0.0].into()),
4539      bound:    shape::Orthant { normal_axis: SignedAxis3::NegY }.into(),
4540      .. b
4541    };
4542    assert_eq!(
4543      Proximity::query (&a, &b),
4544      Proximity {
4545        distance:  1.0,
4546        half_axis: [0.0,  0.5, 0.0].into(),
4547        normal:    Unit3::axis_y().invert(),
4548        midpoint:  [0.0, -0.5, 0.0].into()
4549      }
4550    );
4551
4552    // disjoint: capsule outside orthant +X
4553    let a = object::Static {
4554      position: component::Position ([2.0, 0.0, 0.0].into()),
4555      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4556      .. a
4557    };
4558    let b = object::Static {
4559      position: component::Position ([0.0, 0.0, 0.0].into()),
4560      bound:    shape::Orthant { normal_axis: SignedAxis3::PosX }.into(),
4561      .. b
4562    };
4563    assert_eq!(
4564      Proximity::query (&a, &b),
4565      Proximity {
4566        distance:  1.0,
4567        half_axis: [-0.5, 0.0, 0.0].into(),
4568        normal:    Unit3::axis_x(),
4569        midpoint:  [ 0.5, 0.0, 0.0].into()
4570      }
4571    );
4572
4573    // disjoint: outside orthant -X
4574    let a = object::Static {
4575      position: component::Position ([-2.0, 0.0, 0.0].into()),
4576      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4577      .. a
4578    };
4579    let b = object::Static {
4580      position: component::Position ([0.0, 0.0, 0.0].into()),
4581      bound:    shape::Orthant { normal_axis: SignedAxis3::NegX }.into(),
4582      .. b
4583    };
4584    assert_eq!(
4585      Proximity::query (&a, &b),
4586      Proximity {
4587        distance:  1.0,
4588        half_axis: [ 0.5, 0.0, 0.0].into(),
4589        normal:    Unit3::axis_x().invert(),
4590        midpoint:  [-0.5, 0.0, 0.0].into()
4591      }
4592    );
4593
4594    // intersect: capsule and orthant position coincide; normal +Z
4595    let a = object::Static {
4596      position: component::Position ([0.0, 0.0, 0.0].into()),
4597      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4598      .. a
4599    };
4600    let b = object::Static {
4601      position: component::Position ([0.0, 0.0, 0.0].into()),
4602      bound:    shape::Orthant { normal_axis: SignedAxis3::PosZ }.into(),
4603      .. b
4604    };
4605    assert_eq!(
4606      Proximity::query (&a, &b),
4607      Proximity {
4608        distance:  -3.0,
4609        half_axis: [0.0, 0.0,  1.5].into(),
4610        normal:    Unit3::axis_z(),
4611        midpoint:  [0.0, 0.0, -1.5].into()
4612      }
4613    );
4614
4615    // intersect: capsule and orthant position coincide; normal -Z
4616    let a = object::Static {
4617      position: component::Position ([0.0, 0.0, 0.0].into()),
4618      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4619      .. a
4620    };
4621    let b = object::Static {
4622      position: component::Position ([0.0, 0.0, 0.0].into()),
4623      bound:    shape::Orthant { normal_axis: SignedAxis3::NegZ }.into(),
4624      .. b
4625    };
4626    assert_eq!(
4627      Proximity::query (&a, &b),
4628      Proximity {
4629        distance:  -3.0,
4630        half_axis: [0.0, 0.0, -1.5].into(),
4631        normal:    Unit3::axis_z().invert(),
4632        midpoint:  [0.0, 0.0,  1.5].into()
4633      }
4634    );
4635
4636    // intersect: capsule and orthant position coincide; normal +Y
4637    let a = object::Static {
4638      position: component::Position ([0.0, 0.0, 0.0].into()),
4639      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4640      .. a
4641    };
4642    let b = object::Static {
4643      position: component::Position ([0.0, 0.0, 0.0].into()),
4644      bound:    shape::Orthant { normal_axis: SignedAxis3::PosY }.into(),
4645      .. b
4646    };
4647    assert_eq!(
4648      Proximity::query (&a, &b),
4649      Proximity {
4650        distance:  -1.0,
4651        half_axis: [0.0,  0.5, 0.0].into(),
4652        normal:    Unit3::axis_y(),
4653        midpoint:  [0.0, -0.5, 0.0].into()
4654      }
4655    );
4656
4657    // intersect: capsule and orthant position coincide; normal -Y
4658    let a = object::Static {
4659      position: component::Position ([0.0, 0.0, 0.0].into()),
4660      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4661      .. a
4662    };
4663    let b = object::Static {
4664      position: component::Position ([0.0, 0.0, 0.0].into()),
4665      bound:    shape::Orthant { normal_axis: SignedAxis3::NegY }.into(),
4666      .. b
4667    };
4668    assert_eq!(
4669      Proximity::query (&a, &b),
4670      Proximity {
4671        distance:  -1.0,
4672        half_axis: [0.0, -0.5, 0.0].into(),
4673        normal:    Unit3::axis_y().invert(),
4674        midpoint:  [0.0,  0.5, 0.0].into()
4675      }
4676    );
4677
4678    // intersect: capsule and orthant position coincide; normal +X
4679    let a = object::Static {
4680      position: component::Position ([0.0, 0.0, 0.0].into()),
4681      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4682      .. a
4683    };
4684    let b = object::Static {
4685      position: component::Position ([0.0, 0.0, 0.0].into()),
4686      bound:    shape::Orthant { normal_axis: SignedAxis3::PosX }.into(),
4687      .. b
4688    };
4689    assert_eq!(
4690      Proximity::query (&a, &b),
4691      Proximity {
4692        distance:  -1.0,
4693        half_axis: [ 0.5, 0.0, 0.0].into(),
4694        normal:    Unit3::axis_x(),
4695        midpoint:  [-0.5, 0.0, 0.0].into()
4696      }
4697    );
4698
4699    // intersect: capsule and orthant position coincide; normal -X
4700    let a = object::Static {
4701      position: component::Position ([0.0, 0.0, 0.0].into()),
4702      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4703      .. a
4704    };
4705    let b = object::Static {
4706      position: component::Position ([0.0, 0.0, 0.0].into()),
4707      bound:    shape::Orthant { normal_axis: SignedAxis3::NegX }.into(),
4708      .. b
4709    };
4710    assert_eq!(
4711      Proximity::query (&a, &b),
4712      Proximity {
4713        distance:  -1.0,
4714        half_axis: [-0.5, 0.0, 0.0].into(),
4715        normal:    Unit3::axis_x().invert(),
4716        midpoint:  [ 0.5, 0.0, 0.0].into()
4717      }
4718    );
4719
4720    //
4721    //  sphere v. orthant
4722    //
4723
4724    // disjoint: sphere outside of orthant +Z
4725    let a = object::Static {
4726      position: component::Position ([0.0, 0.0, 4.0].into()),
4727      bound:    component::Bound::from (shape::Sphere::noisy (1.0)),
4728      .. a
4729    };
4730    let b = object::Static {
4731      position: component::Position ([0.0, 0.0, 0.0].into()),
4732      bound:    shape::Orthant { normal_axis: SignedAxis3::PosZ }.into(),
4733      .. b
4734    };
4735    assert_eq!(
4736      Proximity::query (&a, &b),
4737      Proximity {
4738        distance:  3.0,
4739        half_axis: [0.0, 0.0, -1.5].into(),
4740        normal:    Unit3::axis_z(),
4741        midpoint:  [0.0, 0.0,  1.5].into()
4742      }
4743    );
4744
4745    // disjoint: sphere outside of orthant -Z
4746    let a = object::Static {
4747      position: component::Position ([0.0, 0.0, -4.0].into()),
4748      bound:    component::Bound::from (shape::Sphere::noisy (1.0)),
4749      .. a
4750    };
4751    let b = object::Static {
4752      position: component::Position ([0.0, 0.0, 0.0].into()),
4753      bound:    shape::Orthant { normal_axis: SignedAxis3::NegZ }.into(),
4754      .. b
4755    };
4756    assert_eq!(
4757      Proximity::query (&a, &b),
4758      Proximity {
4759        distance:  3.0,
4760        half_axis: [0.0, 0.0,  1.5].into(),
4761        normal:    Unit3::axis_z().invert(),
4762        midpoint:  [0.0, 0.0, -1.5].into()
4763      }
4764    );
4765
4766    // disjoint: sphere outside orthant +Y
4767    let a = object::Static {
4768      position: component::Position ([0.0, 2.0, 0.0].into()),
4769      bound:    component::Bound::from (shape::Sphere::noisy (1.0)),
4770      .. a
4771    };
4772    let b = object::Static {
4773      position: component::Position ([0.0, 0.0, 0.0].into()),
4774      bound:    shape::Orthant { normal_axis: SignedAxis3::PosY }.into(),
4775      .. b
4776    };
4777    assert_eq!(
4778      Proximity::query (&a, &b),
4779      Proximity {
4780        distance:  1.0,
4781        half_axis: [0.0, -0.5, 0.0].into(),
4782        normal:    Unit3::axis_y(),
4783        midpoint:  [0.0,  0.5, 0.0].into()
4784      }
4785    );
4786
4787    // disjoint: sphere outside orthant -Y
4788    let a = object::Static {
4789      position: component::Position ([0.0, -2.0, 0.0].into()),
4790      bound:    component::Bound::from (shape::Sphere::noisy (1.0)),
4791      .. a
4792    };
4793    let b = object::Static {
4794      position: component::Position ([0.0, 0.0, 0.0].into()),
4795      bound:    shape::Orthant { normal_axis: SignedAxis3::NegY }.into(),
4796      .. b
4797    };
4798    assert_eq!(
4799      Proximity::query (&a, &b),
4800      Proximity {
4801        distance:  1.0,
4802        half_axis: [0.0,  0.5, 0.0].into(),
4803        normal:    Unit3::axis_y().invert(),
4804        midpoint:  [0.0, -0.5, 0.0].into()
4805      }
4806    );
4807
4808    // disjoint: sphere outside orthant +X
4809    let a = object::Static {
4810      position: component::Position ([2.0, 0.0, 0.0].into()),
4811      bound:    component::Bound::from (shape::Sphere::noisy (1.0)),
4812      .. a
4813    };
4814    let b = object::Static {
4815      position: component::Position ([0.0, 0.0, 0.0].into()),
4816      bound:    shape::Orthant { normal_axis: SignedAxis3::PosX }.into(),
4817      .. b
4818    };
4819    assert_eq!(
4820      Proximity::query (&a, &b),
4821      Proximity {
4822        distance:  1.0,
4823        half_axis: [-0.5, 0.0, 0.0].into(),
4824        normal:    Unit3::axis_x(),
4825        midpoint:  [ 0.5, 0.0, 0.0].into()
4826      }
4827    );
4828
4829    // disjoint: outside orthant -X
4830    let a = object::Static {
4831      position: component::Position ([-2.0, 0.0, 0.0].into()),
4832      bound:    component::Bound::from (shape::Sphere::noisy (1.0)),
4833      .. a
4834    };
4835    let b = object::Static {
4836      position: component::Position ([0.0, 0.0, 0.0].into()),
4837      bound:    shape::Orthant { normal_axis: SignedAxis3::NegX }.into(),
4838      .. b
4839    };
4840    assert_eq!(
4841      Proximity::query (&a, &b),
4842      Proximity {
4843        distance:  1.0,
4844        half_axis: [ 0.5, 0.0, 0.0].into(),
4845        normal:    Unit3::axis_x().invert(),
4846        midpoint:  [-0.5, 0.0, 0.0].into()
4847      }
4848    );
4849
4850    // intersect: sphere and orthant position coincide; normal +Z
4851    let a = object::Static {
4852      position: component::Position ([0.0, 0.0, 0.0].into()),
4853      bound:    component::Bound::from (shape::Sphere::noisy (1.0)),
4854      .. a
4855    };
4856    let b = object::Static {
4857      position: component::Position ([0.0, 0.0, 0.0].into()),
4858      bound:    shape::Orthant { normal_axis: SignedAxis3::PosZ }.into(),
4859      .. b
4860    };
4861    assert_eq!(
4862      Proximity::query (&a, &b),
4863      Proximity {
4864        distance:  -1.0,
4865        half_axis: [0.0, 0.0,  0.5].into(),
4866        normal:    Unit3::axis_z(),
4867        midpoint:  [0.0, 0.0, -0.5].into()
4868      }
4869    );
4870
4871    // intersect: sphere and orthant position coincide; normal -Z
4872    let a = object::Static {
4873      position: component::Position ([0.0, 0.0, 0.0].into()),
4874      bound:    component::Bound::from (shape::Sphere::noisy (1.0)),
4875      .. a
4876    };
4877    let b = object::Static {
4878      position: component::Position ([0.0, 0.0, 0.0].into()),
4879      bound:    shape::Orthant { normal_axis: SignedAxis3::NegZ }.into(),
4880      .. b
4881    };
4882    assert_eq!(
4883      Proximity::query (&a, &b),
4884      Proximity {
4885        distance:  -1.0,
4886        half_axis: [0.0, 0.0, -0.5].into(),
4887        normal:    Unit3::axis_z().invert(),
4888        midpoint:  [0.0, 0.0,  0.5].into()
4889      }
4890    );
4891
4892    // intersect: sphere and orthant position coincide; normal +Y
4893    let a = object::Static {
4894      position: component::Position ([0.0, 0.0, 0.0].into()),
4895      bound:    component::Bound::from (shape::Sphere::noisy (1.0)),
4896      .. a
4897    };
4898    let b = object::Static {
4899      position: component::Position ([0.0, 0.0, 0.0].into()),
4900      bound:    shape::Orthant { normal_axis: SignedAxis3::PosY }.into(),
4901      .. b
4902    };
4903    assert_eq!(
4904      Proximity::query (&a, &b),
4905      Proximity {
4906        distance:  -1.0,
4907        half_axis: [0.0,  0.5, 0.0].into(),
4908        normal:    Unit3::axis_y(),
4909        midpoint:  [0.0, -0.5, 0.0].into()
4910      }
4911    );
4912
4913    // intersect: sphere and orthant position coincide; normal -Y
4914    let a = object::Static {
4915      position: component::Position ([0.0, 0.0, 0.0].into()),
4916      bound:    component::Bound::from (shape::Sphere::noisy (1.0)),
4917      .. a
4918    };
4919    let b = object::Static {
4920      position: component::Position ([0.0, 0.0, 0.0].into()),
4921      bound:    shape::Orthant { normal_axis: SignedAxis3::NegY }.into(),
4922      .. b
4923    };
4924    assert_eq!(
4925      Proximity::query (&a, &b),
4926      Proximity {
4927        distance:  -1.0,
4928        half_axis: [0.0, -0.5, 0.0].into(),
4929        normal:    Unit3::axis_y().invert(),
4930        midpoint:  [0.0,  0.5, 0.0].into()
4931      }
4932    );
4933
4934    // intersect: sphere and orthant position coincide; normal +X
4935    let a = object::Static {
4936      position: component::Position ([0.0, 0.0, 0.0].into()),
4937      bound:    component::Bound::from (shape::Sphere::noisy (1.0)),
4938      .. a
4939    };
4940    let b = object::Static {
4941      position: component::Position ([0.0, 0.0, 0.0].into()),
4942      bound:    shape::Orthant { normal_axis: SignedAxis3::PosX }.into(),
4943      .. b
4944    };
4945    assert_eq!(
4946      Proximity::query (&a, &b),
4947      Proximity {
4948        distance:  -1.0,
4949        half_axis: [ 0.5, 0.0, 0.0].into(),
4950        normal:    Unit3::axis_x(),
4951        midpoint:  [-0.5, 0.0, 0.0].into()
4952      }
4953    );
4954
4955    // intersect: sphere and orthant position coincide; normal -X
4956    let a = object::Static {
4957      position: component::Position ([0.0, 0.0, 0.0].into()),
4958      bound:    component::Bound::from (shape::Sphere::noisy (1.0)),
4959      .. a
4960    };
4961    let b = object::Static {
4962      position: component::Position ([0.0, 0.0, 0.0].into()),
4963      bound:    shape::Orthant { normal_axis: SignedAxis3::NegX }.into(),
4964      .. b
4965    };
4966    assert_eq!(
4967      Proximity::query (&a, &b),
4968      Proximity {
4969        distance:  -1.0,
4970        half_axis: [-0.5, 0.0, 0.0].into(),
4971        normal:    Unit3::axis_x().invert(),
4972        midpoint:  [ 0.5, 0.0, 0.0].into()
4973      }
4974    );
4975
4976    //
4977    //  cuboid v. orthant
4978    //
4979
4980    // disjoint: cuboid outside of orthant +Z
4981    let a = object::Static {
4982      position: component::Position ([0.0, 0.0, 4.0].into()),
4983      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
4984      .. a
4985    };
4986    let b = object::Static {
4987      position: component::Position ([0.0, 0.0, 0.0].into()),
4988      bound:    shape::Orthant { normal_axis: SignedAxis3::PosZ }.into(),
4989      .. b
4990    };
4991    assert_eq!(
4992      Proximity::query (&a, &b),
4993      Proximity {
4994        distance:  3.0,
4995        half_axis: [0.0, 0.0, -1.5].into(),
4996        normal:    Unit3::axis_z(),
4997        midpoint:  [0.0, 0.0,  1.5].into()
4998      }
4999    );
5000
5001    // disjoint: cuboid outside of orthant -Z
5002    let a = object::Static {
5003      position: component::Position ([0.0, 0.0, -4.0].into()),
5004      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
5005      .. a
5006    };
5007    let b = object::Static {
5008      position: component::Position ([0.0, 0.0, 0.0].into()),
5009      bound:    shape::Orthant { normal_axis: SignedAxis3::NegZ }.into(),
5010      .. b
5011    };
5012    assert_eq!(
5013      Proximity::query (&a, &b),
5014      Proximity {
5015        distance:  3.0,
5016        half_axis: [0.0, 0.0,  1.5].into(),
5017        normal:    Unit3::axis_z().invert(),
5018        midpoint:  [0.0, 0.0, -1.5].into()
5019      }
5020    );
5021
5022    // disjoint: cuboid outside orthant +Y
5023    let a = object::Static {
5024      position: component::Position ([0.0, 2.0, 0.0].into()),
5025      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
5026      .. a
5027    };
5028    let b = object::Static {
5029      position: component::Position ([0.0, 0.0, 0.0].into()),
5030      bound:    shape::Orthant { normal_axis: SignedAxis3::PosY }.into(),
5031      .. b
5032    };
5033    assert_eq!(
5034      Proximity::query (&a, &b),
5035      Proximity {
5036        distance:  1.0,
5037        half_axis: [0.0, -0.5, 0.0].into(),
5038        normal:    Unit3::axis_y(),
5039        midpoint:  [0.0,  0.5, 0.0].into()
5040      }
5041    );
5042
5043    // disjoint: cuboid outside orthant -Y
5044    let a = object::Static {
5045      position: component::Position ([0.0, -2.0, 0.0].into()),
5046      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
5047      .. a
5048    };
5049    let b = object::Static {
5050      position: component::Position ([0.0, 0.0, 0.0].into()),
5051      bound:    shape::Orthant { normal_axis: SignedAxis3::NegY }.into(),
5052      .. b
5053    };
5054    assert_eq!(
5055      Proximity::query (&a, &b),
5056      Proximity {
5057        distance:  1.0,
5058        half_axis: [0.0,  0.5, 0.0].into(),
5059        normal:    Unit3::axis_y().invert(),
5060        midpoint:  [0.0, -0.5, 0.0].into()
5061      }
5062    );
5063
5064    // disjoint: cuboid outside orthant +X
5065    let a = object::Static {
5066      position: component::Position ([2.0, 0.0, 0.0].into()),
5067      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
5068      .. a
5069    };
5070    let b = object::Static {
5071      position: component::Position ([0.0, 0.0, 0.0].into()),
5072      bound:    shape::Orthant { normal_axis: SignedAxis3::PosX }.into(),
5073      .. b
5074    };
5075    assert_eq!(
5076      Proximity::query (&a, &b),
5077      Proximity {
5078        distance:  1.0,
5079        half_axis: [-0.5, 0.0, 0.0].into(),
5080        normal:    Unit3::axis_x(),
5081        midpoint:  [ 0.5, 0.0, 0.0].into()
5082      }
5083    );
5084
5085    // disjoint: outside orthant -X
5086    let a = object::Static {
5087      position: component::Position ([-2.0, 0.0, 0.0].into()),
5088      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
5089      .. a
5090    };
5091    let b = object::Static {
5092      position: component::Position ([0.0, 0.0, 0.0].into()),
5093      bound:    shape::Orthant { normal_axis: SignedAxis3::NegX }.into(),
5094      .. b
5095    };
5096    assert_eq!(
5097      Proximity::query (&a, &b),
5098      Proximity {
5099        distance:  1.0,
5100        half_axis: [ 0.5, 0.0, 0.0].into(),
5101        normal:    Unit3::axis_x().invert(),
5102        midpoint:  [-0.5, 0.0, 0.0].into()
5103      }
5104    );
5105
5106    // intersect: cuboid and orthant position coincide; normal +Z
5107    let a = object::Static {
5108      position: component::Position ([0.0, 0.0, 0.0].into()),
5109      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
5110      .. a
5111    };
5112    let b = object::Static {
5113      position: component::Position ([0.0, 0.0, 0.0].into()),
5114      bound:    shape::Orthant { normal_axis: SignedAxis3::PosZ }.into(),
5115      .. b
5116    };
5117    assert_eq!(
5118      Proximity::query (&a, &b),
5119      Proximity {
5120        distance:  -1.0,
5121        half_axis: [0.0, 0.0,  0.5].into(),
5122        normal:    Unit3::axis_z(),
5123        midpoint:  [0.0, 0.0, -0.5].into()
5124      }
5125    );
5126
5127    // intersect: cuboid and orthant position coincide; normal -Z
5128    let a = object::Static {
5129      position: component::Position ([0.0, 0.0, 0.0].into()),
5130      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
5131      .. a
5132    };
5133    let b = object::Static {
5134      position: component::Position ([0.0, 0.0, 0.0].into()),
5135      bound:    shape::Orthant { normal_axis: SignedAxis3::NegZ }.into(),
5136      .. b
5137    };
5138    assert_eq!(
5139      Proximity::query (&a, &b),
5140      Proximity {
5141        distance:  -1.0,
5142        half_axis: [0.0, 0.0, -0.5].into(),
5143        normal:    Unit3::axis_z().invert(),
5144        midpoint:  [0.0, 0.0,  0.5].into()
5145      }
5146    );
5147
5148    // intersect: cuboid and orthant position coincide; normal +Y
5149    let a = object::Static {
5150      position: component::Position ([0.0, 0.0, 0.0].into()),
5151      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
5152      .. a
5153    };
5154    let b = object::Static {
5155      position: component::Position ([0.0, 0.0, 0.0].into()),
5156      bound:    shape::Orthant { normal_axis: SignedAxis3::PosY }.into(),
5157      .. b
5158    };
5159    assert_eq!(
5160      Proximity::query (&a, &b),
5161      Proximity {
5162        distance:  -1.0,
5163        half_axis: [0.0,  0.5, 0.0].into(),
5164        normal:    Unit3::axis_y(),
5165        midpoint:  [0.0, -0.5, 0.0].into()
5166      }
5167    );
5168
5169    // intersect: cuboid and orthant position coincide; normal -Y
5170    let a = object::Static {
5171      position: component::Position ([0.0, 0.0, 0.0].into()),
5172      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
5173      .. a
5174    };
5175    let b = object::Static {
5176      position: component::Position ([0.0, 0.0, 0.0].into()),
5177      bound:    shape::Orthant { normal_axis: SignedAxis3::NegY }.into(),
5178      .. b
5179    };
5180    assert_eq!(
5181      Proximity::query (&a, &b),
5182      Proximity {
5183        distance:  -1.0,
5184        half_axis: [0.0, -0.5, 0.0].into(),
5185        normal:    Unit3::axis_y().invert(),
5186        midpoint:  [0.0,  0.5, 0.0].into()
5187      }
5188    );
5189
5190    // intersect: cuboid and orthant position coincide; normal +X
5191    let a = object::Static {
5192      position: component::Position ([0.0, 0.0, 0.0].into()),
5193      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
5194      .. a
5195    };
5196    let b = object::Static {
5197      position: component::Position ([0.0, 0.0, 0.0].into()),
5198      bound:    shape::Orthant { normal_axis: SignedAxis3::PosX }.into(),
5199      .. b
5200    };
5201    assert_eq!(
5202      Proximity::query (&a, &b),
5203      Proximity {
5204        distance:  -1.0,
5205        half_axis: [ 0.5, 0.0, 0.0].into(),
5206        normal:    Unit3::axis_x(),
5207        midpoint:  [-0.5, 0.0, 0.0].into()
5208      }
5209    );
5210
5211    // intersect: cuboid and orthant position coincide; normal -X
5212    let a = object::Static {
5213      position: component::Position ([0.0, 0.0, 0.0].into()),
5214      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
5215      .. a
5216    };
5217    let b = object::Static {
5218      position: component::Position ([0.0, 0.0, 0.0].into()),
5219      bound:    shape::Orthant { normal_axis: SignedAxis3::NegX }.into(),
5220      .. b
5221    };
5222    assert_eq!(
5223      Proximity::query (&a, &b),
5224      Proximity {
5225        distance:  -1.0,
5226        half_axis: [-0.5, 0.0, 0.0].into(),
5227        normal:    Unit3::axis_x().invert(),
5228        midpoint:  [ 0.5, 0.0, 0.0].into()
5229      }
5230    );
5231
5232  } // end test_distance_query
5233
5234  #[test]
5235  #[expect(clippy::approx_constant)]
5236  fn query_hull() {
5237    // cuboid/hull
5238    let object_a = (
5239      component::Position ([0.0, 1.0, 1.6].into()),
5240      component::Bound::from (shape::Cuboid::noisy ([2.2, 2.2, 0.2]))
5241    );
5242    let object_b = {
5243      let hull = {
5244        let points = [
5245          [0.7333798838354574, 0.07387687099658402, -0.4254516842354885],
5246          [0.28934048979234384, -0.41943067939022566, 0.20302532977593885],
5247          [0.006562630606422983, 0.7345982182090985, 0.6784706255126608],
5248          [-0.16787537854609894, -0.7656664645854194, 0.004114727975213302],
5249          [-0.6400977561459589, -0.062079241531162725, -0.33763483879239625],
5250          [-0.3844054415702367, 0.6216012595122049, -0.04739397447380115]
5251        ].map (Point3::from);
5252        geometry::Hull3::from_points (&points).unwrap()
5253      };
5254      ( component::Position (
5255          [-0.021233076872148704, 1.4862487906056179, 2.2279516842354887].into()),
5256        component::Bound::from (hull)
5257      )
5258    };
5259    let proximity = Proximity::query (&object_a, &object_b);
5260    assert_eq!(proximity.distance, 0.0025000000000001688);
5261
5262    let object_a = {
5263      let hull = {
5264        let points = [
5265          [-1.6970562748477143, -2.2,  1.4142135623730951],
5266          [-1.4142135623730954, -2.2,  1.697056274847714],
5267          [-1.6970562748477143,  2.2,  1.4142135623730951],
5268          [-1.4142135623730954,  2.2,  1.697056274847714],
5269          [ 1.4142135623730954, -2.2, -1.697056274847714],
5270          [ 1.6970562748477143, -2.2, -1.4142135623730951],
5271          [ 1.4142135623730954,  2.2, -1.697056274847714],
5272          [ 1.6970562748477143,  2.2, -1.4142135623730951]
5273        ].map (Point3::from);
5274        geometry::Hull3::from_points (&points).unwrap()
5275      };
5276      ( component::Position ([-3.9, 1.0, 11.2125].into()),
5277        component::Bound::from (hull)
5278      )
5279    };
5280    let object_b = (
5281      component::Position (
5282        [-4.004858348343004, -1.2161640324940572, 11.869089780656527].into()),
5283      component::Bound::from (shape::Sphere::noisy (0.5))
5284    );
5285    let proximity = Proximity::query (&object_a, &object_b);
5286    assert_eq!(proximity.distance, -0.3091811079468084);
5287
5288    let object_a = {
5289      let hull = {
5290        let points = [
5291          [-2.2, -1.4142135623730954, -1.697056274847714],
5292          [-2.2, -1.6970562748477143, -1.4142135623730951],
5293          [-2.2,  1.6970562748477143,  1.4142135623730951],
5294          [-2.2,  1.4142135623730954,  1.697056274847714],
5295          [ 2.2, -1.4142135623730954, -1.697056274847714],
5296          [ 2.2, -1.6970562748477143, -1.4142135623730951],
5297          [ 2.2,  1.6970562748477143,  1.4142135623730951],
5298          [ 2.2,  1.4142135623730954,  1.69705627484771]
5299        ].map (Point3::from);
5300        geometry::Hull3::from_points (&points).unwrap()
5301      };
5302      ( component::Position ([0.0, 4.9, 11.2125].into()),
5303        component::Bound::from (hull)
5304      )
5305    };
5306    let object_b = (
5307      component::Position (
5308        [1.3113904930865399, 3.1609676773871804, 9.819471286970987].into()),
5309      component::Bound::from (shape::Sphere::noisy (0.5))
5310    );
5311    let proximity = Proximity::query (&object_a, &object_b);
5312    assert_eq!(proximity.distance, -0.4529809974587258);
5313
5314    let object_a = {
5315      let hull = {
5316        let points = [
5317          [-2.2, -1.6970562748477138,   1.4142135623730956],
5318          [-2.2, -1.414213562373095,    1.6970562748477145],
5319          [-2.2,  1.414213562373095,   -1.6970562748477145],
5320          [-2.2,  1.6970562748477138,  -1.4142135623730956],
5321          [ 2.2, -1.6970562748477138,   1.4142135623730956],
5322          [ 2.2, -1.414213562373095,    1.6970562748477145],
5323          [ 2.2,  1.414213562373095,   -1.6970562748477145],
5324          [ 2.2,  1.6970562748477138,  -1.4142135623730956]
5325        ].map (Point3::from);
5326        geometry::Hull3::from_points (&points).unwrap()
5327      };
5328      ( component::Position ([0.0, -2.9, 11.2125].into()),
5329        component::Bound::from (hull)
5330      )
5331    };
5332    let object_b = (
5333      component::Position (
5334        [2.256458188002921, -0.9674933916701636, 10.065811222437437].into()),
5335      component::Bound::from (shape::Sphere::noisy (0.5))
5336    );
5337    let proximity = Proximity::query (&object_a, &object_b);
5338    assert_eq!(proximity.distance, -0.13988958350691275);
5339
5340    let object_a = {
5341      let hull = {
5342        let points = [
5343          [-1.8000000000000003, -1.4142135623730954, -1.697056274847714 ],
5344          [-1.8000000000000003, -1.6970562748477143, -1.4142135623730951],
5345          [-1.8000000000000003,  1.6970562748477143,  1.4142135623730951],
5346          [-1.8000000000000003,  1.4142135623730954,  1.697056274847714 ],
5347          [ 1.8000000000000003, -1.4142135623730954, -1.697056274847714 ],
5348          [ 1.8000000000000003, -1.6970562748477143, -1.4142135623730951],
5349          [ 1.8000000000000003,  1.6970562748477143,  1.4142135623730951],
5350          [ 1.8000000000000003,  1.4142135623730954,  1.697056274847714 ]
5351        ].map (Point3::from);
5352        geometry::Hull3::from_points (&points).unwrap()
5353      };
5354      ( component::Position ([0.0, 4.5, 11.2125].into()),
5355        component::Bound::from (hull)
5356      )
5357    };
5358    let object_b = (
5359      component::Position (
5360        [-1.8483359321030037, 3.6499052772728637, 11.360649949330503].into()),
5361      component::Bound::from (shape::Sphere::noisy (0.5))
5362    );
5363    let proximity = Proximity::query (&object_a, &object_b);
5364    assert_eq!(proximity.distance, 0.008169602403322415);
5365  }
5366
5367  #[test]
5368  fn distance_to_triangle_edges() {
5369    let triangle = geometry::Triangle3::noisy (
5370      [-2.0, -1.0, 0.0].into(),
5371      [ 1.0, -1.0, 0.0].into(),
5372      [ 1.0,  5.0, 0.0].into()
5373    );
5374    // origin
5375    let s = 0.5;
5376    let t = 1.0 / 6.0;
5377    let (d0, d1, d2) = triangle_distance_to_edges (triangle, s, t);
5378    approx::assert_relative_eq!(d0, 1.0);
5379    assert_eq!(d1, 3.0 / 5.0.sqrt());
5380    assert_eq!(d2, 1.0);
5381  }
5382
5383} // end tests