Skip to main content

linear_sim/collision/proximity/
mod.rs

1use crate::{collision, component, 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/// - 'Intersect' 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 plane (&self) -> geometry::Plane3 <f64> {
75    geometry::Plane3::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      use geometry::Primitive;
1926      let mut out = None;
1927      let mut min_distance = f64::MAX;
1928      for edge in triangle_a.edges() {
1929        let proximity = Proximity::query_triangle_segment (triangle_b, &edge);
1930        if proximity.distance < -f64::EPSILON * 2.0.powi (12) {
1931          out = None;
1932          break
1933        }
1934        if proximity.distance < min_distance {
1935          min_distance = proximity.distance;
1936          // we swap the roles of object A and B
1937          out = Some (proximity.flip());
1938        }
1939      }
1940      if out.is_some() {
1941        // only check the other triangle if the first triangle didn't overlap
1942        for edge in triangle_b.edges() {
1943          let proximity = Proximity::query_triangle_segment (triangle_a, &edge);
1944          if proximity.distance < -f64::EPSILON * 2.0.powi (12) {
1945            out = None;
1946            break
1947          }
1948          if proximity.distance < min_distance {
1949            min_distance = proximity.distance;
1950            out = Some (proximity);
1951          }
1952        }
1953      }
1954      if out.is_none() {
1955        // triangles overlap
1956        if is_recurse {
1957          log::error!("triangle/triangle proximity query can't recurse more than once");
1958          panic!("triangle/triangle proximity query can't recurse more than once");
1959        }
1960        // edge intersected: construct minkowski hull A - B
1961        let points : [Point3 <f64>; 9] = std::array::from_fn (|i|{
1962          let p = triangle_a.points()[i / 3];
1963          let q = triangle_b.points()[i % 3];
1964          Point3 (p.0 - q.0)
1965        });
1966        // brute force search: there are 9 choose 3 = 84 unique triangles, for each
1967        // triangle, calculate the distance and whether or not it is exterior; discard
1968        // any triangles that are further away than the nearest exterior triangle
1969        let get_triangle = |a, b, c|
1970          geometry::Triangle3::new (points[a], points[b], points[c]);
1971        let mut nearest_distance : Option <geometry::Distance3 <f64>> = None;
1972        let tolerance = f64::EPSILON * 2.0.powi (10);
1973        for i in 0..9 {
1974          for j in (i + 1)..9 {
1975            for k in (j + 1)..9 {
1976              if let Some (triangle) = get_triangle (i, j, k) {
1977                let distance =
1978                  geometry::Distance3::triangle_point (triangle, Point3::origin());
1979                if distance.distance_squared() < nearest_distance.as_ref().map_or (
1980                  NonNegative::unchecked (f64::MAX),
1981                  geometry::Distance3::distance_squared)
1982                {
1983                  // check if it is exterior
1984                  let normal = {
1985                    let mut normal = triangle.normal();
1986                    if normal.dot (triangle.point_a().0) < 0.0 {
1987                      normal = -normal
1988                    }
1989                    normal
1990                  };
1991                  let mut exterior = true;
1992                  for point in points.iter() {
1993                    if normal.dot (point - triangle.point_a()) > tolerance {
1994                      exterior = false;
1995                      break
1996                    }
1997                  }
1998                  if exterior {
1999                    nearest_distance = Some (distance);
2000                  }
2001                }
2002              }
2003            }
2004          }
2005        }
2006        let mut nearest = nearest_distance.unwrap();
2007        let distance    = -*nearest.distance();
2008        let half_axis   = -nearest.nearest_a().0 * 0.5;
2009        let resolved_proximity = {
2010          let translate = |mut triangle : geometry::Triangle3 <f64>, displacement| {
2011            triangle.translate (displacement);
2012            triangle
2013          };
2014          let resolved_a = translate (*triangle_a, half_axis);
2015          let resolved_b = translate (*triangle_b, -half_axis);
2016          // TODO: instead of recursing can we query nearest features ?
2017          query_triangle_triangle_recursive (&resolved_a, &resolved_b, true)
2018        };
2019        out = Some(Proximity { distance, half_axis, .. resolved_proximity });
2020      }
2021      out.unwrap()
2022    }
2023    query_triangle_triangle_recursive (triangle_a, triangle_b, false)
2024  }
2025
2026  pub fn query_triangle_line (
2027    triangle : &geometry::Triangle3 <f64>,
2028    line     : &geometry::Segment3 <f64>
2029  ) -> Self {
2030    use approx::{AbsDiffEq, RelativeEq};
2031    let (([s, t], nearest_a), (_, nearest_b)) =
2032      geometry::distance::nearest_triangle3_line3 (*triangle, *line);
2033    if approx::relative_eq!(nearest_a, nearest_b,
2034      max_relative = f64::default_max_relative() * 256.0,
2035      epsilon      = f64::default_epsilon() * 256.0)
2036    {
2037      // line and triangle intersect
2038      let distance;
2039      let half_axis;
2040      let triangle_normal = triangle.normal();
2041      let (midpoint, normal) = if approx::abs_diff_eq!(triangle_normal.dot (*line.vector()), 0.0) {
2042        // line and triangle are parallel:
2043        // normal is perpendicular to triangle face
2044        let a_to_b   = nearest_b - nearest_a;
2045        distance     = a_to_b.magnitude();
2046        half_axis    = a_to_b * 0.5;
2047        let midpoint = nearest_a + half_axis;
2048        let normal   = triangle_normal.normalize();
2049        (midpoint, normal)
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        let 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        let 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        (midpoint, normal)
2106      };
2107      return Proximity { distance, half_axis, midpoint, normal }
2108    }
2109    let a_to_b    = nearest_b - nearest_a;
2110    let distance  = a_to_b.magnitude();
2111    let half_axis = a_to_b * 0.5;
2112    let midpoint  = nearest_a + half_axis;
2113    let normal    = Unit3::normalize (-a_to_b);
2114    Proximity { distance, half_axis, midpoint, normal }
2115  }
2116
2117  pub fn query_triangle_segment (
2118    triangle : &geometry::Triangle3 <f64>,
2119    segment  : &geometry::Segment3 <f64>
2120  ) -> Self {
2121    use approx::{AbsDiffEq, RelativeEq};
2122    let (([s, t], nearest_a), (_r, nearest_b)) =
2123      geometry::distance::nearest_triangle3_segment3 (*triangle, *segment);
2124    if approx::relative_eq!(nearest_a, nearest_b,
2125      max_relative = f64::default_max_relative() * 2.0.powi (17),
2126      epsilon      = f64::default_epsilon() * 2.0.powi (17))
2127    {
2128      // segment and triangle intersect
2129      // we can check edge intersection by looking at s and t
2130      if let Some((mut proximity, edge_perp)) = if approx::abs_diff_eq!(*s, 0.0,
2131          epsilon = f64::default_epsilon() * 2.0.powi (23))
2132        {
2133          // intersect edge AC
2134          Some (( Proximity::query_segment_segment (&triangle.edge_ca(), segment),
2135            triangle.perpendicular_ca() ))
2136        } else if approx::abs_diff_eq!(*t, 0.0,
2137          epsilon = f64::default_epsilon() * 2.0.powi (23))
2138        {
2139          // intersect edge AB
2140          Some (( Proximity::query_segment_segment (&triangle.edge_ab(), segment),
2141            triangle.perpendicular_ab() ))
2142        } else if approx::relative_eq!(*s + *t, 1.0,
2143          max_relative = f64::default_max_relative() * 2.0.powi (23),
2144          epsilon = f64::default_epsilon() * 2.0.powi (23))
2145        {
2146          // intersect edge BC
2147          Some (( Proximity::query_segment_segment (&triangle.edge_bc(), segment),
2148            triangle.perpendicular_bc() ))
2149        } else {
2150          None
2151        }
2152      {
2153        if edge_perp.dot (*proximity.normal) > 0.0 {
2154          // ensure normal points from segment towards triangle
2155          proximity.normal = -proximity.normal;
2156        }
2157        return proximity
2158      }
2159      // the penetration depth is the shortest distance to move the segment to one of
2160      // the triangle edges, or else to move the segment perpendicularly to the triangle
2161      // surface
2162      let distance;
2163      let half_axis;
2164      let midpoint;
2165      let normal;
2166      let triangle_normal          = triangle.normal();
2167      let mut triangle_normal_unit = None;
2168      let segment_vec              = segment.vector();
2169      if approx::abs_diff_eq!(triangle_normal.dot (*segment_vec), 0.0) {
2170        // segment and triangle are parallel:
2171        // penetration depth is zero and direction is perpendicular to triangle face
2172        distance  = 0.0;
2173        half_axis = Vector3::zero();
2174        midpoint  = nearest_a;
2175        normal    = triangle_normal.normalize();
2176      } else {
2177        // segment intersects triangle at a single point
2178        let (seg_depth_sq, seg_depth_vec) = if triangle_normal.cross (*segment_vec)
2179          == Vector3::zero()
2180        {
2181          // segment is perpendicular to triangle: just use endpoint distances
2182          let depth_vec_a = segment.point_a() - nearest_b;
2183          let depth_vec_b = segment.point_b() - nearest_b;
2184          let dsq_a = depth_vec_a.magnitude_squared();
2185          let dsq_b = depth_vec_b.magnitude_squared();
2186          if dsq_a < dsq_b {
2187            (dsq_a, depth_vec_a)
2188          } else {
2189            (dsq_b, depth_vec_b)
2190          }
2191        } else {
2192          // segment intersects at an angle
2193          // project onto triangle plane
2194          let normal_unit      = triangle_normal.normalize();
2195          triangle_normal_unit = Some (normal_unit);
2196          let tri0_to_seg0     = segment.point_a() - triangle.point_a();
2197          let tri0_to_seg1     = segment.point_b() - triangle.point_a();
2198          let normal_dot_seg0  = normal_unit.dot (tri0_to_seg0);
2199          let normal_dot_seg1  = normal_unit.dot (tri0_to_seg1);
2200          let proj_seg0        = segment.point_a() - *normal_unit * normal_dot_seg0;
2201          let proj_seg1        = segment.point_b() - *normal_unit * normal_dot_seg1;
2202          // convert projected points to barycentric coordinates
2203          let ab               = triangle.point_b() - triangle.point_a();
2204          let ac               = triangle.point_c() - triangle.point_a();
2205          let ab_mag_sq        = ab.magnitude_squared();
2206          let ab_dot_ac        = ab.dot (ac);
2207          let ac_mag_sq        = ac.magnitude_squared();
2208          //let determinant     = ab_mag_sq * ac_mag_sq - ab_dot_ac.squared();
2209          let determinant      = ab_mag_sq.mul_add (ac_mag_sq,  -ab_dot_ac.squared());
2210          // barycentric coords: b0 = 1 - s - t, b1 = s, b2 = t
2211          let barycentric_to_cartesian = |s, t| triangle.point_a() + ab * s + ac * t;
2212          let barycentric_coords = |p_proj : Point3 <f64>| {
2213            let to_p = p_proj - triangle.point_a();
2214            let to_p_dot_ab = to_p.dot (ab);
2215            let to_p_dot_ac = to_p.dot (ac);
2216            //let s = (ac_mag_sq * to_p_dot_ab - ab_dot_ac * to_p_dot_ac) / determinant;
2217            let s = ac_mag_sq.mul_add (to_p_dot_ab,  -ab_dot_ac * to_p_dot_ac)
2218              / determinant;
2219            //let t = (ab_mag_sq * to_p_dot_ac - ab_dot_ac * to_p_dot_ab) / determinant;
2220            let t = ab_mag_sq.mul_add (to_p_dot_ac, -ab_dot_ac * to_p_dot_ab)
2221              / determinant;
2222            if cfg!(debug_assertions) {
2223              approx::assert_relative_eq!(p_proj, barycentric_to_cartesian (s, t),
2224                epsilon = f64::default_epsilon() * 2.0.powi (40),
2225                max_relative = f64::default_max_relative() * 2.0.powi (40));
2226            }
2227            [s, t]
2228          };
2229          let [s0, t0] = barycentric_coords (proj_seg0);
2230          let [s1, t1] = barycentric_coords (proj_seg1);
2231          let (base_s, base_t) = (s0, t0);
2232          let (vec_s, vec_t)   = (s1 - s0, t1 - t0);
2233          // returns clipped barycentric coordinates and parameter along segment
2234          let clip_seg_endpoint = |s, t| {
2235            // clip against each edge in barycentric space
2236            // describe the projected line segment in barycentric space as parameterized
2237            // line `base + u * vec`
2238            // edge AB: t
2239            // edge BC: 1 - s - t
2240            // edge CA: s
2241            // clip s == 0
2242            let (mut s, mut t) = (s, t);
2243            let mut u = 0.0;
2244            #[expect(clippy::useless_let_if_seq)]
2245            let mut clipped = false;
2246            if s < 0.0 {
2247              // solve: 0 = base_s + u * vec_s
2248              u = -base_s / vec_s;
2249              s = 0.0;
2250              t = base_t + u * vec_t;
2251              clipped = true;
2252            }
2253            if t < 0.0 {
2254              // solve: 0 = base_t + u * vec_t
2255              u = -base_t / vec_t;
2256              s = base_s + u * vec_s;
2257              t = 0.0;
2258              clipped = true;
2259            }
2260            if s + t > 1.0 {
2261              // solve: for u such that s + t == 1
2262              u = (1.0 - base_s - base_t) / (vec_s + vec_t);
2263              s = base_s + u * vec_s;
2264              t = base_t + u * vec_t;
2265              if cfg!(debug_assertions) {
2266                approx::assert_relative_eq!(s + t, 1.0,
2267                  max_relative = f64::default_max_relative() * 2.0.powi (16),
2268                  epsilon = f64::default_epsilon() * 2.0.powi (16));
2269              }
2270              debug_assert!(s >= 0.0);
2271              debug_assert!(t >= 0.0);
2272              clipped = true;
2273            }
2274            if clipped {
2275              Some (([s, t], u))
2276            } else {
2277              None
2278            }
2279          };
2280          let (dsq_a, depth_vec_a) =
2281            if let Some (([s, t], u)) = clip_seg_endpoint (s0, t0) {
2282              let clipped   = segment.point_a() + *segment_vec * u;
2283              let proj      = barycentric_to_cartesian (s, t);
2284              let depth_vec = clipped - proj;
2285              (depth_vec.magnitude_squared(), depth_vec)
2286            } else {
2287              let depth_vec = segment.point_a() - proj_seg0;
2288              (depth_vec.magnitude_squared(), depth_vec)
2289            };
2290          let (dsq_b, depth_vec_b) =
2291            if let Some (([s, t], u)) = clip_seg_endpoint (s1, t1) {
2292              let clipped   = segment.point_a() + *segment_vec * u;
2293              let proj      = barycentric_to_cartesian (s, t);
2294              let depth_vec = clipped - proj;
2295              (depth_vec.magnitude_squared(), depth_vec)
2296            } else {
2297              let depth_vec = segment.point_b() - proj_seg1;
2298              (depth_vec.magnitude_squared(), depth_vec)
2299            };
2300          if dsq_a < dsq_b {
2301            (dsq_a, depth_vec_a)
2302          } else {
2303            (dsq_b, depth_vec_b)
2304          }
2305        };
2306        // now compute the distance to nearest edge
2307        let (d0, d1, d2) = triangle_distance_to_edges (*triangle, *s, *t);
2308        let edge_dist_sq = f64::min (d0 * d0, f64::min (d1 * d1, d2 * d2));
2309        // choose minimum between perpendicular depth and edge distance
2310        if seg_depth_sq <= edge_dist_sq {
2311          // perpendicular movement is shorter
2312          let unit_normal = triangle_normal_unit
2313            .unwrap_or_else (|| triangle_normal.normalize());
2314          if unit_normal.dot (seg_depth_vec) < 0.0 {
2315            normal = -unit_normal;
2316          } else {
2317            normal = unit_normal;
2318          }
2319          distance  = -seg_depth_sq.sqrt();
2320          half_axis = *normal * -distance * 0.5;
2321          midpoint  = nearest_a + half_axis;
2322        } else {
2323          // moving to edge is shorter
2324          let edge_dist = edge_dist_sq.sqrt();
2325          distance      = -edge_dist;
2326          // find which edge is nearest
2327          let edge0     = triangle.point_b() - triangle.point_a();
2328          let edge1     = triangle.point_c() - triangle.point_a();
2329          let edge2     = triangle.point_c() - triangle.point_b();
2330          let len2_e0   = edge0.magnitude_squared();
2331          let len2_e1   = edge1.magnitude_squared();
2332          let len2_e2   = edge2.magnitude_squared();
2333          let (nearest_edge_point, nearest_edge_perp) = if d0 <= d1 && d0 <= d2 {
2334            let t = (nearest_b - triangle.point_b()).dot (edge0) / len2_e0;
2335            (triangle.point_b() + edge0 * t, -triangle.perpendicular_ab())
2336          } else if d1 <= d2 {
2337            let t = (nearest_b - triangle.point_a()).dot (edge1) / len2_e1;
2338            (triangle.point_a() + edge1 * t, -triangle.perpendicular_ca())
2339          } else {
2340            let t = (nearest_b - triangle.point_a()).dot (edge2) / len2_e2;
2341            (triangle.point_a() + edge2 * t, -triangle.perpendicular_bc())
2342          };
2343          half_axis = (nearest_b - nearest_edge_point) * 0.5;
2344          midpoint  = nearest_a + half_axis;
2345          normal    = nearest_edge_perp.normalize();
2346        }
2347      }
2348      return Proximity { distance, half_axis, midpoint, normal }
2349    }
2350    let a_to_b    = nearest_b - nearest_a;
2351    let distance  = a_to_b.magnitude();
2352    let half_axis = a_to_b * 0.5;
2353    let midpoint  = nearest_a + half_axis;
2354    let normal    = -a_to_b.normalize();
2355    Proximity { distance, half_axis, midpoint, normal }
2356  }
2357
2358  pub fn query_triangle_point (
2359    triangle : &geometry::Triangle3 <f64>,
2360    point    : &Point3 <f64>
2361  ) -> Self {
2362    let ([s, t], nearest_a) = triangle.nearest_point (*point);
2363    let a_to_b    = point - nearest_a;
2364    let distance  = a_to_b.magnitude();
2365    let half_axis = a_to_b * 0.5;
2366    let midpoint  = nearest_a + half_axis;
2367    let normal    = if *point == nearest_a {
2368      // TODO: for efficiency should we just return the triangle normal in all cases ?
2369      let perp = if *t == 0.0 {
2370        // on edge ab
2371        -triangle.perpendicular_ab()
2372      } else if *s == 0.0 {
2373        // on edge ac
2374        -triangle.perpendicular_ca()
2375      } else if *s + *t == 1.0 {
2376        // on edge bc
2377        -triangle.perpendicular_bc()
2378      } else {
2379        // use the triangle normal
2380        triangle.normal()
2381      };
2382      *perp
2383    } else {
2384      -a_to_b
2385    }.normalize();
2386    Proximity { distance, half_axis, midpoint, normal }
2387  }
2388
2389  pub fn query_line_line (
2390    line_a : &geometry::Segment3 <f64>,
2391    line_b : &geometry::Segment3 <f64>
2392  ) -> Self {
2393    use approx::{AbsDiffEq, RelativeEq};
2394    use num::Zero;
2395    let ((_, nearest_a), (_, nearest_b)) =
2396      geometry::distance::nearest_line3_line3 (*line_a, *line_b);
2397    let a_to_b    = nearest_b - nearest_a;
2398    let distance  = a_to_b.magnitude();
2399    let half_axis = a_to_b * 0.5;
2400    let midpoint  = nearest_a + half_axis;
2401    let normal    = if approx::relative_eq!(nearest_a, nearest_b,
2402      max_relative = f64::default_max_relative() * 256.0,
2403      epsilon      = f64::default_epsilon() * 256.0)
2404    {
2405      // intersection
2406      let line_a_dir = line_a.vector();
2407      let line_b_dir = line_b.vector();
2408      let mut perp   = line_a_dir.cross (*line_b_dir);
2409      if perp.is_zero() {
2410        perp = line_a_dir.orthogonal();
2411      }
2412      perp
2413    } else {
2414      -a_to_b
2415    }.normalize();
2416    Proximity { distance, half_axis, midpoint, normal }
2417  }
2418
2419  pub fn query_line_segment (
2420    line    : &geometry::Segment3 <f64>,
2421    segment : &geometry::Segment3 <f64>
2422  ) -> Self {
2423    use approx::{AbsDiffEq, RelativeEq};
2424    let seg_dir   = segment.vector();
2425    let line_dir  = line.vector();
2426    let ((_, nearest_a), (s1, nearest_b)) =
2427      geometry::distance::nearest_line3_segment3 (*line, *segment);
2428    let a_to_b    = nearest_b - nearest_a;
2429    let distance  = a_to_b.magnitude();
2430    let half_axis = a_to_b * 0.5;
2431    let midpoint  = nearest_a + half_axis;
2432    let normal    = if approx::relative_eq!(nearest_a, nearest_b,
2433      max_relative = f64::default_max_relative() * 256.0,
2434      epsilon      = f64::default_epsilon() * 256.0)
2435    {
2436      // intersection
2437      if 0.0 < *s1 && *s1 < 1.0 {
2438        // intersection internal to segment
2439        let mut perp = seg_dir.cross (*line_dir);
2440        if perp.is_approx_zero() {
2441          // line and segment are parallel
2442          perp = seg_dir.orthogonal();
2443        }
2444        debug_assert!(!perp.is_approx_zero());
2445        perp
2446      } else {
2447        // when an endpoint is touching, choose the normal to point away from the
2448        // segment
2449        debug_assert!(*s1 == 0.0 || *s1 == 1.0);
2450        let mut perp = seg_dir.cross (*line_dir).cross (*line_dir);
2451        if perp.is_approx_zero() {
2452          // parallel
2453          perp = seg_dir.orthogonal()
2454        } else if *s1 == 1.0 {
2455          perp = -perp
2456        }
2457        debug_assert!((nearest_a - segment.point_a()).dot (perp) >= 0.0);
2458        debug_assert!((nearest_a - segment.point_b()).dot (perp) >= 0.0);
2459        perp
2460      }
2461    } else {
2462      -a_to_b
2463    }.normalize();
2464    Proximity { distance, half_axis, midpoint, normal }
2465  }
2466
2467  pub fn query_line_point (
2468    line  : &geometry::Segment3 <f64>,
2469    point : &Point3 <f64>
2470  ) -> Self {
2471    let (_, nearest_a) = line.nearest_point (*point);
2472    let nearest_b      = *point;
2473    let a_to_b         = nearest_b - nearest_a;
2474    let distance       = a_to_b.magnitude();
2475    let half_axis      = a_to_b * 0.5;
2476    let midpoint       = nearest_a + half_axis;
2477    let normal         = if approx::relative_eq!(nearest_a, nearest_b) {
2478      *line.perpendicular()
2479    } else {
2480      -a_to_b
2481    }.normalize();
2482    Proximity { distance, half_axis, midpoint, normal }
2483  }
2484
2485  pub fn query_segment_segment (
2486    segment_a : &geometry::Segment3 <f64>,
2487    segment_b : &geometry::Segment3 <f64>
2488  ) -> Self {
2489    use approx::{AbsDiffEq, RelativeEq};
2490    // method from Eberly GeometricTools:
2491    // <https://github.com/davideberly/GeometricTools/blob/a9a2b149485857273ea1f1cfa5416b392f495882/GTE/Mathematics/DistSegmentSegment.h>
2492    let vec_a = segment_a.vector();
2493    let vec_b = segment_b.vector();
2494    let vec_b0a0 = segment_a.point_a() - segment_b.point_a();
2495    let a = Positive::unchecked (*vec_a.self_dot());
2496    let b = vec_a.dot (*vec_b);
2497    let c = Positive::unchecked (*vec_b.self_dot());
2498    let d = vec_a.dot (vec_b0a0);
2499    let e = vec_b.dot (vec_b0a0);
2500    let f00 = d;
2501    let f10 = f00 + *a;
2502    let f01 = f00 - b;
2503    let f11 = f10 - b;
2504    let g00 = -e;
2505    let g10 = g00 - b;
2506    let g01 = g00 + *c;
2507    let g11 = g10 + *c;
2508    let svalue = [
2509      clamped_root (*a, f00, f10),
2510      clamped_root (*a, f01, f11)
2511    ];
2512    let mut classify = [i8::MAX; 2];
2513    for i in 0..2 {
2514      if svalue[i] <= 0.0 {
2515        classify[i] = -1
2516      } else if svalue[i] >= 1.0 {
2517        classify[i] = 1
2518      } else {
2519        classify[i] = 0
2520      }
2521    }
2522    let param_a;
2523    let param_b;
2524    if classify[0] == -1 && classify[1] == -1 {
2525      param_a = 0.0;
2526      param_b = clamped_root (*c, g00, g01);
2527    } else if classify[0] == 1 && classify[1] == 1 {
2528      param_a = 1.0;
2529      param_b = clamped_root (*c, g10, g11);
2530    } else {
2531      let mut edge = [u8::MAX; 2];
2532      let mut end  = [[f64::NAN; 2]; 2];
2533      if classify[0] == -1 {
2534        edge[0] = 0;
2535        end[0][0] = 0.0;
2536        end[0][1] = f00 / b;
2537        if end[0][1] < 0.0 || end[0][1] > 1.0 {
2538          end[0][1] = 0.5
2539        }
2540        if classify[1] == 0 {
2541          edge[1] = 3;
2542          end[1][0] = svalue[1];
2543          end[1][1] = 1.0;
2544        } else {
2545          debug_assert_eq!(classify[1], 1);
2546          edge[1] = 1;
2547          end[1][0] = 1.0;
2548          end[1][1] = f10 / b;
2549          if end[1][1] < 0.0 || end[1][1] > 1.0 {
2550            end[1][1] = 0.5
2551          }
2552        }
2553      } else if classify[0] == 0 {
2554        edge[0] = 2;
2555        end[0][0] = svalue[0];
2556        end[0][1] = 0.0;
2557        if classify[1] == -1 {
2558          edge[1] = 0;
2559          end[1][0] = 0.0;
2560          end[1][1] = f00 / b;
2561          if end[1][1] < 0.0 || end[1][1] > 1.0 {
2562            end[1][1] = 0.5
2563          }
2564        } else if classify[1] == 0 {
2565          edge[1] = 3;
2566          end[1][0] = svalue[1];
2567          end[1][1] = 1.0;
2568        } else {
2569          debug_assert_eq!(classify[1], 1);
2570          edge[1] = 1;
2571          end[1][0] = 1.0;
2572          end[1][1] = f10 / b;
2573          if end[1][1] < 0.0 || end[1][1] > 1.0 {
2574            end[1][1] = 0.5
2575          }
2576        }
2577      } else {
2578        debug_assert_eq!(classify[0], 1);
2579        edge[0] = 1;
2580        end[0][0] = 1.0;
2581        end[0][1] = f10 / b;
2582        if end[0][1] < 0.0 || end[0][1] > 1.0 {
2583          end[0][1] = 0.5
2584        }
2585        if classify[1] == 0 {
2586          edge[1] = 3;
2587          end[1][0] = svalue[1];
2588          end[1][1] = 1.0;
2589        } else {
2590          edge[1] = 0;
2591          end[1][0] = 0.0;
2592          end[1][1] = f00 / b;
2593          if end[1][1] < 0.0 || end[1][1] > 1.0 {
2594            end[1][1] = 0.5
2595          }
2596        }
2597      }
2598      // compute minimum parameters
2599      let delta = end[1][1] - end[0][1];
2600      //let h0 = delta * (-b * end[0][0] + c * end[0][1] - e);
2601      let h0 = delta * ((-b).mul_add (end[0][0], c.mul_add (end[0][1], -e)));
2602      if h0 >= 0.0 {
2603        param_a = (end[0][0] + end[1][0]) / 2.0;
2604        param_b = (end[0][1] + end[1][1]) / 2.0;
2605      } else {
2606        //let h1 = delta * (-b * end[1][0] + c * end[1][1] - e);
2607        let h1 = delta * ((-b).mul_add (end[1][0], c.mul_add (end[1][1], -e)));
2608        if h1 <= 0.0 {
2609          if edge[1] == 0 {
2610            param_a = 0.0;
2611            param_b = clamped_root (*c, g00, g01);
2612          } else if edge[1] == 1 {
2613            param_a = 1.0;
2614            param_b = clamped_root (*c, g10, g11);
2615          } else {
2616            param_a = end[1][0];
2617            param_b = end[1][1];
2618          }
2619        } else {
2620          debug_assert!(h0 < 0.0);
2621          debug_assert!(h1 > 0.0);
2622          let z = (h0 / (h0 - h1)).clamp (0.0, 1.0);
2623          let omz = 1.0 - z;
2624          param_a = omz * end[0][0] + z * end[1][0];
2625          param_b = omz * end[0][1] + z * end[1][1];
2626        }
2627      }
2628      debug_assert_ne!(edge[0], u8::MAX);
2629      debug_assert_ne!(edge[1], u8::MAX);
2630      debug_assert_ne!(end[0][0], f64::NAN);
2631      debug_assert_ne!(end[0][1], f64::NAN);
2632      debug_assert_ne!(end[1][0], f64::NAN);
2633      debug_assert_ne!(end[1][1], f64::NAN);
2634    }
2635    let nearest_a = Point3::from (segment_a.point_a().0 * (1.0 - param_a)
2636      + segment_a.point_b().0 * param_a);
2637    let nearest_b = Point3::from (segment_b.point_a().0 * (1.0 - param_b)
2638      + segment_b.point_b().0 * param_b);
2639    let a_to_b    = nearest_b - nearest_a;
2640    let distance  = a_to_b.magnitude();
2641    let half_axis = a_to_b * 0.5;
2642    let midpoint  = nearest_a + half_axis;
2643    let normal    = if approx::relative_eq!(nearest_a, nearest_b,
2644      max_relative = f64::default_max_relative() * 2.0.powi (20),
2645      epsilon      = f64::default_epsilon() * 2.0.powi (20))
2646    {
2647      // generate a normal vector perpendicular to the edges
2648      let mut perp = vec_a.cross (*vec_b);
2649      if perp == Vector3::zero() {
2650        // edges are parallel
2651        perp = vec_a.cross (Vector3::unit_z());
2652        if perp == Vector3::zero() {
2653          // edges are parallel to the Z axis, use the X axis instead
2654          perp = vec_a.cross (Vector3::unit_x());
2655        }
2656      }
2657      Unit3::normalize (perp)
2658    } else {
2659      Unit3::normalize (-a_to_b)
2660    };
2661    Proximity { distance, half_axis, midpoint, normal }
2662  }
2663
2664  pub fn query_segment_point (
2665    segment : &geometry::Segment3 <f64>,
2666    point   : &Point3 <f64>
2667  ) -> Self {
2668    let (s, nearest_a) = segment.nearest_point (*point);
2669    let nearest_b      = *point;
2670    let a_to_b         = nearest_b - nearest_a;
2671    let distance       = a_to_b.magnitude();
2672    let half_axis      = a_to_b * 0.5;
2673    let midpoint       = nearest_a + half_axis;
2674    let normal         = if approx::relative_eq!(nearest_a, nearest_b) {
2675      let perp = if *s == 0.0 {
2676        debug_assert_eq!(nearest_a, segment.point_a());
2677        -segment.vector()
2678      } else if *s == 1.0 {
2679        segment.vector()
2680      } else {
2681        segment.perpendicular()
2682      };
2683      *perp
2684    } else {
2685      -a_to_b
2686    }.normalize();
2687    Proximity { distance, half_axis, midpoint, normal }
2688  }
2689}
2690
2691/// Given a point on a triangle defined as s(b - a) + t(c - a), compute the distance to
2692/// each edge of the triangle
2693fn triangle_distance_to_edges (triangle : geometry::Triangle3 <f64>, s : f64, t : f64)
2694  -> (f64, f64, f64)
2695{
2696  use approx::{AbsDiffEq, RelativeEq};
2697  debug_assert!(s + t <= 1.0);
2698  // barycentric coordinates of each vertex
2699  let b0        = 1.0 - s - t;
2700  let b1        = s;
2701  let b2        = t;
2702  // edges opposite to each vertex
2703  let edge0     = triangle.point_c() - triangle.point_b();
2704  let edge1     = triangle.point_c() - triangle.point_a();
2705  let edge2     = triangle.point_b() - triangle.point_a();
2706  let len_e0_sq = edge0.magnitude_squared();
2707  let len_e1_sq = edge1.magnitude_squared();
2708  let len_e2_sq = edge2.magnitude_squared();
2709  // length of triangle normal is 2 * area
2710  let area_2_sq = triangle.normal().magnitude_squared();
2711  // convert barycentric to trilinear coordinates
2712  let d0        = b0 * (area_2_sq / len_e0_sq).sqrt();
2713  let d1        = b1 * (area_2_sq / len_e1_sq).sqrt();
2714  let d2        = b2 * (area_2_sq / len_e2_sq).sqrt();
2715  approx::assert_relative_eq!(
2716    triangle.barycentric ([b0, b1, b2].into()),
2717    triangle.trilinear ([d0, d1, d2].into()),
2718    max_relative = f64::default_max_relative() * 32.0,
2719    epsilon = f64::default_epsilon() * 32.0);
2720  (d0, d1, d2)
2721}
2722
2723fn clamped_root (slope : f64, h0 : f64, h1 : f64) -> f64 {
2724  // helper function for Eberly GeometricTools methods
2725  if h0 < 0.0 {
2726    if h1 > 0.0 {
2727      let mut root = -h0 / slope;
2728      if root > 1.0 {
2729        root = 0.5
2730      }
2731      root
2732    } else {
2733      1.0
2734    }
2735  } else {
2736    0.0
2737  }
2738}
2739
2740#[cfg(test)]
2741mod tests {
2742  use approx::AbsDiffEq;
2743  use rand;
2744  use rand_distr;
2745  use rand_xorshift::XorShiftRng;
2746  use std::f64::consts::*;
2747  use super::*;
2748
2749  #[test]
2750  fn segment_point() {
2751    use geometry::Primitive;
2752    use rand::SeedableRng;
2753    use rand_distr::Distribution;
2754    // point above segment
2755    let segment = geometry::Segment3::noisy (
2756      [-1.0, 0.0, 0.0].into(),
2757      [ 1.0, 0.0, 0.0].into());
2758    let point = point3 (0.0, 1.0, 0.0);
2759    let proximity = Proximity::query_segment_point (&segment, &point);
2760    assert_eq!(proximity.nearest_a(), [0.0, 0.0, 0.0].into());
2761    assert_eq!(proximity.nearest_b(), [0.0, 1.0, 0.0].into());
2762    assert_eq!(proximity.distance, 1.0);
2763    assert_eq!(proximity.half_axis, [0.0, 0.5, 0.0].into());
2764    assert_eq!(proximity.midpoint, [0.0, 0.5, 0.0].into());
2765    assert_eq!(proximity.normal, -Unit3::axis_y());
2766    // point on segment: middle
2767    let segment = geometry::Segment3::noisy (
2768      [-1.0, 0.0, 0.0].into(),
2769      [ 1.0, 0.0, 0.0].into());
2770    let point = point3 (0.0, 0.0, 0.0);
2771    let proximity = Proximity::query_segment_point (&segment, &point);
2772    assert_eq!(proximity.nearest_a(), [0.0, 0.0, 0.0].into());
2773    assert_eq!(proximity.nearest_b(), [0.0, 0.0, 0.0].into());
2774    assert_eq!(proximity.distance, 0.0);
2775    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
2776    assert_eq!(proximity.midpoint, [0.0, 0.0, 0.0].into());
2777    assert_eq!(proximity.normal, Unit3::axis_z());
2778    // point on segment: end
2779    let segment = geometry::Segment3::noisy (
2780      [-1.0, 0.0, 0.0].into(),
2781      [ 1.0, 0.0, 0.0].into());
2782    let point = point3 (1.0, 0.0, 0.0);
2783    let proximity = Proximity::query_segment_point (&segment, &point);
2784    assert_eq!(proximity.nearest_a(), [1.0, 0.0, 0.0].into());
2785    assert_eq!(proximity.nearest_b(), [1.0, 0.0, 0.0].into());
2786    assert_eq!(proximity.distance, 0.0);
2787    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
2788    assert_eq!(proximity.midpoint, [1.0, 0.0, 0.0].into());
2789    assert_eq!(proximity.normal, segment.vector().normalize());
2790    // test that normal is invariant under translation
2791    let segment = geometry::Segment3::noisy (
2792      [-1.0, 0.0, 0.0].into(),
2793      [ 1.0, 0.0, 0.0].into());
2794    let point = point3 (0.0, 0.0, 0.0);
2795    let mut rng = XorShiftRng::seed_from_u64 (0);
2796    let std_normal = rand_distr::StandardNormal;
2797    // cauchy sample
2798    let randf = |rng : &mut XorShiftRng| {
2799      let x : f64 = std_normal.sample (rng);
2800      let y : f64 = std_normal.sample (rng);
2801      x / y
2802    };
2803    for _ in 0..10000 {
2804      let translation = vector3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
2805      let mut segment = segment;
2806      segment.translate (translation);
2807      let point = point + translation;
2808      let proximity = Proximity::query_segment_point (&segment, &point);
2809      assert_eq!(proximity.normal, Unit3::axis_z());
2810    }
2811  }
2812
2813  #[test]
2814  fn segment_segment() {
2815    use approx::RelativeEq;
2816    use geometry::Primitive;
2817    use rand::SeedableRng;
2818    use rand_distr::Distribution;
2819    // parallel
2820    let segment1 = geometry::Segment3::noisy (
2821      [-2.0, 2.0, 0.0].into(),
2822      [ 2.0, 2.0, 0.0].into());
2823    let segment2 = geometry::Segment3::noisy (
2824      [-1.0, 0.0, 0.0].into(),
2825      [ 0.0, 0.0, 0.0].into());
2826    let proximity = Proximity::query_segment_segment (&segment1, &segment2);
2827    assert_eq!(proximity.nearest_a(), [-0.5, 2.0, 0.0].into());
2828    assert_eq!(proximity.nearest_b(), [-0.5, 0.0, 0.0].into());
2829    assert_eq!(proximity.distance, 2.0);
2830    assert_eq!(proximity.half_axis, [0.0, -1.0, 0.0].into());
2831    assert_eq!(proximity.midpoint, [-0.5, 1.0, 0.0].into());
2832    assert_eq!(proximity.normal, Unit3::noisy ([0.0, 1.0, 0.0].into()));
2833    let segment1 = geometry::Segment3::noisy (
2834      [-2.0, 2.0, 0.0].into(),
2835      [ 2.0, 2.0, 0.0].into());
2836    let segment2 = geometry::Segment3::noisy (
2837      [ 0.0, 0.0, 0.0].into(),
2838      [-3.0, 0.0, 0.0].into());
2839    let proximity = Proximity::query_segment_segment (&segment1, &segment2);
2840    assert_eq!(proximity.nearest_a(), [-1.0, 2.0, 0.0].into());
2841    assert_eq!(proximity.nearest_b(), [-1.0, 0.0, 0.0].into());
2842    assert_eq!(proximity.distance, 2.0);
2843    assert_eq!(proximity.half_axis, [0.0, -1.0, 0.0].into());
2844    assert_eq!(proximity.midpoint, [-1.0, 1.0, 0.0].into());
2845    assert_eq!(proximity.normal, Unit3::noisy ([0.0, 1.0, 0.0].into()));
2846    let segment1 = geometry::Segment3::noisy (
2847      [-2.0, 2.0, 0.0].into(),
2848      [ 2.0, 2.0, 0.0].into());
2849    let segment2 = geometry::Segment3::noisy (
2850      [ 0.0, 0.0, 0.0].into(),
2851      [ 3.0, 0.0, 0.0].into());
2852    let proximity = Proximity::query_segment_segment (&segment1, &segment2);
2853    assert_eq!(proximity.nearest_a(), [ 1.0, 2.0, 0.0].into());
2854    assert_eq!(proximity.nearest_b(), [ 1.0, 0.0, 0.0].into());
2855    assert_eq!(proximity.distance, 2.0);
2856    assert_eq!(proximity.half_axis, [0.0, -1.0, 0.0].into());
2857    assert_eq!(proximity.midpoint, [ 1.0, 1.0, 0.0].into());
2858    assert_eq!(proximity.normal, Unit3::noisy ([0.0, 1.0, 0.0].into()));
2859    let segment1 = geometry::Segment3::noisy (
2860      [-2.0, 2.0, 0.0].into(),
2861      [ 2.0, 2.0, 0.0].into());
2862    let segment2 = geometry::Segment3::noisy (
2863      [ 3.0, 0.0, 0.0].into(),
2864      [ 0.0, 0.0, 0.0].into());
2865    let proximity = Proximity::query_segment_segment (&segment1, &segment2);
2866    assert_eq!(proximity.nearest_a(), [ 1.0, 2.0, 0.0].into());
2867    assert_eq!(proximity.nearest_b(), [ 1.0, 0.0, 0.0].into());
2868    assert_eq!(proximity.distance, 2.0);
2869    assert_eq!(proximity.half_axis, [0.0, -1.0, 0.0].into());
2870    assert_eq!(proximity.midpoint, [ 1.0, 1.0, 0.0].into());
2871    assert_eq!(proximity.normal, Unit3::noisy ([0.0, 1.0, 0.0].into()));
2872    let segment1 = geometry::Segment3::noisy (
2873      [-2.0, 2.0, 0.0].into(),
2874      [ 2.0, 2.0, 0.0].into());
2875    let segment2 = geometry::Segment3::noisy (
2876      [-3.0, 0.0, 0.0].into(),
2877      [ 0.0, 0.0, 0.0].into());
2878    let proximity = Proximity::query_segment_segment (&segment1, &segment2);
2879    assert_eq!(proximity.nearest_a(), [0.0, 2.0, 0.0].into());
2880    assert_eq!(proximity.nearest_b(), [0.0, 0.0, 0.0].into());
2881    assert_eq!(proximity.distance, 2.0);
2882    assert_eq!(proximity.half_axis, [0.0, -1.0, 0.0].into());
2883    assert_eq!(proximity.midpoint, [0.0, 1.0, 0.0].into());
2884    assert_eq!(proximity.normal, Unit3::noisy ([0.0, 1.0, 0.0].into()));
2885    let segment1 = geometry::Segment3::noisy (
2886      [-2.0, 2.0, 0.0].into(),
2887      [ 2.0, 2.0, 0.0].into());
2888    let segment2 = geometry::Segment3::noisy (
2889      [ 1.0, -1.0, 0.0].into(),
2890      [ 1.0,  1.0, 0.0].into());
2891    let proximity = Proximity::query_segment_segment (&segment1, &segment2);
2892    assert_eq!(proximity.nearest_a(), [ 1.0, 2.0, 0.0].into());
2893    assert_eq!(proximity.nearest_b(), [ 1.0, 1.0, 0.0].into());
2894    assert_eq!(proximity.distance, 1.0);
2895    assert_eq!(proximity.half_axis, [0.0, -0.5, 0.0].into());
2896    assert_eq!(proximity.midpoint, [ 1.0, 1.5, 0.0].into());
2897    assert_eq!(proximity.normal, Unit3::noisy ([0.0, 1.0, 0.0].into()));
2898    let segment1 = geometry::Segment3::noisy (
2899      [-2.0, 2.0, 0.0].into(),
2900      [ 2.0, 2.0, 0.0].into());
2901    let segment2 = geometry::Segment3::noisy (
2902      [-3.0, 0.0, 0.0].into(),
2903      [-3.0, 3.0, 0.0].into());
2904    let proximity = Proximity::query_segment_segment (&segment1, &segment2);
2905    assert_eq!(proximity.nearest_a(), [-2.0, 2.0, 0.0].into());
2906    assert_eq!(proximity.nearest_b(), [-3.0, 2.0, 0.0].into());
2907    assert_eq!(proximity.distance, 1.0);
2908    assert_eq!(proximity.half_axis, [-0.5, 0.0, 0.0].into());
2909    assert_eq!(proximity.midpoint, [-2.5, 2.0, 0.0].into());
2910    assert_eq!(proximity.normal, Unit3::noisy ([1.0, 0.0, 0.0].into()));
2911    let segment1 = geometry::Segment3::noisy (
2912      [-2.0, 2.0, 0.0].into(),
2913      [ 2.0, 2.0, 0.0].into());
2914    let segment2 = geometry::Segment3::noisy (
2915      [ 3.0, 0.0, 0.0].into(),
2916      [ 3.0, 3.0, 0.0].into());
2917    let proximity = Proximity::query_segment_segment (&segment1, &segment2);
2918    assert_eq!(proximity.nearest_a(), [2.0, 2.0, 0.0].into());
2919    assert_eq!(proximity.nearest_b(), [3.0, 2.0, 0.0].into());
2920    assert_eq!(proximity.distance, 1.0);
2921    assert_eq!(proximity.half_axis, [0.5, 0.0, 0.0].into());
2922    assert_eq!(proximity.midpoint, [2.5, 2.0, 0.0].into());
2923    assert_eq!(proximity.normal, Unit3::noisy ([-1.0, 0.0, 0.0].into()));
2924    // normal should be invariant under translation
2925    let segment1 = geometry::Segment3::noisy (
2926      [ 0.0,  1.0, 0.0].into(),
2927      [ 0.0, -1.0, 0.0].into());
2928    let segment2 = geometry::Segment3::noisy (
2929      [ -0.5, -0.5, -0.5].into(),
2930      [  0.5,  0.5,  0.5].into());
2931    let proximity1 = Proximity::query_segment_segment (&segment1, &segment2);
2932    let mut rng = XorShiftRng::seed_from_u64 (0);
2933    let std_normal = rand_distr::StandardNormal;
2934    // cauchy sample
2935    let randf = |rng : &mut XorShiftRng| {
2936      let x : f64 = std_normal.sample (rng);
2937      let y : f64 = std_normal.sample (rng);
2938      x / y
2939    };
2940    for _ in 0..10000 {
2941      let translation = vector3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
2942      let mut segment1 = segment1;
2943      let mut segment2 = segment2;
2944      segment1.translate (translation);
2945      segment2.translate (translation);
2946      let proximity = Proximity::query_segment_segment (&segment1, &segment2);
2947      approx::assert_relative_eq!(*proximity.normal, *proximity1.normal,
2948        max_relative = f64::default_max_relative() * 2.0.powi (10))
2949    }
2950  }
2951
2952  #[test]
2953  fn line_point() {
2954    use geometry::Primitive;
2955    use rand::SeedableRng;
2956    use rand_distr::Distribution;
2957    // point above line
2958    let line = geometry::Segment3::noisy (
2959      [-1.0, 0.0, 0.0].into(),
2960      [ 1.0, 0.0, 0.0].into());
2961    let point = point3 (0.0, 1.0, 0.0);
2962    let proximity = Proximity::query_line_point (&line, &point);
2963    assert_eq!(proximity.nearest_a(), [0.0, 0.0, 0.0].into());
2964    assert_eq!(proximity.nearest_b(), [0.0, 1.0, 0.0].into());
2965    assert_eq!(proximity.distance, 1.0);
2966    assert_eq!(proximity.half_axis, [0.0, 0.5, 0.0].into());
2967    assert_eq!(proximity.midpoint, [0.0, 0.5, 0.0].into());
2968    assert_eq!(proximity.normal, -Unit3::axis_y());
2969    // point on line: middle
2970    let line = geometry::Segment3::noisy (
2971      [-1.0, 0.0, 0.0].into(),
2972      [ 1.0, 0.0, 0.0].into());
2973    let point = point3 (0.0, 0.0, 0.0);
2974    let proximity = Proximity::query_line_point (&line, &point);
2975    assert_eq!(proximity.nearest_a(), [0.0, 0.0, 0.0].into());
2976    assert_eq!(proximity.nearest_b(), [0.0, 0.0, 0.0].into());
2977    assert_eq!(proximity.distance, 0.0);
2978    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
2979    assert_eq!(proximity.midpoint, [0.0, 0.0, 0.0].into());
2980    assert_eq!(proximity.normal, Unit3::axis_z());
2981    // point on line: end
2982    let line = geometry::Segment3::noisy (
2983      [-1.0, 0.0, 0.0].into(),
2984      [ 1.0, 0.0, 0.0].into());
2985    let point = point3 (1.0, 0.0, 0.0);
2986    let proximity = Proximity::query_line_point (&line, &point);
2987    assert_eq!(proximity.nearest_a(), [1.0, 0.0, 0.0].into());
2988    assert_eq!(proximity.nearest_b(), [1.0, 0.0, 0.0].into());
2989    assert_eq!(proximity.distance, 0.0);
2990    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
2991    assert_eq!(proximity.midpoint, [1.0, 0.0, 0.0].into());
2992    assert_eq!(proximity.normal, Unit3::axis_z());
2993    // test that normal is invariant under translation
2994    let line = geometry::Segment3::noisy (
2995      [-1.0, 0.0, 0.0].into(),
2996      [ 1.0, 0.0, 0.0].into());
2997    let point = point3 (0.0, 0.0, 0.0);
2998    let mut rng = XorShiftRng::seed_from_u64 (0);
2999    let std_normal = rand_distr::StandardNormal;
3000    // cauchy sample
3001    let randf = |rng : &mut XorShiftRng| {
3002      let x : f64 = std_normal.sample (rng);
3003      let y : f64 = std_normal.sample (rng);
3004      x / y
3005    };
3006    for _ in 0..10000 {
3007      let translation = vector3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3008      let mut line = line;
3009      line.translate (translation);
3010      let point = point + translation;
3011      let proximity = Proximity::query_line_point (&line, &point);
3012      assert_eq!(proximity.normal, Unit3::axis_z());
3013    }
3014  }
3015
3016  #[test]
3017  fn line_segment() {
3018    use approx::RelativeEq;
3019    use geometry::Primitive;
3020    use rand::SeedableRng;
3021    use rand_distr::Distribution;
3022    // parallel
3023    let line    = geometry::Segment3::noisy (
3024      [-2.0, 0.0, 0.0].into(),
3025      [ 2.0, 0.0, 0.0].into());
3026    let segment = geometry::Segment3::noisy (
3027      [-2.0, 2.0, 0.0].into(),
3028      [ 2.0, 2.0, 0.0].into());
3029    let proximity = Proximity::query_line_segment (&line, &segment);
3030    assert_eq!(proximity.nearest_a(), [-2.0, 0.0, 0.0].into());
3031    assert_eq!(proximity.nearest_b(), [-2.0, 2.0, 0.0].into());
3032    assert_eq!(proximity.distance, 2.0);
3033    assert_eq!(proximity.half_axis, [0.0, 1.0, 0.0].into());
3034    assert_eq!(proximity.midpoint, [-2.0, 1.0, 0.0].into());
3035    assert_eq!(proximity.normal, -Unit3::axis_y());
3036    let line    = geometry::Segment3::noisy (
3037      [-5.0, 0.0, 0.0].into(),
3038      [-2.0, 0.0, 0.0].into());
3039    let segment = geometry::Segment3::noisy (
3040      [-2.0, 2.0, 0.0].into(),
3041      [ 2.0, 2.0, 0.0].into());
3042    let proximity = Proximity::query_line_segment (&line, &segment);
3043    assert_eq!(proximity.nearest_a(), [-2.0, 0.0, 0.0].into());
3044    assert_eq!(proximity.nearest_b(), [-2.0, 2.0, 0.0].into());
3045    assert_eq!(proximity.distance, 2.0);
3046    assert_eq!(proximity.half_axis, [0.0, 1.0, 0.0].into());
3047    assert_eq!(proximity.midpoint, [-2.0, 1.0, 0.0].into());
3048    assert_eq!(proximity.normal, -Unit3::axis_y());
3049    // perpendicular
3050    let line    = geometry::Segment3::noisy (
3051      [0.0, -1.0, 0.0].into(),
3052      [0.0,  1.0, 0.0].into());
3053    let segment = geometry::Segment3::noisy (
3054      [1.0,  1.0, 0.0].into(),
3055      [3.0,  1.0, 0.0].into());
3056    let proximity = Proximity::query_line_segment (&line, &segment);
3057    assert_eq!(proximity.nearest_a(), [0.0, 1.0, 0.0].into());
3058    assert_eq!(proximity.nearest_b(), [1.0, 1.0, 0.0].into());
3059    assert_eq!(proximity.distance, 1.0);
3060    assert_eq!(proximity.half_axis, [0.5, 0.0, 0.0].into());
3061    assert_eq!(proximity.midpoint, [0.5, 1.0, 0.0].into());
3062    assert_eq!(proximity.normal, -Unit3::axis_x());
3063    let line    = geometry::Segment3::noisy (
3064      [ 0.0, -1.0, 0.0].into(),
3065      [ 0.0,  1.0, 0.0].into());
3066    let segment = geometry::Segment3::noisy (
3067      [-3.0,  1.0, 0.0].into(),
3068      [-1.0,  1.0, 0.0].into());
3069    let proximity = Proximity::query_line_segment (&line, &segment);
3070    assert_eq!(proximity.nearest_a(), [ 0.0, 1.0, 0.0].into());
3071    assert_eq!(proximity.nearest_b(), [-1.0, 1.0, 0.0].into());
3072    assert_eq!(proximity.distance, 1.0);
3073    assert_eq!(proximity.half_axis, [-0.5, 0.0, 0.0].into());
3074    assert_eq!(proximity.midpoint, [-0.5, 1.0, 0.0].into());
3075    assert_eq!(proximity.normal, Unit3::axis_x());
3076    // in front
3077    let line    = geometry::Segment3::noisy (
3078      [ 0.0, -1.0, 0.0].into(),
3079      [ 0.0,  1.0, 0.0].into());
3080    let segment = geometry::Segment3::noisy (
3081      [-2.0,  2.0, 1.0].into(),
3082      [ 2.0,  2.0, 1.0].into());
3083    let proximity = Proximity::query_line_segment (&line, &segment);
3084    assert_eq!(proximity.nearest_a(), [ 0.0, 2.0, 0.0].into());
3085    assert_eq!(proximity.nearest_b(), [ 0.0, 2.0, 1.0].into());
3086    assert_eq!(proximity.distance, 1.0);
3087    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.5].into());
3088    assert_eq!(proximity.midpoint, [0.0, 2.0, 0.5].into());
3089    assert_eq!(proximity.normal, -Unit3::axis_z());
3090    // intersection: middle
3091    let line    = geometry::Segment3::noisy (
3092      [ 0.0, -1.0, 0.0].into(),
3093      [ 0.0,  1.0, 0.0].into());
3094    let segment = geometry::Segment3::noisy (
3095      [-2.0,  2.0, 0.0].into(),
3096      [ 2.0,  2.0, 0.0].into());
3097    let proximity = Proximity::query_line_segment (&line, &segment);
3098    assert_eq!(proximity.nearest_a(), [ 0.0, 2.0, 0.0].into());
3099    assert_eq!(proximity.nearest_b(), [ 0.0, 2.0, 0.0].into());
3100    assert_eq!(proximity.distance, 0.0);
3101    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3102    assert_eq!(proximity.midpoint, [0.0, 2.0, 0.0].into());
3103    assert_eq!(proximity.normal, Unit3::axis_z());
3104    // intersection: endpoint A
3105    let line    = geometry::Segment3::noisy (
3106      [-2.0, -1.0, 0.0].into(),
3107      [-2.0,  1.0, 0.0].into());
3108    let segment = geometry::Segment3::noisy (
3109      [-2.0,  2.0, 0.0].into(),
3110      [ 2.0,  2.0, 0.0].into());
3111    let proximity = Proximity::query_line_segment (&line, &segment);
3112    assert_eq!(proximity.nearest_a(), [-2.0, 2.0, 0.0].into());
3113    assert_eq!(proximity.nearest_b(), [-2.0, 2.0, 0.0].into());
3114    assert_eq!(proximity.distance, 0.0);
3115    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3116    assert_eq!(proximity.midpoint, [-2.0, 2.0, 0.0].into());
3117    assert_eq!(proximity.normal, -Unit3::axis_x());
3118    let line    = geometry::Segment3::noisy (
3119      [-2.0, -1.0, 0.0].into(),
3120      [-2.0,  1.0, 0.0].into());
3121    let segment = geometry::Segment3::noisy (
3122      [-2.0,   2.0, 0.0].into(),
3123      [ 2.0,  -2.0, 0.0].into());
3124    let proximity = Proximity::query_line_segment (&line, &segment);
3125    assert_eq!(proximity.nearest_a(), [-2.0, 2.0, 0.0].into());
3126    assert_eq!(proximity.nearest_b(), [-2.0, 2.0, 0.0].into());
3127    assert_eq!(proximity.distance, 0.0);
3128    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3129    assert_eq!(proximity.midpoint, [-2.0, 2.0, 0.0].into());
3130    assert_eq!(proximity.normal, -Unit3::axis_x());
3131    // intersection: endpoint B
3132    let line    = geometry::Segment3::noisy (
3133      [ 2.0, -1.0, 0.0].into(),
3134      [ 2.0,  1.0, 0.0].into());
3135    let segment = geometry::Segment3::noisy (
3136      [-2.0,  2.0, 0.0].into(),
3137      [ 2.0,  2.0, 0.0].into());
3138    let proximity = Proximity::query_line_segment (&line, &segment);
3139    assert_eq!(proximity.nearest_a(), [2.0, 2.0, 0.0].into());
3140    assert_eq!(proximity.nearest_b(), [2.0, 2.0, 0.0].into());
3141    assert_eq!(proximity.distance, 0.0);
3142    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3143    assert_eq!(proximity.midpoint, [2.0, 2.0, 0.0].into());
3144    assert_eq!(proximity.normal, Unit3::axis_x());
3145    let line    = geometry::Segment3::noisy (
3146      [ 2.0, -1.0, 0.0].into(),
3147      [ 2.0,  1.0, 0.0].into());
3148    let segment = geometry::Segment3::noisy (
3149      [-2.0,  2.0, 0.0].into(),
3150      [ 2.0,  0.0, 0.0].into());
3151    let proximity = Proximity::query_line_segment (&line, &segment);
3152    assert_eq!(proximity.nearest_a(), [2.0, 0.0, 0.0].into());
3153    assert_eq!(proximity.nearest_b(), [2.0, 0.0, 0.0].into());
3154    assert_eq!(proximity.distance, 0.0);
3155    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3156    assert_eq!(proximity.midpoint, [2.0, 0.0, 0.0].into());
3157    assert_eq!(proximity.normal, Unit3::axis_x());
3158    // intersection: parallel
3159    let line    = geometry::Segment3::noisy (
3160      [-1.0, 2.0, 0.0].into(),
3161      [ 0.0, 2.0, 0.0].into());
3162    let segment = geometry::Segment3::noisy (
3163      [-2.0, 2.0, 0.0].into(),
3164      [ 2.0, 2.0, 0.0].into());
3165    let proximity = Proximity::query_line_segment (&line, &segment);
3166    assert_eq!(proximity.nearest_a(), [-2.0, 2.0, 0.0].into());
3167    assert_eq!(proximity.nearest_b(), [-2.0, 2.0, 0.0].into());
3168    assert_eq!(proximity.distance, 0.0);
3169    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3170    assert_eq!(proximity.midpoint, [-2.0, 2.0, 0.0].into());
3171    assert_eq!(proximity.normal, Unit3::axis_z());
3172    // normal should be invariant under translation
3173    let line = geometry::Segment3::noisy (
3174      [ -0.5, -0.5, -0.5].into(),
3175      [  0.5,  0.5,  0.5].into());
3176    let segment = geometry::Segment3::noisy (
3177      [ 0.0,  1.0, 0.0].into(),
3178      [ 0.0, -1.0, 0.0].into());
3179    let proximity1 = Proximity::query_line_segment (&line, &segment);
3180    let mut rng = XorShiftRng::seed_from_u64 (0);
3181    let std_normal = rand_distr::StandardNormal;
3182    // cauchy sample
3183    let randf = |rng : &mut XorShiftRng| {
3184      let x : f64 = std_normal.sample (rng);
3185      let y : f64 = std_normal.sample (rng);
3186      x / y
3187    };
3188    for _ in 0..10000 {
3189      let translation = vector3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3190      let mut line    = line;
3191      let mut segment = segment;
3192      line.translate (translation);
3193      segment.translate (translation);
3194      let proximity = Proximity::query_line_segment (&line, &segment);
3195      approx::assert_relative_eq!(*proximity.normal, *proximity1.normal,
3196        max_relative = f64::default_max_relative() * 2.0.powi (10))
3197    }
3198  }
3199
3200  #[test]
3201  fn line_line() {
3202    use approx::RelativeEq;
3203    use geometry::Primitive;
3204    use rand::SeedableRng;
3205    use rand_distr::Distribution;
3206    // parallel
3207    let line_a    = geometry::Segment3::noisy (
3208      [-2.0, 0.0, 0.0].into(),
3209      [ 2.0, 0.0, 0.0].into());
3210    let line_b = geometry::Segment3::noisy (
3211      [-2.0, 2.0, 0.0].into(),
3212      [ 2.0, 2.0, 0.0].into());
3213    let proximity = Proximity::query_line_line (&line_a, &line_b);
3214    assert_eq!(proximity.nearest_a(), [-2.0, 0.0, 0.0].into());
3215    assert_eq!(proximity.nearest_b(), [-2.0, 2.0, 0.0].into());
3216    assert_eq!(proximity.distance, 2.0);
3217    assert_eq!(proximity.half_axis, [0.0, 1.0, 0.0].into());
3218    assert_eq!(proximity.midpoint, [-2.0, 1.0, 0.0].into());
3219    assert_eq!(proximity.normal, -Unit3::axis_y());
3220    let line_a    = geometry::Segment3::noisy (
3221      [-5.0, 0.0, 0.0].into(),
3222      [-2.0, 0.0, 0.0].into());
3223    let line_b = geometry::Segment3::noisy (
3224      [-2.0, 2.0, 0.0].into(),
3225      [ 2.0, 2.0, 0.0].into());
3226    let proximity = Proximity::query_line_line (&line_a, &line_b);
3227    assert_eq!(proximity.nearest_a(), [-2.0, 0.0, 0.0].into());
3228    assert_eq!(proximity.nearest_b(), [-2.0, 2.0, 0.0].into());
3229    assert_eq!(proximity.distance, 2.0);
3230    assert_eq!(proximity.half_axis, [0.0, 1.0, 0.0].into());
3231    assert_eq!(proximity.midpoint, [-2.0, 1.0, 0.0].into());
3232    assert_eq!(proximity.normal, -Unit3::axis_y());
3233    // perpendicular
3234    let line_a    = geometry::Segment3::noisy (
3235      [0.0, -1.0, 0.0].into(),
3236      [0.0,  1.0, 0.0].into());
3237    let line_b = geometry::Segment3::noisy (
3238      [1.0,  1.0, 0.0].into(),
3239      [3.0,  1.0, 0.0].into());
3240    let proximity = Proximity::query_line_line (&line_a, &line_b);
3241    assert_eq!(proximity.nearest_a(), [0.0, 1.0, 0.0].into());
3242    assert_eq!(proximity.nearest_b(), [0.0, 1.0, 0.0].into());
3243    assert_eq!(proximity.distance, 0.0);
3244    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3245    assert_eq!(proximity.midpoint, [0.0, 1.0, 0.0].into());
3246    assert_eq!(proximity.normal, -Unit3::axis_z());
3247    let line_a    = geometry::Segment3::noisy (
3248      [ 0.0, -1.0, 0.0].into(),
3249      [ 0.0,  1.0, 0.0].into());
3250    let line_b = geometry::Segment3::noisy (
3251      [-3.0,  1.0, 0.0].into(),
3252      [-1.0,  1.0, 0.0].into());
3253    let proximity = Proximity::query_line_line (&line_a, &line_b);
3254    assert_eq!(proximity.nearest_a(), [0.0, 1.0, 0.0].into());
3255    assert_eq!(proximity.nearest_b(), [0.0, 1.0, 0.0].into());
3256    assert_eq!(proximity.distance, 0.0);
3257    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3258    assert_eq!(proximity.midpoint, [0.0, 1.0, 0.0].into());
3259    assert_eq!(proximity.normal, -Unit3::axis_z());
3260    // in front
3261    let line_a    = geometry::Segment3::noisy (
3262      [ 0.0, -1.0, 0.0].into(),
3263      [ 0.0,  1.0, 0.0].into());
3264    let line_b = geometry::Segment3::noisy (
3265      [-2.0,  2.0, 1.0].into(),
3266      [ 2.0,  2.0, 1.0].into());
3267    let proximity = Proximity::query_line_line (&line_a, &line_b);
3268    assert_eq!(proximity.nearest_a(), [ 0.0, 2.0, 0.0].into());
3269    assert_eq!(proximity.nearest_b(), [ 0.0, 2.0, 1.0].into());
3270    assert_eq!(proximity.distance, 1.0);
3271    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.5].into());
3272    assert_eq!(proximity.midpoint, [0.0, 2.0, 0.5].into());
3273    assert_eq!(proximity.normal, -Unit3::axis_z());
3274    // intersection: middle
3275    let line_a    = geometry::Segment3::noisy (
3276      [ 0.0, -1.0, 0.0].into(),
3277      [ 0.0,  1.0, 0.0].into());
3278    let line_b = geometry::Segment3::noisy (
3279      [-2.0,  2.0, 0.0].into(),
3280      [ 2.0,  2.0, 0.0].into());
3281    let proximity = Proximity::query_line_line (&line_a, &line_b);
3282    assert_eq!(proximity.nearest_a(), [ 0.0, 2.0, 0.0].into());
3283    assert_eq!(proximity.nearest_b(), [ 0.0, 2.0, 0.0].into());
3284    assert_eq!(proximity.distance, 0.0);
3285    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3286    assert_eq!(proximity.midpoint, [0.0, 2.0, 0.0].into());
3287    assert_eq!(proximity.normal, -Unit3::axis_z());
3288    // intersection: endpoint A
3289    let line_a    = geometry::Segment3::noisy (
3290      [-2.0, -1.0, 0.0].into(),
3291      [-2.0,  1.0, 0.0].into());
3292    let line_b = geometry::Segment3::noisy (
3293      [-2.0,  2.0, 0.0].into(),
3294      [ 2.0,  2.0, 0.0].into());
3295    let proximity = Proximity::query_line_line (&line_a, &line_b);
3296    assert_eq!(proximity.nearest_a(), [-2.0, 2.0, 0.0].into());
3297    assert_eq!(proximity.nearest_b(), [-2.0, 2.0, 0.0].into());
3298    assert_eq!(proximity.distance, 0.0);
3299    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3300    assert_eq!(proximity.midpoint, [-2.0, 2.0, 0.0].into());
3301    assert_eq!(proximity.normal, -Unit3::axis_z());
3302    let line_a    = geometry::Segment3::noisy (
3303      [-2.0, -1.0, 0.0].into(),
3304      [-2.0,  1.0, 0.0].into());
3305    let line_b = geometry::Segment3::noisy (
3306      [-2.0,   2.0, 0.0].into(),
3307      [ 2.0,  -2.0, 0.0].into());
3308    let proximity = Proximity::query_line_line (&line_a, &line_b);
3309    assert_eq!(proximity.nearest_a(), [-2.0, 2.0, 0.0].into());
3310    assert_eq!(proximity.nearest_b(), [-2.0, 2.0, 0.0].into());
3311    assert_eq!(proximity.distance, 0.0);
3312    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3313    assert_eq!(proximity.midpoint, [-2.0, 2.0, 0.0].into());
3314    assert_eq!(proximity.normal, -Unit3::axis_z());
3315    // intersection: endpoint B
3316    let line_a    = geometry::Segment3::noisy (
3317      [ 2.0, -1.0, 0.0].into(),
3318      [ 2.0,  1.0, 0.0].into());
3319    let line_b = geometry::Segment3::noisy (
3320      [-2.0,  2.0, 0.0].into(),
3321      [ 2.0,  2.0, 0.0].into());
3322    let proximity = Proximity::query_line_line (&line_a, &line_b);
3323    assert_eq!(proximity.nearest_a(), [2.0, 2.0, 0.0].into());
3324    assert_eq!(proximity.nearest_b(), [2.0, 2.0, 0.0].into());
3325    assert_eq!(proximity.distance, 0.0);
3326    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3327    assert_eq!(proximity.midpoint, [2.0, 2.0, 0.0].into());
3328    assert_eq!(proximity.normal, -Unit3::axis_z());
3329    let line_a    = geometry::Segment3::noisy (
3330      [ 2.0, -1.0, 0.0].into(),
3331      [ 2.0,  1.0, 0.0].into());
3332    let line_b = geometry::Segment3::noisy (
3333      [-2.0,  2.0, 0.0].into(),
3334      [ 2.0,  0.0, 0.0].into());
3335    let proximity = Proximity::query_line_line (&line_a, &line_b);
3336    assert_eq!(proximity.nearest_a(), [2.0, 0.0, 0.0].into());
3337    assert_eq!(proximity.nearest_b(), [2.0, 0.0, 0.0].into());
3338    assert_eq!(proximity.distance, 0.0);
3339    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3340    assert_eq!(proximity.midpoint, [2.0, 0.0, 0.0].into());
3341    assert_eq!(proximity.normal, -Unit3::axis_z());
3342    // intersection: parallel
3343    let line_a    = geometry::Segment3::noisy (
3344      [-1.0, 2.0, 0.0].into(),
3345      [ 0.0, 2.0, 0.0].into());
3346    let line_b = geometry::Segment3::noisy (
3347      [-2.0, 2.0, 0.0].into(),
3348      [ 2.0, 2.0, 0.0].into());
3349    let proximity = Proximity::query_line_line (&line_a, &line_b);
3350    assert_eq!(proximity.nearest_a(), [-2.0, 2.0, 0.0].into());
3351    assert_eq!(proximity.nearest_b(), [-2.0, 2.0, 0.0].into());
3352    assert_eq!(proximity.distance, 0.0);
3353    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3354    assert_eq!(proximity.midpoint, [-2.0, 2.0, 0.0].into());
3355    assert_eq!(proximity.normal, Unit3::axis_z());
3356    // normal should be invariant under translation
3357    let line_a = geometry::Segment3::noisy (
3358      [ -0.5, -0.5, -0.5].into(),
3359      [  0.5,  0.5,  0.5].into());
3360    let line_b = geometry::Segment3::noisy (
3361      [ 0.0,  1.0, 0.0].into(),
3362      [ 0.0, -1.0, 0.0].into());
3363    let proximity1 = Proximity::query_line_line (&line_a, &line_b);
3364    let mut rng = XorShiftRng::seed_from_u64 (0);
3365    let std_normal = rand_distr::StandardNormal;
3366    // cauchy sample
3367    let randf = |rng : &mut XorShiftRng| {
3368      let x : f64 = std_normal.sample (rng);
3369      let y : f64 = std_normal.sample (rng);
3370      x / y
3371    };
3372    for _ in 0..10000 {
3373      let translation = vector3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3374      let mut line_a = line_a;
3375      let mut line_b = line_b;
3376      line_a.translate (translation);
3377      line_b.translate (translation);
3378      let proximity = Proximity::query_line_line (&line_a, &line_b);
3379      approx::assert_relative_eq!(*proximity.normal, *proximity1.normal,
3380        max_relative = f64::default_max_relative() * 2.0.powi (10))
3381    }
3382  }
3383
3384  #[test]
3385  fn triangle_point() {
3386    use approx::RelativeEq;
3387    use geometry::Primitive;
3388    use rand::SeedableRng;
3389    use rand_distr::Distribution;
3390    let triangle = geometry::Triangle3::noisy (
3391      [-1.0, -1.0, 0.0].into(),
3392      [ 1.0, -1.0, 0.0].into(),
3393      [ 0.0,  1.0, 0.0].into());
3394    let point = point3 (0.0, 0.0, 0.0);
3395    let proximity = Proximity::query_triangle_point (&triangle, &point);
3396    assert_eq!(proximity.nearest_a(), [0.0, 0.0, 0.0].into());
3397    assert_eq!(proximity.nearest_b(), [0.0, 0.0, 0.0].into());
3398    assert_eq!(proximity.distance, 0.0);
3399    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3400    assert_eq!(proximity.midpoint, [0.0, 0.0, 0.0].into());
3401    assert_eq!(proximity.normal, Unit3::axis_z());
3402    let triangle = geometry::Triangle3::noisy (
3403      [-1.0, -1.0, 0.0].into(),
3404      [ 1.0, -1.0, 0.0].into(),
3405      [ 0.0,  1.0, 0.0].into());
3406    let point = point3 (0.5, 0.0, 0.0);
3407    let proximity = Proximity::query_triangle_point (&triangle, &point);
3408    assert_eq!(proximity.nearest_a(), [0.5, 0.0, 0.0].into());
3409    assert_eq!(proximity.nearest_b(), [0.5, 0.0, 0.0].into());
3410    assert_eq!(proximity.distance, 0.0);
3411    assert_eq!(proximity.half_axis, [0.0, 0.0, 0.0].into());
3412    assert_eq!(proximity.midpoint, [0.5, 0.0, 0.0].into());
3413    assert_eq!(proximity.normal, Unit3::normalize ([-2.0, -1.0, 0.0].into()));
3414    let triangle = geometry::Triangle3::noisy (
3415      [ 0.0, -2.0, 3.5].into(),
3416      [ 2.0,  2.0, 3.5].into(),
3417      [-2.0,  2.0, 3.5].into());
3418    let point = point3 (0.0, 0.0, 2.0);
3419    let proximity = Proximity::query_triangle_point (&triangle, &point);
3420    assert_eq!(proximity.distance, 1.5);
3421    assert_eq!(proximity.half_axis, [0.0, 0.0, -0.75].into());
3422    assert_eq!(proximity.midpoint, [0.0, 0.0, 2.75].into());
3423    assert_eq!(proximity.normal, Unit3::axis_z());
3424    assert_eq!(proximity.nearest_a(), [0.0, 0.0, 3.5].into());
3425    assert_eq!(proximity.nearest_b(), [0.0, 0.0, 2.0].into());
3426    // test that normal is invariant under translation
3427    let triangle = geometry::Triangle3::noisy (
3428      [-1.0, -1.0, 0.0].into(),
3429      [ 1.0, -1.0, 0.0].into(),
3430      [ 0.0,  1.0, 0.0].into());
3431    let point = point3 (1.0, 0.0, 0.0);
3432    let proximity1 = Proximity::query_triangle_point (&triangle, &point);
3433    let mut rng = XorShiftRng::seed_from_u64 (0);
3434    let std_normal = rand_distr::StandardNormal;
3435    // cauchy sample
3436    let randf = |rng : &mut XorShiftRng| {
3437      let x : f64 = std_normal.sample (rng);
3438      let y : f64 = std_normal.sample (rng);
3439      x / y
3440    };
3441    for _ in 0..10000 {
3442      let translation = vector3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3443      let mut triangle = triangle;
3444      triangle.translate (translation);
3445      let point = point + translation;
3446      let proximity = Proximity::query_triangle_point (&triangle, &point);
3447      approx::assert_relative_eq!(*proximity.normal, *proximity1.normal,
3448        max_relative = f64::default_max_relative() * 2.0.powi (12));
3449    }
3450  }
3451
3452  #[test]
3453  fn triangle_segment() {
3454    use approx::RelativeEq;
3455    use geometry::Primitive;
3456    use rand::SeedableRng;
3457    use rand_distr::Distribution;
3458    let mut triangle = geometry::Triangle3::noisy (
3459      [-1.0, -1.0, 0.0].into(),
3460      [ 1.0, -1.0, 0.0].into(),
3461      [ 0.0,  1.0, 0.0].into());
3462    let mut segment = geometry::Segment3::noisy (
3463      [-2.0, -2.0, -0.01].into(),
3464      [ 2.0,  2.0,  0.01].into());
3465    let proximity = Proximity::query_triangle_segment (&triangle, &segment);
3466    triangle.translate (proximity.half_axis);
3467    segment.translate (-proximity.half_axis);
3468    let proximity = Proximity::query_triangle_segment (&triangle, &segment);
3469    approx::assert_abs_diff_eq!(proximity.distance, 0.0,
3470      epsilon = f64::default_epsilon() * 2.0);
3471    let mut triangle = geometry::Triangle3::noisy (
3472      [-1.0, -1.0, 0.0].into(),
3473      [ 1.0, -1.0, 0.0].into(),
3474      [ 0.0,  1.0, 0.0].into());
3475    let mut segment = geometry::Segment3::noisy (
3476      [-2.0,  2.0, -10.0].into(),
3477      [ 2.0, -2.0,  10.0].into());
3478    let proximity = Proximity::query_triangle_segment (&triangle, &segment);
3479    triangle.translate (proximity.half_axis);
3480    segment.translate (-proximity.half_axis);
3481    let proximity = Proximity::query_triangle_segment (&triangle, &segment);
3482    approx::assert_abs_diff_eq!(proximity.distance, 0.0,
3483      epsilon = f64::default_epsilon() * 2.0);
3484    // normal should be invariant under translation
3485    let triangle = geometry::Triangle3::noisy (
3486      [-1.0, -1.0, 0.0].into(),
3487      [ 1.0, -1.0, 0.0].into(),
3488      [ 0.0,  1.0, 0.0].into());
3489    let segment = geometry::Segment3::noisy (
3490      [-1.5, 0.0, -2.0].into(),
3491      [ 2.0, 0.0,  2.0].into());
3492    let proximity1 = Proximity::query_triangle_segment (&triangle, &segment);
3493    println!("normal: {}", *proximity1.normal);
3494    let mut rng = XorShiftRng::seed_from_u64 (0);
3495    let std_normal = rand_distr::StandardNormal;
3496    // cauchy sample
3497    let randf = |rng : &mut XorShiftRng| {
3498      let x : f64 = std_normal.sample (rng);
3499      let y : f64 = std_normal.sample (rng);
3500      x / y
3501    };
3502    for _ in 0..10000 {
3503      let translation  = vector3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3504      let mut triangle = triangle;
3505      let mut segment  = segment;
3506      triangle.translate (translation);
3507      segment.translate (translation);
3508      let proximity = Proximity::query_triangle_segment (&triangle, &segment);
3509      approx::assert_relative_eq!(*proximity.normal, *proximity1.normal,
3510        max_relative = f64::default_max_relative() * 2.0.powi (10))
3511    }
3512  }
3513
3514  #[test]
3515  fn triangle_line() {
3516    use approx::RelativeEq;
3517    use geometry::Primitive;
3518    use rand::SeedableRng;
3519    use rand_distr::Distribution;
3520    let triangle = geometry::Triangle3::noisy (
3521      [-1.0, -1.0, 0.0].into(),
3522      [ 1.0, -1.0, 0.0].into(),
3523      [ 0.0,  1.0, 0.0].into()
3524    );
3525    let line = geometry::Segment3::noisy (
3526      [-2.0, 2.0, 0.0].into(),
3527      [ 2.0, 2.0, 0.0].into());
3528    let proximity = Proximity::query_triangle_line (&triangle, &line);
3529    assert_eq!(proximity.nearest_a(), [0.0, 1.0, 0.0].into());
3530    assert_eq!(proximity.nearest_b(), [0.0, 2.0, 0.0].into());
3531    assert_eq!(proximity.distance, 1.0);
3532    assert_eq!(proximity.half_axis, [0.0, 0.5, 0.0].into());
3533    assert_eq!(proximity.midpoint, [0.0, 1.5, 0.0].into());
3534    assert_eq!(proximity.normal, -Unit3::axis_y());
3535    // normal should be invariant under translation
3536    let triangle = geometry::Triangle3::noisy (
3537      [-1.0, -1.0, 0.0].into(),
3538      [ 1.0, -1.0, 0.0].into(),
3539      [ 0.0,  1.0, 0.0].into());
3540    let line = geometry::Segment3::noisy (
3541      [-2.0, 0.0, 0.0].into(),
3542      [ 2.0, 0.0, 0.0].into());
3543    let proximity1 = Proximity::query_triangle_line (&triangle, &line);
3544    let mut rng = XorShiftRng::seed_from_u64 (0);
3545    let std_normal = rand_distr::StandardNormal;
3546    // cauchy sample
3547    let randf = |rng : &mut XorShiftRng| {
3548      let x : f64 = std_normal.sample (rng);
3549      let y : f64 = std_normal.sample (rng);
3550      x / y
3551    };
3552    for _ in 0..10000 {
3553      let translation  = vector3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3554      let mut triangle = triangle;
3555      let mut line     = line;
3556      triangle.translate (translation);
3557      line.translate (translation);
3558      let proximity = Proximity::query_triangle_line (&triangle, &line);
3559      approx::assert_relative_eq!(*proximity.normal, *proximity1.normal,
3560        max_relative = f64::default_max_relative() * 2.0.powi (10))
3561    }
3562  }
3563
3564  #[test]
3565  #[expect(clippy::unreadable_literal)]
3566  fn triangle_triangle() {
3567    use approx::RelativeEq;
3568    use geometry::Primitive;
3569    use rand::SeedableRng;
3570    use rand_distr::Distribution;
3571    let triangle_a = geometry::Triangle3::noisy (
3572      [-3.0, 0.0, 0.0].into(),
3573      [-1.0, 0.0, 0.0].into(),
3574      [-2.0, 2.0, 0.0].into());
3575    let triangle_b = geometry::Triangle3::noisy (
3576      [ 3.0,  0.0, 0.0].into(),
3577      [ 1.0,  0.0, 0.0].into(),
3578      [ 2.0,  2.0, 0.0].into());
3579    let proximity = Proximity::query_triangle_triangle (&triangle_a, &triangle_b);
3580    assert_eq!(proximity, Proximity {
3581      distance:  2.0,
3582      half_axis: [1.0, 0.0, 0.0].into(),
3583      midpoint:  [0.0, 0.0, 0.0].into(),
3584      normal:    -Unit3::axis_x()
3585    });
3586    let triangle_a = geometry::Triangle3::noisy (
3587      [-2.0, -2.0, 0.0].into(),
3588      [ 2.0, -2.0, 0.0].into(),
3589      [ 0.0,  2.0, 0.0].into());
3590    let triangle_b = geometry::Triangle3::noisy (
3591      [-0.5, 0.0, -0.1].into(),
3592      [ 0.5, 0.0, -0.1].into(),
3593      [ 0.0, 0.0,  2.0].into());
3594    let proximity = Proximity::query_triangle_triangle (&triangle_a, &triangle_b);
3595    assert_eq!(proximity.distance, -0.1);
3596    approx::assert_relative_eq!(proximity.half_axis, [0.0, 0.0, -0.05].into());
3597    let triangle_a = geometry::Triangle3::noisy (
3598      [-2.0, -2.0, 0.0].into(),
3599      [ 2.0, -2.0, 0.0].into(),
3600      [ 0.0,  2.0, 0.0].into());
3601    let triangle_b = geometry::Triangle3::noisy (
3602      [-1.0, 0.0, -0.1].into(),
3603      [ 1.0, 0.0, -0.1].into(),
3604      [ 0.0, 0.0,  2.0].into());
3605    let proximity = Proximity::query_triangle_triangle (&triangle_a, &triangle_b);
3606    assert_eq!(proximity.distance, -0.1);
3607    assert_eq!(proximity.half_axis, [0.0, 0.0, -0.05].into());
3608    let mut triangle_a = geometry::Triangle3::noisy (
3609      [-2.0, -2.0, 0.0].into(),
3610      [ 2.0, -2.0, 0.0].into(),
3611      [ 0.0,  2.0, 0.0].into());
3612    let mut triangle_b = geometry::Triangle3::noisy (
3613      [ 0.0, 0.0, -2.0].into(),
3614      [-1.0, 0.0,  2.0].into(),
3615      [ 1.0, 0.0,  2.0].into());
3616    let proximity = Proximity::query_triangle_triangle (&triangle_a, &triangle_b);
3617    assert_eq!(proximity, Proximity {
3618      distance:  -1.3093073414159542,
3619      half_axis: [0.5714285714285714, -0.2857142857142857, -0.1428571428571429].into(),
3620      midpoint:  [-0.14285714285714285, 0.28571428571428564, -0.1428571428571429].into(),
3621      normal:    Unit3::noisy (
3622        [0.8728715609439696, -0.4364357804719848, -0.2182178902359924].into())
3623    });
3624    // make the arrangement asymmetrical so that the result is consistent
3625    triangle_a.translate (proximity.half_axis * 0.5);
3626    triangle_b.translate (-proximity.half_axis * 0.5);
3627    let proximity1 = Proximity::query_triangle_triangle (&triangle_a, &triangle_b);
3628    approx::assert_relative_eq!(proximity1.distance * 2.0, proximity.distance,
3629      max_relative = f64::default_max_relative() * 2.0);
3630    approx::assert_relative_eq!(proximity1.half_axis * 2.0, proximity.half_axis,
3631      max_relative = f64::default_max_relative() * 16.0);
3632    approx::assert_relative_eq!(proximity1.midpoint, proximity.midpoint,
3633      max_relative = f64::default_max_relative() * 8.0);
3634    approx::assert_relative_eq!(*proximity1.normal, *proximity.normal);
3635    // normal should be invariant under translation
3636    let mut rng = XorShiftRng::seed_from_u64 (0);
3637    let std_normal = rand_distr::StandardNormal;
3638    // cauchy sample
3639    let randf = |rng : &mut XorShiftRng| {
3640      let x : f64 = std_normal.sample (rng);
3641      let y : f64 = std_normal.sample (rng);
3642      x / y
3643    };
3644    for _i in 0..5000 {
3645      let translation  = vector3 (randf (&mut rng), randf (&mut rng), randf (&mut rng));
3646      let mut triangle_a = triangle_a;
3647      let mut triangle_b = triangle_b;
3648      triangle_a.translate (translation);
3649      triangle_b.translate (translation);
3650      let proximity = Proximity::query_triangle_triangle (&triangle_a, &triangle_b);
3651      approx::assert_relative_eq!(*proximity.normal, *proximity1.normal,
3652        max_relative = f64::default_max_relative() * 2.0.powi (10))
3653    }
3654  }
3655
3656  #[test]
3657  #[expect(clippy::suboptimal_flops)]
3658  fn distance_query() {
3659    use rand::{RngExt, SeedableRng};
3660    use approx::{assert_relative_eq, assert_ulps_eq};
3661
3662    //
3663    //  capsule v. capsule
3664    //
3665
3666    // intersection: positions of capsules A and B are identical-- half_axis and
3667    // normal parallel to +X
3668    let a = object::Static {
3669      position: component::Position ([0.0, 0.0, 0.0].into()),
3670      bound:    component::Bound::from (shape::Capsule::noisy (2.0, 3.0)),
3671      material: component::MATERIAL_STONE,
3672      collidable: true
3673    };
3674    let b = object::Static {
3675      position: component::Position ([0.0, 0.0, 0.0].into()),
3676      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
3677      material: component::MATERIAL_STONE,
3678      collidable: true
3679    };
3680    assert_eq!(
3681      Proximity::query (&a, &b),
3682      Proximity {
3683        distance:  -3.0,
3684        half_axis: [ 1.5, 0.0, 0.0].into(),
3685        normal:    Unit3::axis_x(),
3686        midpoint:  [-0.5, 0.0, 0.0].into()
3687      }
3688    );
3689
3690    // intersection: cylinder shafts of A and B overlap-- half_axis and normal
3691    // parallel to +X
3692    let a = object::Static {
3693      position: component::Position ([0.0, 0.0, 1.0].into()),  .. a
3694    };
3695    let b = object::Static {
3696      position: component::Position ([0.0, 0.0, -1.0].into()), .. b
3697    };
3698    assert_eq!(
3699      Proximity::query (&a, &b),
3700      Proximity {
3701        distance:  -3.0,
3702        half_axis: [ 1.5, 0.0,  0.0].into(),
3703        normal:    Unit3::axis_x(),
3704        midpoint:  [-0.5, 0.0, -0.5].into()
3705      }
3706    );
3707
3708    // intersection: cylinder shaft max of A coincides with cylinder shaft min
3709    // of B
3710    let a = object::Static {
3711      position: component::Position ([0.0, 0.0, -3.0].into()), .. a
3712    };
3713    let b = object::Static {
3714      position: component::Position ([0.0, 0.0, 2.0].into()),  .. b
3715    };
3716    assert_eq!(
3717      Proximity::query (&a, &b),
3718      Proximity {
3719        distance:  -3.0,
3720        half_axis: [ 1.5, 0.0, 0.0].into(),
3721        normal:    Unit3::axis_x(),
3722        midpoint:  [-0.5, 0.0, 0.0].into()
3723      }
3724    );
3725
3726    // intersection: cylinder shaft min of A coincides with cylinder shaft max of B
3727    let a = object::Static {
3728      position: component::Position ([0.0, 0.0, 3.0].into()),  .. a
3729    };
3730    let b = object::Static {
3731      position: component::Position ([0.0, 0.0, -2.0].into()), .. b
3732    };
3733    assert_eq!(
3734      Proximity::query (&a, &b),
3735      Proximity {
3736        distance:  -3.0,
3737        half_axis: [ 1.5, 0.0, 0.0].into(),
3738        normal:    Unit3::axis_x(),
3739        midpoint:  [-0.5, 0.0, 0.0].into()
3740      }
3741    );
3742
3743    // intersection: cylinder shaft of A adjacent to cylinder shaft of B
3744    let a = object::Static {
3745      position: component::Position ([-1.0, 0.0, 1.0].into()), .. a
3746    };
3747    let b = object::Static {
3748      position: component::Position ([0.0, 0.0, -1.0].into()), .. b
3749    };
3750    assert_eq!(
3751      Proximity::query (&a, &b),
3752      Proximity {
3753        distance:  -2.0,
3754        half_axis: [-1.0, 0.0,  0.0].into(),
3755        normal:    Unit3::axis_x().invert(),
3756        midpoint:  [ 0.0, 0.0, -0.5].into()
3757      }
3758    );
3759
3760    // intersection: cylinder shaft of B above cylinder shaft of A
3761    let a = object::Static {
3762      position: component::Position ([0.0, 0.0, -3.0].into()), .. a
3763    };
3764    let b = object::Static {
3765      position: component::Position ([0.0, 0.0,  3.0].into()), .. b
3766    };
3767    assert_eq!(
3768      Proximity::query (&a, &b),
3769      Proximity {
3770        distance:  -2.0,
3771        half_axis: [ 0.0, 0.0, -1.0].into(),
3772        normal:    Unit3::axis_z().invert(),
3773        midpoint:  [ 0.0, 0.0,  1.0].into()
3774      }
3775    );
3776
3777    // intersection: cylinder shaft of A above cylinder shaft of B
3778    let a = object::Static {
3779      position: component::Position ([0.0, 0.0,  3.0].into()), .. a
3780    };
3781    let b = object::Static {
3782      position: component::Position ([0.0, 0.0, -3.0].into()), .. b
3783    };
3784    assert_eq!(
3785      Proximity::query (&a, &b),
3786      Proximity {
3787        distance:  -2.0,
3788        half_axis: [ 0.0, 0.0,  1.0].into(),
3789        normal:    Unit3::axis_z(),
3790        midpoint:  [ 0.0, 0.0, -1.0].into()
3791      }
3792    );
3793
3794    // intersection: cylinder shafts of A and B are adjacent
3795    let a = object::Static {
3796      position: component::Position ([ 1.0, 0.0, 0.0].into()), .. a
3797    };
3798    let b = object::Static {
3799      position: component::Position ([-1.0, 0.0, 0.0].into()), .. b
3800    };
3801    assert_eq!(
3802      Proximity::query (&a, &b),
3803      Proximity {
3804        distance:  -1.0,
3805        half_axis: [ 0.5, 0.0, 0.0].into(),
3806        normal:    Unit3::axis_x(),
3807        midpoint:  [-0.5, 0.0, 0.0].into()
3808      }
3809    );
3810
3811    // disjoint: capsule A above and to the right (+X) of capsule B
3812    let a = object::Static {
3813      position: component::Position ([ 2.0, 0.0,  5.0].into()), .. a
3814    };
3815    let b = object::Static {
3816      position: component::Position ([-1.0, 0.0, -3.0].into()), .. b
3817    };
3818
3819    let proximity = Proximity::query (&a, &b);
3820    let distance_manual = {
3821      let distance  = vector3 (3.0, 0.0, 3.0).magnitude() - 3.0;
3822      let half_axis : Vector3 <_> =
3823        0.5 * distance * vector3 (-1.0, 0.0, -1.0).normalized();
3824      let normal    = Unit3::normalize ([1.0, 0.0, 1.0].into());
3825      let midpoint  = Point3::from ([-1.0, 0.0, -1.0]) + *normal - half_axis;
3826      Proximity { distance, half_axis, midpoint, normal }
3827    };
3828    assert_eq!(proximity.distance, distance_manual.distance);
3829    assert_relative_eq!(proximity.half_axis, distance_manual.half_axis);
3830    assert_relative_eq!(*proximity.normal,   *distance_manual.normal);
3831    assert_relative_eq!(proximity.midpoint,  distance_manual.midpoint,
3832      epsilon = 2.0 * f64::EPSILON);
3833
3834    // disjoint: cylinder shafts of A and B are adjacent
3835    let a = object::Static {
3836      position: component::Position ([ 3.0, 0.0, 3.0].into()), .. a
3837    };
3838    let b = object::Static {
3839      position: component::Position ([-1.0, 0.0, 0.0].into()), .. b
3840    };
3841    assert_eq!(
3842      Proximity::query (&a, &b),
3843      Proximity {
3844        distance:  1.0,
3845        half_axis: [-0.5, 0.0, 0.0].into(),
3846        normal:    Unit3::axis_x(),
3847        midpoint:  [ 0.5, 0.0, 1.0].into()
3848      }
3849    );
3850
3851    //
3852    //  cuboid v. cuboid
3853    //
3854
3855    // disjoint: nearest points are corners
3856    let a = object::Static {
3857      position: component::Position ([-5.0, -5.0, -5.0].into()),
3858      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 2.0, 3.0])),
3859      .. a
3860    };
3861    let b = object::Static {
3862      position: component::Position ([5.0, 5.0, 5.0].into()),
3863      bound:    component::Bound::from (shape::Cuboid::noisy ([3.0, 1.0, 2.0])),
3864      .. b
3865    };
3866    let proximity = Proximity::query (&a, &b);
3867    assert_eq!(proximity.distance, f64::sqrt (110.0));
3868    assert_eq!(proximity.half_axis, [3.0, 3.5, 2.5].into());
3869    assert_ulps_eq!(*proximity.normal, -vector3 (3.0, 3.5, 2.5).normalized());
3870    assert_eq!(proximity.midpoint,  [-1.0, 0.5, 0.5].into());
3871
3872    // disjoint: overlap on X axis only
3873    let a = object::Static {
3874      position: component::Position ([-5.0, 0.0, -5.0].into()), .. a
3875    };
3876    let b = object::Static {
3877      position: component::Position ([5.0, 0.0, 5.0].into()), .. b
3878    };
3879    let proximity = Proximity::query (&a, &b);
3880    assert_eq!(proximity.distance, f64::sqrt (61.0));
3881    assert_eq!(proximity.half_axis, [3.0, 0.0, 2.5].into());
3882    assert_ulps_eq!(*proximity.normal, -vector3 (3.0, 0.0, 2.5).normalized());
3883    assert_eq!(proximity.midpoint,  [-1.0, 0.0, 0.5].into());
3884
3885    //
3886    //  capsule v. cuboid
3887    //
3888
3889    // disjoint: capsule nearest cuboid max corner
3890    let a = object::Static {
3891      position: component::Position ([5.0, 5.0, 3.0].into()),
3892      bound:    component::Bound::from (shape::Capsule::noisy (SQRT_2, 2.0)),
3893      .. a
3894    };
3895    let b = object::Static {
3896      position: component::Position ([0.0, 0.0, 0.0].into()),
3897      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
3898      .. b
3899    };
3900    let proximity = Proximity::query (&a, &b);
3901    assert_ulps_eq!(proximity.distance, 3.0 * SQRT_2);
3902    assert_eq!(proximity.half_axis, [-1.5, -1.5, 0.0].into());
3903    assert_ulps_eq!(*proximity.normal, vector3 (1.0, 1.0, 0.0).normalized());
3904    assert_eq!(proximity.midpoint,  [ 2.5, 2.5, 1.0].into());
3905
3906    // disjoint: capsule nearest cuboid min corner
3907    let a = object::Static {
3908      position: component::Position ([-5.0, -5.0, -3.0].into()),
3909      bound:    component::Bound::from (shape::Capsule::noisy (SQRT_2, 2.0)),
3910      .. a
3911    };
3912    let b = object::Static {
3913      position: component::Position ([0.0, 0.0, 0.0].into()),
3914      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
3915      .. b
3916    };
3917    let proximity = Proximity::query (&a, &b);
3918    assert_ulps_eq!(proximity.distance, 3.0 * SQRT_2);
3919    assert_eq!(proximity.half_axis, [ 1.5,  1.5, 0.0].into());
3920    assert_ulps_eq!(*proximity.normal, vector3 (-1.0, -1.0, 0.0).normalized());
3921    assert_eq!(proximity.midpoint,  [-2.5,-2.5,-1.0].into());
3922
3923    // disjoint: capsule nearest cuboid max Y edge
3924    let a = object::Static {
3925      position: component::Position ([4.0, 0.5, 4.0].into()),
3926      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
3927      .. a
3928    };
3929    let b = object::Static {
3930      position: component::Position ([0.0, 0.0, 0.0].into()),
3931      bound:    component::Bound::from (shape::Cuboid::noisy ([2.0, 2.0, 2.0])),
3932      .. b
3933    };
3934    assert_eq!(
3935      Proximity::query (&a, &b),
3936      Proximity {
3937        distance:  1.0,
3938        half_axis: [-0.5, 0.0, 0.0].into(),
3939        normal:    Unit3::axis_x(),
3940        midpoint:  [ 2.5, 0.5, 2.0].into()
3941      }
3942    );
3943
3944    // disjoint: capsule nearest cuboid min Y edge
3945    let a = object::Static {
3946      position: component::Position ([4.0, 0.5, -4.0].into()), .. a
3947    };
3948    let b = object::Static {
3949      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
3950    };
3951    assert_eq!(
3952      Proximity::query (&a, &b),
3953      Proximity {
3954        distance:  1.0,
3955        half_axis: [-0.5, 0.0,  0.0].into(),
3956        normal:    Unit3::axis_x(),
3957        midpoint:  [ 2.5, 0.5, -2.0].into()
3958      }
3959    );
3960
3961    // disjoint: capsule nearest cuboid max X edge
3962    let a = object::Static {
3963      position: component::Position ([0.5, 4.0, 4.0].into()), .. a
3964    };
3965    let b = object::Static {
3966      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
3967    };
3968    assert_eq!(
3969      Proximity::query (&a, &b),
3970      Proximity {
3971        distance:  1.0,
3972        half_axis: [ 0.0, -0.5, 0.0].into(),
3973        normal:    Unit3::axis_y(),
3974        midpoint:  [ 0.5,  2.5, 2.0].into()
3975      }
3976    );
3977
3978    // disjoint: capsule nearest cuboid min X edge
3979    let a = object::Static {
3980      position: component::Position ([0.5, 4.0, -4.0].into()), .. a
3981    };
3982    let b = object::Static {
3983      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
3984    };
3985    assert_eq!(
3986      Proximity::query (&a, &b),
3987      Proximity {
3988        distance:  1.0,
3989        half_axis: [ 0.0, -0.5,  0.0].into(),
3990        normal:    Unit3::axis_y(),
3991        midpoint:  [ 0.5,  2.5, -2.0].into()
3992      }
3993    );
3994
3995    // disjoint: capsule A above cuboid B
3996    let a = object::Static {
3997      position: component::Position ([0.5, 0.5, 6.0].into()), .. a
3998    };
3999    let b = object::Static {
4000      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4001    };
4002    assert_eq!(
4003      Proximity::query (&a, &b),
4004      Proximity {
4005        distance:  1.0,
4006        half_axis: [ 0.0, 0.0, -0.5].into(),
4007        normal:    Unit3::axis_z(),
4008        midpoint:  [ 0.5, 0.5,  2.5].into()
4009      }
4010    );
4011
4012    // disjoint: capsule A below cuboid B
4013    let a = object::Static {
4014      position: component::Position ([0.5, 0.5, -6.0].into()), .. a
4015    };
4016    let b = object::Static {
4017      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4018    };
4019    assert_eq!(
4020      Proximity::query (&a, &b),
4021      Proximity {
4022        distance:  1.0,
4023        half_axis: [ 0.0, 0.0,  0.5].into(),
4024        normal:    Unit3::axis_z().invert(),
4025        midpoint:  [ 0.5, 0.5, -2.5].into()
4026      }
4027    );
4028
4029    // disjoint: capsule A cylinder nearest max Z edge of cuboid B
4030    let a = object::Static {
4031      position: component::Position ([3.0, 3.0, 2.0].into()), .. a
4032    };
4033    let b = object::Static {
4034      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4035    };
4036    let proximity = Proximity::query (&a, &b);
4037    assert_eq!(proximity.distance, SQRT_2 - 1.0);
4038    assert_eq!(proximity.half_axis, [
4039      -0.5 * f64::sqrt (0.5 * (SQRT_2 - 1.0).powi (2)),
4040      -0.5 * f64::sqrt (0.5 * (SQRT_2 - 1.0).powi (2)),
4041       0.0
4042    ].into());
4043    assert_ulps_eq!(*proximity.normal,
4044      [FRAC_1_SQRT_2, FRAC_1_SQRT_2, 0.0].into());
4045    assert_eq!(proximity.midpoint, [
4046      2.0 + 0.5 * f64::sqrt (0.5 * (SQRT_2 - 1.0).powi (2)),
4047      2.0 + 0.5 * f64::sqrt (0.5 * (SQRT_2 - 1.0).powi (2)),
4048      1.0
4049    ].into());
4050
4051    // disjoint: capsule A cylinder nearest min Z edge of cuboid B
4052    let a = object::Static {
4053      position: component::Position ([-3.0, -3.0, -2.0].into()), .. a
4054    };
4055    let b = object::Static {
4056      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4057    };
4058    let proximity = Proximity::query (&a, &b);
4059    assert_eq!(proximity.distance, SQRT_2 - 1.0);
4060    assert_eq!(proximity.half_axis, [
4061       0.5 * f64::sqrt (0.5 * (SQRT_2 - 1.0).powi (2)),
4062       0.5 * f64::sqrt (0.5 * (SQRT_2 - 1.0).powi (2)),
4063       0.0
4064    ].into());
4065    assert_ulps_eq!(*proximity.normal, [-FRAC_1_SQRT_2, -FRAC_1_SQRT_2, 0.0].into());
4066    assert_eq!(proximity.midpoint, [
4067      -2.0 - 0.5 * f64::sqrt (0.5 * (SQRT_2 - 1.0).powi (2)),
4068      -2.0 - 0.5 * f64::sqrt (0.5 * (SQRT_2 - 1.0).powi (2)),
4069      -1.0
4070    ].into());
4071
4072    // disjoint: capsule A cylinder nearest max X/Z face of cuboid B
4073    let a = object::Static {
4074      position: component::Position ([1.0, 4.0, 2.0].into()), .. a
4075    };
4076    let b = object::Static {
4077      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4078    };
4079    assert_eq!(
4080      Proximity::query (&a, &b),
4081      Proximity {
4082        distance:  1.0,
4083        half_axis: [ 0.0, -0.5,  0.0].into(),
4084        normal:    Unit3::axis_y(),
4085        midpoint:  [ 1.0,  2.5,  1.0].into()
4086      }
4087    );
4088
4089    // disjoint: capsule A cylinder nearest min X/Z face of cuboid B
4090    let a = object::Static {
4091      position: component::Position ([-1.0, -4.0, -2.0].into()), .. a
4092    };
4093    let b = object::Static {
4094      position: component::Position ([0.0, 0.0, 0.0].into()),    .. b
4095    };
4096    assert_eq!(
4097      Proximity::query (&a, &b),
4098      Proximity {
4099        distance:  1.0,
4100        half_axis: [ 0.0,  0.5,  0.0].into(),
4101        normal:    Unit3::axis_y().invert(),
4102        midpoint:  [-1.0, -2.5, -1.0].into()
4103      }
4104    );
4105
4106    // disjoint: capsule A cylinder nearest max Y/Z face of cuboid B
4107    let a = object::Static {
4108      position: component::Position ([4.0, 1.0, 2.0].into()), .. a
4109    };
4110    let b = object::Static {
4111      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4112    };
4113    assert_eq!(
4114      Proximity::query (&a, &b),
4115      Proximity {
4116        distance:  1.0,
4117        half_axis: [-0.5, 0.0, 0.0].into(),
4118        normal:    Unit3::axis_x(),
4119        midpoint:  [ 2.5, 1.0, 1.0].into()
4120      }
4121    );
4122
4123    // disjoint: capsule A cylinder nearest min Y/Z face of cuboid B
4124    let a = object::Static {
4125      position: component::Position ([-4.0, -1.0, -2.0].into()), .. a
4126    };
4127    let b = object::Static {
4128      position: component::Position ([0.0, 0.0, 0.0].into()),    .. b
4129    };
4130    assert_eq!(
4131      Proximity::query (&a, &b),
4132      Proximity {
4133        distance:  1.0,
4134        half_axis: [ 0.5,  0.0,  0.0].into(),
4135        normal:    Unit3::axis_x().invert(),
4136        midpoint:  [-2.5, -1.0, -1.0].into()
4137      }
4138    );
4139
4140    // intersect: capsule cylinder shaft min equal to cuboid max corner
4141    let a = object::Static {
4142      position: component::Position ([2.0, 2.0, 4.0].into()),
4143      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4144      .. a
4145    };
4146    let b = object::Static {
4147      position: component::Position ([0.0, 0.0, 0.0].into()),
4148      bound:    component::Bound::from (shape::Cuboid::noisy ([2.0, 2.0, 2.0])),
4149      .. b
4150    };
4151    assert_eq!(
4152      Proximity::query (&a, &b),
4153      Proximity {
4154        distance:  -1.0,
4155        half_axis: [ 0.5, 0.0, 0.0].into(),
4156        normal:    Unit3::axis_x(),
4157        midpoint:  [ 1.5, 2.0, 2.0].into()
4158      }
4159    );
4160
4161    // intersect: capsule cylinder shaft max equal to cuboid min corner
4162    let a = object::Static {
4163      position: component::Position ([-2.0, -2.0, -4.0].into()), .. a
4164    };
4165    let b = object::Static {
4166      position: component::Position ([0.0, 0.0, 0.0].into()),  .. b
4167    };
4168    assert_eq!(
4169      Proximity::query (&a, &b),
4170      Proximity {
4171        distance:  -1.0,
4172        half_axis: [-0.5,  0.0, 0.0].into(),
4173        normal:    Unit3::axis_x().invert(),
4174        midpoint:  [-1.5, -2.0,-2.0].into()
4175      }
4176    );
4177
4178    // intersect: capsule cylinder shaft min equal to cuboid max Y edge
4179    let a = object::Static {
4180      position: component::Position ([2.0, 1.0, 4.0].into()), .. a
4181    };
4182    let b = object::Static {
4183      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4184    };
4185    assert_eq!(
4186      Proximity::query (&a, &b),
4187      Proximity {
4188        distance:  -1.0,
4189        half_axis: [ 0.5, 0.0, 0.0].into(),
4190        normal:    Unit3::axis_x(),
4191        midpoint:  [ 1.5, 1.0, 2.0].into()
4192      }
4193    );
4194
4195    // intersect: capsule cylinder shaft max equal to cuboid min Y edge
4196    let a = object::Static {
4197      position: component::Position ([-2.0, 1.0, -4.0].into()), .. a
4198    };
4199    let b = object::Static {
4200      position: component::Position ([0.0, 0.0, 0.0].into()),  .. b
4201    };
4202    assert_eq!(
4203      Proximity::query (&a, &b),
4204      Proximity {
4205        distance:  -1.0,
4206        half_axis: [-0.5, 0.0,  0.0].into(),
4207        normal:    Unit3::axis_x().invert(),
4208        midpoint:  [-1.5, 1.0, -2.0].into()
4209      }
4210    );
4211
4212    // intersect: capsule cylinder shaft min equal to cuboid max X edge
4213    let a = object::Static {
4214      position: component::Position ([1.0, 2.0, 4.0].into()), .. a
4215    };
4216    let b = object::Static {
4217      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4218    };
4219    assert_eq!(
4220      Proximity::query (&a, &b),
4221      Proximity {
4222        distance:  -1.0,
4223        half_axis: [ 0.0, 0.5, 0.0].into(),
4224        normal:    Unit3::axis_y(),
4225        midpoint:  [ 1.0, 1.5, 2.0].into()
4226      }
4227    );
4228
4229    // intersect: capsule cylinder shaft max equal to cuboid min X edge
4230    let a = object::Static {
4231      position: component::Position ([ 1.0, -2.0, -4.0].into()), .. a
4232    };
4233    let b = object::Static {
4234      position: component::Position ([0.0, 0.0, 0.0].into()),  .. b
4235    };
4236    assert_eq!(
4237      Proximity::query (&a, &b),
4238      Proximity {
4239        distance:  -1.0,
4240        half_axis: [0.0, -0.5,  0.0].into(),
4241        normal:    Unit3::axis_y().invert(),
4242        midpoint:  [1.0, -1.5, -2.0].into()
4243      }
4244    );
4245
4246    // intersect: capsule cylinder shaft min equal to cuboid max Z face
4247    let a = object::Static {
4248      position: component::Position ([1.0, 1.0, 4.0].into()), .. a
4249    };
4250    let b = object::Static {
4251      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4252    };
4253    assert_eq!(
4254      Proximity::query (&a, &b),
4255      Proximity {
4256        distance:  -1.0,
4257        half_axis: [ 0.0, 0.0, 0.5].into(),
4258        normal:    Unit3::axis_z(),
4259        midpoint:  [ 1.0, 1.0, 1.5].into()
4260      }
4261    );
4262
4263    // intersect: capsule cylinder shaft max equal to cuboid min Z face
4264    let a = object::Static {
4265      position: component::Position ([-1.0, -1.0, -4.0].into()), .. a
4266    };
4267    let b = object::Static {
4268      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4269    };
4270    assert_eq!(
4271      Proximity::query (&a, &b),
4272      Proximity {
4273        distance:  -1.0,
4274        half_axis: [ 0.0,  0.0, -0.5].into(),
4275        normal:    Unit3::axis_z().invert(),
4276        midpoint:  [-1.0, -1.0, -1.5].into()
4277      }
4278    );
4279
4280    // intersect: capsule and cuboid position coincide
4281    let a = object::Static {
4282      position: component::Position ([0.0, 0.0, 0.0].into()), .. a
4283    };
4284    let b = object::Static {
4285      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4286    };
4287    assert_eq!(
4288      Proximity::query (&a, &b),
4289      Proximity {
4290        distance:  -3.0,
4291        half_axis: [ 1.5,  0.0,  0.0].into(),
4292        normal:    Unit3::axis_x(),
4293        midpoint:  [ 0.5,  0.0,  0.0].into()
4294      }
4295    );
4296
4297    // intersect: capsule nearest positive X face of cuboid
4298    let a = object::Static {
4299      position: component::Position ([1.0, 0.0, 0.0].into()), .. a
4300    };
4301    let b = object::Static {
4302      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4303    };
4304    assert_eq!(
4305      Proximity::query (&a, &b),
4306      Proximity {
4307        distance:  -2.0,
4308        half_axis: [ 1.0,  0.0,  0.0].into(),
4309        normal:    Unit3::axis_x(),
4310        midpoint:  [ 1.0,  0.0,  0.0].into()
4311      }
4312    );
4313
4314    // intersect: capsule nearest negative X face of cuboid
4315    let a = object::Static {
4316      position: component::Position ([-1.0, 0.0, 0.0].into()), .. a
4317    };
4318    let b = object::Static {
4319      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4320    };
4321    assert_eq!(
4322      Proximity::query (&a, &b),
4323      Proximity {
4324        distance:  -2.0,
4325        half_axis: [-1.0,  0.0,  0.0].into(),
4326        normal:    Unit3::axis_x().invert(),
4327        midpoint:  [-1.0,  0.0,  0.0].into()
4328      }
4329    );
4330
4331    // intersect: capsule nearest positive Y face of cuboid
4332    let a = object::Static {
4333      position: component::Position ([0.0, 1.0, 0.0].into()), .. a
4334    };
4335    let b = object::Static {
4336      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4337    };
4338    assert_eq!(
4339      Proximity::query (&a, &b),
4340      Proximity {
4341        distance:  -2.0,
4342        half_axis: [0.0, 1.0, 0.0].into(),
4343        normal:    Unit3::axis_y(),
4344        midpoint:  [0.0, 1.0, 0.0].into()
4345      }
4346    );
4347
4348    // intersect: capsule nearest negative Y face of cuboid
4349    let a = object::Static {
4350      position: component::Position ([0.0, -1.0, 0.0].into()), .. a
4351    };
4352    let b = object::Static {
4353      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
4354    };
4355    assert_eq!(
4356      Proximity::query (&a, &b),
4357      Proximity {
4358        distance:  -2.0,
4359        half_axis: [0.0, -1.0, 0.0].into(),
4360        normal:    Unit3::axis_y().invert(),
4361        midpoint:  [0.0, -1.0, 0.0].into()
4362      }
4363    );
4364
4365    // intersect: capsule nearest positive Z face of cuboid
4366    let a = object::Static {
4367      position: component::Position ([0.0, 0.0, 3.0].into()),
4368      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4369      .. a
4370    };
4371    let b = object::Static {
4372      position: component::Position ([0.0, 0.0, 0.0].into()),
4373      bound:    component::Bound::from (shape::Cuboid::noisy ([4.0, 4.0, 2.0])),
4374      .. b
4375    };
4376    assert_eq!(
4377      Proximity::query (&a, &b),
4378      Proximity {
4379        distance:  -2.0,
4380        half_axis: [0.0, 0.0, 1.0].into(),
4381        normal:    Unit3::axis_z(),
4382        midpoint:  [0.0, 0.0, 1.0].into()
4383      }
4384    );
4385
4386    // intersect: capsule nearest positive Z face of cuboid
4387    let a = object::Static {
4388      position: component::Position ([0.0, 0.0, 1.0].into()),
4389      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4390      .. a
4391    };
4392    let b = object::Static {
4393      position: component::Position ([0.0, 0.0, 0.0].into()),
4394      bound:    component::Bound::from (shape::Cuboid::noisy ([4.0, 4.0, 2.0])),
4395      .. b
4396    };
4397    assert_eq!(
4398      Proximity::query (&a, &b),
4399      Proximity {
4400        distance:  -4.0,
4401        half_axis: [0.0, 0.0, 2.0].into(),
4402        normal:    Unit3::axis_z(),
4403        midpoint:  [0.0, 0.0, 0.0].into()
4404      }
4405    );
4406
4407    // intersect: capsule nearest negative Z face of cuboid
4408    let a = object::Static {
4409      position: component::Position ([0.0, 0.0, -3.0].into()),
4410      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4411      .. a
4412    };
4413    let b = object::Static {
4414      position: component::Position ([0.0, 0.0, 0.0].into()),
4415      bound:    component::Bound::from (shape::Cuboid::noisy ([4.0, 4.0, 2.0])),
4416      .. b
4417    };
4418    assert_eq!(
4419      Proximity::query (&a, &b),
4420      Proximity {
4421        distance:  -2.0,
4422        half_axis: [0.0, 0.0, -1.0].into(),
4423        normal:    Unit3::axis_z().invert(),
4424        midpoint:  [0.0, 0.0, -1.0].into()
4425      }
4426    );
4427
4428    // intersect: capsule nearest negative Z face of cuboid
4429    let a = object::Static {
4430      position: component::Position ([0.0, 0.0, -1.0].into()),
4431      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4432      .. a
4433    };
4434    let b = object::Static {
4435      position: component::Position ([0.0, 0.0, 0.0].into()),
4436      bound:    component::Bound::from (shape::Cuboid::noisy ([4.0, 4.0, 2.0])),
4437      .. b
4438    };
4439    assert_eq!(
4440      Proximity::query (&a, &b),
4441      Proximity {
4442        distance:  -4.0,
4443        half_axis: [0.0, 0.0, -2.0].into(),
4444        normal:    Unit3::axis_z().invert(),
4445        midpoint:  [0.0, 0.0,  0.0].into()
4446      }
4447    );
4448
4449    // random queries
4450    let mut rng = XorShiftRng::seed_from_u64 (0);
4451    let a = object::Static {
4452      position: component::Position ([0.0, 0.0, 0.0].into()),
4453      bound:    component::Bound::from (shape::Capsule::noisy (1.5, 1.5)),
4454      .. a
4455    };
4456    for _ in 0..100 {
4457      let position = component::Position ([
4458        rng.random_range (-40.0..40.0),
4459        rng.random_range (-40.0..40.0),
4460        rng.random_range (-40.0..40.0)
4461      ].into());
4462      let bound    = shape::Cuboid::noisy ([
4463        rng.random_range (0.5..5.0),
4464        rng.random_range (0.5..5.0),
4465        rng.random_range (0.5..5.0)
4466      ]).into();
4467      let b = object::Static {
4468        position, bound, material: component::MATERIAL_STONE, collidable: true
4469      };
4470      let _ = Proximity::query (&a, &b);
4471    }
4472
4473    //
4474    //  capsule v. orthant
4475    //
4476    use SignedAxis3;
4477
4478    // disjoint: capsule outside of orthant +Z
4479    let a = object::Static {
4480      position: component::Position ([0.0, 0.0, 4.0].into()),
4481      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4482      .. a
4483    };
4484    let b = object::Static {
4485      position: component::Position ([0.0, 0.0, 0.0].into()),
4486      bound:    shape::Orthant { normal_axis: SignedAxis3::PosZ }.into(),
4487      .. b
4488    };
4489    assert_eq!(
4490      Proximity::query (&a, &b),
4491      Proximity {
4492        distance:  1.0,
4493        half_axis: [0.0, 0.0, -0.5].into(),
4494        normal:    Unit3::axis_z(),
4495        midpoint:  [0.0, 0.0,  0.5].into()
4496      }
4497    );
4498
4499    // disjoint: capsule outside of orthant -Z
4500    let a = object::Static {
4501      position: component::Position ([0.0, 0.0, -4.0].into()),
4502      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4503      .. a
4504    };
4505    let b = object::Static {
4506      position: component::Position ([0.0, 0.0, 0.0].into()),
4507      bound:    shape::Orthant { normal_axis: SignedAxis3::NegZ }.into(),
4508      .. b
4509    };
4510    assert_eq!(
4511      Proximity::query (&a, &b),
4512      Proximity {
4513        distance:  1.0,
4514        half_axis: [0.0, 0.0,  0.5].into(),
4515        normal:    Unit3::axis_z().invert(),
4516        midpoint:  [0.0, 0.0, -0.5].into()
4517      }
4518    );
4519
4520    // disjoint: capsule outside orthant +Y
4521    let a = object::Static {
4522      position: component::Position ([0.0, 2.0, 0.0].into()),
4523      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4524      .. a
4525    };
4526    let b = object::Static {
4527      position: component::Position ([0.0, 0.0, 0.0].into()),
4528      bound:    shape::Orthant { normal_axis: SignedAxis3::PosY }.into(),
4529      .. b
4530    };
4531    assert_eq!(
4532      Proximity::query (&a, &b),
4533      Proximity {
4534        distance:  1.0,
4535        half_axis: [0.0, -0.5, 0.0].into(),
4536        normal:    Unit3::axis_y(),
4537        midpoint:  [0.0,  0.5, 0.0].into()
4538      }
4539    );
4540
4541    // disjoint: capsule outside orthant -Y
4542    let a = object::Static {
4543      position: component::Position ([0.0, -2.0, 0.0].into()),
4544      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4545      .. a
4546    };
4547    let b = object::Static {
4548      position: component::Position ([0.0, 0.0, 0.0].into()),
4549      bound:    shape::Orthant { normal_axis: SignedAxis3::NegY }.into(),
4550      .. b
4551    };
4552    assert_eq!(
4553      Proximity::query (&a, &b),
4554      Proximity {
4555        distance:  1.0,
4556        half_axis: [0.0,  0.5, 0.0].into(),
4557        normal:    Unit3::axis_y().invert(),
4558        midpoint:  [0.0, -0.5, 0.0].into()
4559      }
4560    );
4561
4562    // disjoint: capsule outside orthant +X
4563    let a = object::Static {
4564      position: component::Position ([2.0, 0.0, 0.0].into()),
4565      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4566      .. a
4567    };
4568    let b = object::Static {
4569      position: component::Position ([0.0, 0.0, 0.0].into()),
4570      bound:    shape::Orthant { normal_axis: SignedAxis3::PosX }.into(),
4571      .. b
4572    };
4573    assert_eq!(
4574      Proximity::query (&a, &b),
4575      Proximity {
4576        distance:  1.0,
4577        half_axis: [-0.5, 0.0, 0.0].into(),
4578        normal:    Unit3::axis_x(),
4579        midpoint:  [ 0.5, 0.0, 0.0].into()
4580      }
4581    );
4582
4583    // disjoint: outside orthant -X
4584    let a = object::Static {
4585      position: component::Position ([-2.0, 0.0, 0.0].into()),
4586      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4587      .. a
4588    };
4589    let b = object::Static {
4590      position: component::Position ([0.0, 0.0, 0.0].into()),
4591      bound:    shape::Orthant { normal_axis: SignedAxis3::NegX }.into(),
4592      .. b
4593    };
4594    assert_eq!(
4595      Proximity::query (&a, &b),
4596      Proximity {
4597        distance:  1.0,
4598        half_axis: [ 0.5, 0.0, 0.0].into(),
4599        normal:    Unit3::axis_x().invert(),
4600        midpoint:  [-0.5, 0.0, 0.0].into()
4601      }
4602    );
4603
4604    // intersect: capsule and orthant position coincide; normal +Z
4605    let a = object::Static {
4606      position: component::Position ([0.0, 0.0, 0.0].into()),
4607      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4608      .. a
4609    };
4610    let b = object::Static {
4611      position: component::Position ([0.0, 0.0, 0.0].into()),
4612      bound:    shape::Orthant { normal_axis: SignedAxis3::PosZ }.into(),
4613      .. b
4614    };
4615    assert_eq!(
4616      Proximity::query (&a, &b),
4617      Proximity {
4618        distance:  -3.0,
4619        half_axis: [0.0, 0.0,  1.5].into(),
4620        normal:    Unit3::axis_z(),
4621        midpoint:  [0.0, 0.0, -1.5].into()
4622      }
4623    );
4624
4625    // intersect: capsule and orthant position coincide; normal -Z
4626    let a = object::Static {
4627      position: component::Position ([0.0, 0.0, 0.0].into()),
4628      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4629      .. a
4630    };
4631    let b = object::Static {
4632      position: component::Position ([0.0, 0.0, 0.0].into()),
4633      bound:    shape::Orthant { normal_axis: SignedAxis3::NegZ }.into(),
4634      .. b
4635    };
4636    assert_eq!(
4637      Proximity::query (&a, &b),
4638      Proximity {
4639        distance:  -3.0,
4640        half_axis: [0.0, 0.0, -1.5].into(),
4641        normal:    Unit3::axis_z().invert(),
4642        midpoint:  [0.0, 0.0,  1.5].into()
4643      }
4644    );
4645
4646    // intersect: capsule and orthant position coincide; normal +Y
4647    let a = object::Static {
4648      position: component::Position ([0.0, 0.0, 0.0].into()),
4649      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4650      .. a
4651    };
4652    let b = object::Static {
4653      position: component::Position ([0.0, 0.0, 0.0].into()),
4654      bound:    shape::Orthant { normal_axis: SignedAxis3::PosY }.into(),
4655      .. b
4656    };
4657    assert_eq!(
4658      Proximity::query (&a, &b),
4659      Proximity {
4660        distance:  -1.0,
4661        half_axis: [0.0,  0.5, 0.0].into(),
4662        normal:    Unit3::axis_y(),
4663        midpoint:  [0.0, -0.5, 0.0].into()
4664      }
4665    );
4666
4667    // intersect: capsule and orthant position coincide; normal -Y
4668    let a = object::Static {
4669      position: component::Position ([0.0, 0.0, 0.0].into()),
4670      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4671      .. a
4672    };
4673    let b = object::Static {
4674      position: component::Position ([0.0, 0.0, 0.0].into()),
4675      bound:    shape::Orthant { normal_axis: SignedAxis3::NegY }.into(),
4676      .. b
4677    };
4678    assert_eq!(
4679      Proximity::query (&a, &b),
4680      Proximity {
4681        distance:  -1.0,
4682        half_axis: [0.0, -0.5, 0.0].into(),
4683        normal:    Unit3::axis_y().invert(),
4684        midpoint:  [0.0,  0.5, 0.0].into()
4685      }
4686    );
4687
4688    // intersect: capsule and orthant position coincide; normal +X
4689    let a = object::Static {
4690      position: component::Position ([0.0, 0.0, 0.0].into()),
4691      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4692      .. a
4693    };
4694    let b = object::Static {
4695      position: component::Position ([0.0, 0.0, 0.0].into()),
4696      bound:    shape::Orthant { normal_axis: SignedAxis3::PosX }.into(),
4697      .. b
4698    };
4699    assert_eq!(
4700      Proximity::query (&a, &b),
4701      Proximity {
4702        distance:  -1.0,
4703        half_axis: [ 0.5, 0.0, 0.0].into(),
4704        normal:    Unit3::axis_x(),
4705        midpoint:  [-0.5, 0.0, 0.0].into()
4706      }
4707    );
4708
4709    // intersect: capsule and orthant position coincide; normal -X
4710    let a = object::Static {
4711      position: component::Position ([0.0, 0.0, 0.0].into()),
4712      bound:    component::Bound::from (shape::Capsule::noisy (1.0, 2.0)),
4713      .. a
4714    };
4715    let b = object::Static {
4716      position: component::Position ([0.0, 0.0, 0.0].into()),
4717      bound:    shape::Orthant { normal_axis: SignedAxis3::NegX }.into(),
4718      .. b
4719    };
4720    assert_eq!(
4721      Proximity::query (&a, &b),
4722      Proximity {
4723        distance:  -1.0,
4724        half_axis: [-0.5, 0.0, 0.0].into(),
4725        normal:    Unit3::axis_x().invert(),
4726        midpoint:  [ 0.5, 0.0, 0.0].into()
4727      }
4728    );
4729
4730    //
4731    //  sphere v. orthant
4732    //
4733
4734    // disjoint: sphere outside of orthant +Z
4735    let a = object::Static {
4736      position: component::Position ([0.0, 0.0, 4.0].into()),
4737      bound:    component::Bound::from (shape::Sphere::noisy (1.0)),
4738      .. a
4739    };
4740    let b = object::Static {
4741      position: component::Position ([0.0, 0.0, 0.0].into()),
4742      bound:    shape::Orthant { normal_axis: SignedAxis3::PosZ }.into(),
4743      .. b
4744    };
4745    assert_eq!(
4746      Proximity::query (&a, &b),
4747      Proximity {
4748        distance:  3.0,
4749        half_axis: [0.0, 0.0, -1.5].into(),
4750        normal:    Unit3::axis_z(),
4751        midpoint:  [0.0, 0.0,  1.5].into()
4752      }
4753    );
4754
4755    // disjoint: sphere outside of orthant -Z
4756    let a = object::Static {
4757      position: component::Position ([0.0, 0.0, -4.0].into()),
4758      bound:    component::Bound::from (shape::Sphere::noisy (1.0)),
4759      .. a
4760    };
4761    let b = object::Static {
4762      position: component::Position ([0.0, 0.0, 0.0].into()),
4763      bound:    shape::Orthant { normal_axis: SignedAxis3::NegZ }.into(),
4764      .. b
4765    };
4766    assert_eq!(
4767      Proximity::query (&a, &b),
4768      Proximity {
4769        distance:  3.0,
4770        half_axis: [0.0, 0.0,  1.5].into(),
4771        normal:    Unit3::axis_z().invert(),
4772        midpoint:  [0.0, 0.0, -1.5].into()
4773      }
4774    );
4775
4776    // disjoint: sphere outside orthant +Y
4777    let a = object::Static {
4778      position: component::Position ([0.0, 2.0, 0.0].into()),
4779      bound:    component::Bound::from (shape::Sphere::noisy (1.0)),
4780      .. a
4781    };
4782    let b = object::Static {
4783      position: component::Position ([0.0, 0.0, 0.0].into()),
4784      bound:    shape::Orthant { normal_axis: SignedAxis3::PosY }.into(),
4785      .. b
4786    };
4787    assert_eq!(
4788      Proximity::query (&a, &b),
4789      Proximity {
4790        distance:  1.0,
4791        half_axis: [0.0, -0.5, 0.0].into(),
4792        normal:    Unit3::axis_y(),
4793        midpoint:  [0.0,  0.5, 0.0].into()
4794      }
4795    );
4796
4797    // disjoint: sphere outside orthant -Y
4798    let a = object::Static {
4799      position: component::Position ([0.0, -2.0, 0.0].into()),
4800      bound:    component::Bound::from (shape::Sphere::noisy (1.0)),
4801      .. a
4802    };
4803    let b = object::Static {
4804      position: component::Position ([0.0, 0.0, 0.0].into()),
4805      bound:    shape::Orthant { normal_axis: SignedAxis3::NegY }.into(),
4806      .. b
4807    };
4808    assert_eq!(
4809      Proximity::query (&a, &b),
4810      Proximity {
4811        distance:  1.0,
4812        half_axis: [0.0,  0.5, 0.0].into(),
4813        normal:    Unit3::axis_y().invert(),
4814        midpoint:  [0.0, -0.5, 0.0].into()
4815      }
4816    );
4817
4818    // disjoint: sphere outside orthant +X
4819    let a = object::Static {
4820      position: component::Position ([2.0, 0.0, 0.0].into()),
4821      bound:    component::Bound::from (shape::Sphere::noisy (1.0)),
4822      .. a
4823    };
4824    let b = object::Static {
4825      position: component::Position ([0.0, 0.0, 0.0].into()),
4826      bound:    shape::Orthant { normal_axis: SignedAxis3::PosX }.into(),
4827      .. b
4828    };
4829    assert_eq!(
4830      Proximity::query (&a, &b),
4831      Proximity {
4832        distance:  1.0,
4833        half_axis: [-0.5, 0.0, 0.0].into(),
4834        normal:    Unit3::axis_x(),
4835        midpoint:  [ 0.5, 0.0, 0.0].into()
4836      }
4837    );
4838
4839    // disjoint: outside orthant -X
4840    let a = object::Static {
4841      position: component::Position ([-2.0, 0.0, 0.0].into()),
4842      bound:    component::Bound::from (shape::Sphere::noisy (1.0)),
4843      .. a
4844    };
4845    let b = object::Static {
4846      position: component::Position ([0.0, 0.0, 0.0].into()),
4847      bound:    shape::Orthant { normal_axis: SignedAxis3::NegX }.into(),
4848      .. b
4849    };
4850    assert_eq!(
4851      Proximity::query (&a, &b),
4852      Proximity {
4853        distance:  1.0,
4854        half_axis: [ 0.5, 0.0, 0.0].into(),
4855        normal:    Unit3::axis_x().invert(),
4856        midpoint:  [-0.5, 0.0, 0.0].into()
4857      }
4858    );
4859
4860    // intersect: sphere and orthant position coincide; normal +Z
4861    let a = object::Static {
4862      position: component::Position ([0.0, 0.0, 0.0].into()),
4863      bound:    component::Bound::from (shape::Sphere::noisy (1.0)),
4864      .. a
4865    };
4866    let b = object::Static {
4867      position: component::Position ([0.0, 0.0, 0.0].into()),
4868      bound:    shape::Orthant { normal_axis: SignedAxis3::PosZ }.into(),
4869      .. b
4870    };
4871    assert_eq!(
4872      Proximity::query (&a, &b),
4873      Proximity {
4874        distance:  -1.0,
4875        half_axis: [0.0, 0.0,  0.5].into(),
4876        normal:    Unit3::axis_z(),
4877        midpoint:  [0.0, 0.0, -0.5].into()
4878      }
4879    );
4880
4881    // intersect: sphere and orthant position coincide; normal -Z
4882    let a = object::Static {
4883      position: component::Position ([0.0, 0.0, 0.0].into()),
4884      bound:    component::Bound::from (shape::Sphere::noisy (1.0)),
4885      .. a
4886    };
4887    let b = object::Static {
4888      position: component::Position ([0.0, 0.0, 0.0].into()),
4889      bound:    shape::Orthant { normal_axis: SignedAxis3::NegZ }.into(),
4890      .. b
4891    };
4892    assert_eq!(
4893      Proximity::query (&a, &b),
4894      Proximity {
4895        distance:  -1.0,
4896        half_axis: [0.0, 0.0, -0.5].into(),
4897        normal:    Unit3::axis_z().invert(),
4898        midpoint:  [0.0, 0.0,  0.5].into()
4899      }
4900    );
4901
4902    // intersect: sphere and orthant position coincide; normal +Y
4903    let a = object::Static {
4904      position: component::Position ([0.0, 0.0, 0.0].into()),
4905      bound:    component::Bound::from (shape::Sphere::noisy (1.0)),
4906      .. a
4907    };
4908    let b = object::Static {
4909      position: component::Position ([0.0, 0.0, 0.0].into()),
4910      bound:    shape::Orthant { normal_axis: SignedAxis3::PosY }.into(),
4911      .. b
4912    };
4913    assert_eq!(
4914      Proximity::query (&a, &b),
4915      Proximity {
4916        distance:  -1.0,
4917        half_axis: [0.0,  0.5, 0.0].into(),
4918        normal:    Unit3::axis_y(),
4919        midpoint:  [0.0, -0.5, 0.0].into()
4920      }
4921    );
4922
4923    // intersect: sphere and orthant position coincide; normal -Y
4924    let a = object::Static {
4925      position: component::Position ([0.0, 0.0, 0.0].into()),
4926      bound:    component::Bound::from (shape::Sphere::noisy (1.0)),
4927      .. a
4928    };
4929    let b = object::Static {
4930      position: component::Position ([0.0, 0.0, 0.0].into()),
4931      bound:    shape::Orthant { normal_axis: SignedAxis3::NegY }.into(),
4932      .. b
4933    };
4934    assert_eq!(
4935      Proximity::query (&a, &b),
4936      Proximity {
4937        distance:  -1.0,
4938        half_axis: [0.0, -0.5, 0.0].into(),
4939        normal:    Unit3::axis_y().invert(),
4940        midpoint:  [0.0,  0.5, 0.0].into()
4941      }
4942    );
4943
4944    // intersect: sphere and orthant position coincide; normal +X
4945    let a = object::Static {
4946      position: component::Position ([0.0, 0.0, 0.0].into()),
4947      bound:    component::Bound::from (shape::Sphere::noisy (1.0)),
4948      .. a
4949    };
4950    let b = object::Static {
4951      position: component::Position ([0.0, 0.0, 0.0].into()),
4952      bound:    shape::Orthant { normal_axis: SignedAxis3::PosX }.into(),
4953      .. b
4954    };
4955    assert_eq!(
4956      Proximity::query (&a, &b),
4957      Proximity {
4958        distance:  -1.0,
4959        half_axis: [ 0.5, 0.0, 0.0].into(),
4960        normal:    Unit3::axis_x(),
4961        midpoint:  [-0.5, 0.0, 0.0].into()
4962      }
4963    );
4964
4965    // intersect: sphere and orthant position coincide; normal -X
4966    let a = object::Static {
4967      position: component::Position ([0.0, 0.0, 0.0].into()),
4968      bound:    component::Bound::from (shape::Sphere::noisy (1.0)),
4969      .. a
4970    };
4971    let b = object::Static {
4972      position: component::Position ([0.0, 0.0, 0.0].into()),
4973      bound:    shape::Orthant { normal_axis: SignedAxis3::NegX }.into(),
4974      .. b
4975    };
4976    assert_eq!(
4977      Proximity::query (&a, &b),
4978      Proximity {
4979        distance:  -1.0,
4980        half_axis: [-0.5, 0.0, 0.0].into(),
4981        normal:    Unit3::axis_x().invert(),
4982        midpoint:  [ 0.5, 0.0, 0.0].into()
4983      }
4984    );
4985
4986    //
4987    //  cuboid v. orthant
4988    //
4989
4990    // disjoint: cuboid outside of orthant +Z
4991    let a = object::Static {
4992      position: component::Position ([0.0, 0.0, 4.0].into()),
4993      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
4994      .. a
4995    };
4996    let b = object::Static {
4997      position: component::Position ([0.0, 0.0, 0.0].into()),
4998      bound:    shape::Orthant { normal_axis: SignedAxis3::PosZ }.into(),
4999      .. b
5000    };
5001    assert_eq!(
5002      Proximity::query (&a, &b),
5003      Proximity {
5004        distance:  3.0,
5005        half_axis: [0.0, 0.0, -1.5].into(),
5006        normal:    Unit3::axis_z(),
5007        midpoint:  [0.0, 0.0,  1.5].into()
5008      }
5009    );
5010
5011    // disjoint: cuboid outside of orthant -Z
5012    let a = object::Static {
5013      position: component::Position ([0.0, 0.0, -4.0].into()),
5014      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
5015      .. a
5016    };
5017    let b = object::Static {
5018      position: component::Position ([0.0, 0.0, 0.0].into()),
5019      bound:    shape::Orthant { normal_axis: SignedAxis3::NegZ }.into(),
5020      .. b
5021    };
5022    assert_eq!(
5023      Proximity::query (&a, &b),
5024      Proximity {
5025        distance:  3.0,
5026        half_axis: [0.0, 0.0,  1.5].into(),
5027        normal:    Unit3::axis_z().invert(),
5028        midpoint:  [0.0, 0.0, -1.5].into()
5029      }
5030    );
5031
5032    // disjoint: cuboid outside orthant +Y
5033    let a = object::Static {
5034      position: component::Position ([0.0, 2.0, 0.0].into()),
5035      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
5036      .. a
5037    };
5038    let b = object::Static {
5039      position: component::Position ([0.0, 0.0, 0.0].into()),
5040      bound:    shape::Orthant { normal_axis: SignedAxis3::PosY }.into(),
5041      .. b
5042    };
5043    assert_eq!(
5044      Proximity::query (&a, &b),
5045      Proximity {
5046        distance:  1.0,
5047        half_axis: [0.0, -0.5, 0.0].into(),
5048        normal:    Unit3::axis_y(),
5049        midpoint:  [0.0,  0.5, 0.0].into()
5050      }
5051    );
5052
5053    // disjoint: cuboid outside orthant -Y
5054    let a = object::Static {
5055      position: component::Position ([0.0, -2.0, 0.0].into()),
5056      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
5057      .. a
5058    };
5059    let b = object::Static {
5060      position: component::Position ([0.0, 0.0, 0.0].into()),
5061      bound:    shape::Orthant { normal_axis: SignedAxis3::NegY }.into(),
5062      .. b
5063    };
5064    assert_eq!(
5065      Proximity::query (&a, &b),
5066      Proximity {
5067        distance:  1.0,
5068        half_axis: [0.0,  0.5, 0.0].into(),
5069        normal:    Unit3::axis_y().invert(),
5070        midpoint:  [0.0, -0.5, 0.0].into()
5071      }
5072    );
5073
5074    // disjoint: cuboid outside orthant +X
5075    let a = object::Static {
5076      position: component::Position ([2.0, 0.0, 0.0].into()),
5077      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
5078      .. a
5079    };
5080    let b = object::Static {
5081      position: component::Position ([0.0, 0.0, 0.0].into()),
5082      bound:    shape::Orthant { normal_axis: SignedAxis3::PosX }.into(),
5083      .. b
5084    };
5085    assert_eq!(
5086      Proximity::query (&a, &b),
5087      Proximity {
5088        distance:  1.0,
5089        half_axis: [-0.5, 0.0, 0.0].into(),
5090        normal:    Unit3::axis_x(),
5091        midpoint:  [ 0.5, 0.0, 0.0].into()
5092      }
5093    );
5094
5095    // disjoint: outside orthant -X
5096    let a = object::Static {
5097      position: component::Position ([-2.0, 0.0, 0.0].into()),
5098      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
5099      .. a
5100    };
5101    let b = object::Static {
5102      position: component::Position ([0.0, 0.0, 0.0].into()),
5103      bound:    shape::Orthant { normal_axis: SignedAxis3::NegX }.into(),
5104      .. b
5105    };
5106    assert_eq!(
5107      Proximity::query (&a, &b),
5108      Proximity {
5109        distance:  1.0,
5110        half_axis: [ 0.5, 0.0, 0.0].into(),
5111        normal:    Unit3::axis_x().invert(),
5112        midpoint:  [-0.5, 0.0, 0.0].into()
5113      }
5114    );
5115
5116    // intersect: cuboid and orthant position coincide; normal +Z
5117    let a = object::Static {
5118      position: component::Position ([0.0, 0.0, 0.0].into()),
5119      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
5120      .. a
5121    };
5122    let b = object::Static {
5123      position: component::Position ([0.0, 0.0, 0.0].into()),
5124      bound:    shape::Orthant { normal_axis: SignedAxis3::PosZ }.into(),
5125      .. b
5126    };
5127    assert_eq!(
5128      Proximity::query (&a, &b),
5129      Proximity {
5130        distance:  -1.0,
5131        half_axis: [0.0, 0.0,  0.5].into(),
5132        normal:    Unit3::axis_z(),
5133        midpoint:  [0.0, 0.0, -0.5].into()
5134      }
5135    );
5136
5137    // intersect: cuboid and orthant position coincide; normal -Z
5138    let a = object::Static {
5139      position: component::Position ([0.0, 0.0, 0.0].into()),
5140      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
5141      .. a
5142    };
5143    let b = object::Static {
5144      position: component::Position ([0.0, 0.0, 0.0].into()),
5145      bound:    shape::Orthant { normal_axis: SignedAxis3::NegZ }.into(),
5146      .. b
5147    };
5148    assert_eq!(
5149      Proximity::query (&a, &b),
5150      Proximity {
5151        distance:  -1.0,
5152        half_axis: [0.0, 0.0, -0.5].into(),
5153        normal:    Unit3::axis_z().invert(),
5154        midpoint:  [0.0, 0.0,  0.5].into()
5155      }
5156    );
5157
5158    // intersect: cuboid and orthant position coincide; normal +Y
5159    let a = object::Static {
5160      position: component::Position ([0.0, 0.0, 0.0].into()),
5161      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
5162      .. a
5163    };
5164    let b = object::Static {
5165      position: component::Position ([0.0, 0.0, 0.0].into()),
5166      bound:    shape::Orthant { normal_axis: SignedAxis3::PosY }.into(),
5167      .. b
5168    };
5169    assert_eq!(
5170      Proximity::query (&a, &b),
5171      Proximity {
5172        distance:  -1.0,
5173        half_axis: [0.0,  0.5, 0.0].into(),
5174        normal:    Unit3::axis_y(),
5175        midpoint:  [0.0, -0.5, 0.0].into()
5176      }
5177    );
5178
5179    // intersect: cuboid and orthant position coincide; normal -Y
5180    let a = object::Static {
5181      position: component::Position ([0.0, 0.0, 0.0].into()),
5182      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
5183      .. a
5184    };
5185    let b = object::Static {
5186      position: component::Position ([0.0, 0.0, 0.0].into()),
5187      bound:    shape::Orthant { normal_axis: SignedAxis3::NegY }.into(),
5188      .. b
5189    };
5190    assert_eq!(
5191      Proximity::query (&a, &b),
5192      Proximity {
5193        distance:  -1.0,
5194        half_axis: [0.0, -0.5, 0.0].into(),
5195        normal:    Unit3::axis_y().invert(),
5196        midpoint:  [0.0,  0.5, 0.0].into()
5197      }
5198    );
5199
5200    // intersect: cuboid and orthant position coincide; normal +X
5201    let a = object::Static {
5202      position: component::Position ([0.0, 0.0, 0.0].into()),
5203      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
5204      .. a
5205    };
5206    let b = object::Static {
5207      position: component::Position ([0.0, 0.0, 0.0].into()),
5208      bound:    shape::Orthant { normal_axis: SignedAxis3::PosX }.into(),
5209      .. b
5210    };
5211    assert_eq!(
5212      Proximity::query (&a, &b),
5213      Proximity {
5214        distance:  -1.0,
5215        half_axis: [ 0.5, 0.0, 0.0].into(),
5216        normal:    Unit3::axis_x(),
5217        midpoint:  [-0.5, 0.0, 0.0].into()
5218      }
5219    );
5220
5221    // intersect: cuboid and orthant position coincide; normal -X
5222    let a = object::Static {
5223      position: component::Position ([0.0, 0.0, 0.0].into()),
5224      bound:    component::Bound::from (shape::Cuboid::noisy ([1.0, 1.0, 1.0])),
5225      .. a
5226    };
5227    let b = object::Static {
5228      position: component::Position ([0.0, 0.0, 0.0].into()),
5229      bound:    shape::Orthant { normal_axis: SignedAxis3::NegX }.into(),
5230      .. b
5231    };
5232    assert_eq!(
5233      Proximity::query (&a, &b),
5234      Proximity {
5235        distance:  -1.0,
5236        half_axis: [-0.5, 0.0, 0.0].into(),
5237        normal:    Unit3::axis_x().invert(),
5238        midpoint:  [ 0.5, 0.0, 0.0].into()
5239      }
5240    );
5241
5242  } // end test_distance_query
5243
5244  #[test]
5245  #[expect(clippy::approx_constant)]
5246  fn query_hull() {
5247    // cuboid/hull
5248    let object_a = (
5249      component::Position ([0.0, 1.0, 1.6].into()),
5250      component::Bound::from (shape::Cuboid::noisy ([2.2, 2.2, 0.2]))
5251    );
5252    let object_b = {
5253      let hull = {
5254        let points = [
5255          [0.7333798838354574, 0.07387687099658402, -0.4254516842354885],
5256          [0.28934048979234384, -0.41943067939022566, 0.20302532977593885],
5257          [0.006562630606422983, 0.7345982182090985, 0.6784706255126608],
5258          [-0.16787537854609894, -0.7656664645854194, 0.004114727975213302],
5259          [-0.6400977561459589, -0.062079241531162725, -0.33763483879239625],
5260          [-0.3844054415702367, 0.6216012595122049, -0.04739397447380115]
5261        ].map (Point3::from);
5262        geometry::Hull3::from_points_with_mesh (&points).unwrap()
5263      };
5264      ( component::Position (
5265          [-0.021233076872148704, 1.4862487906056179, 2.2279516842354887].into()),
5266        component::Bound::from (hull)
5267      )
5268    };
5269    let proximity = Proximity::query (&object_a, &object_b);
5270    assert_eq!(proximity.distance, 0.0025000000000001688);
5271
5272    let object_a = {
5273      let hull = {
5274        let points = [
5275          [-1.6970562748477143, -2.2,  1.4142135623730951],
5276          [-1.4142135623730954, -2.2,  1.697056274847714],
5277          [-1.6970562748477143,  2.2,  1.4142135623730951],
5278          [-1.4142135623730954,  2.2,  1.697056274847714],
5279          [ 1.4142135623730954, -2.2, -1.697056274847714],
5280          [ 1.6970562748477143, -2.2, -1.4142135623730951],
5281          [ 1.4142135623730954,  2.2, -1.697056274847714],
5282          [ 1.6970562748477143,  2.2, -1.4142135623730951]
5283        ].map (Point3::from);
5284        geometry::Hull3::from_points_with_mesh (&points).unwrap()
5285      };
5286      ( component::Position ([-3.9, 1.0, 11.2125].into()),
5287        component::Bound::from (hull)
5288      )
5289    };
5290    let object_b = (
5291      component::Position (
5292        [-4.004858348343004, -1.2161640324940572, 11.869089780656527].into()),
5293      component::Bound::from (shape::Sphere::noisy (0.5))
5294    );
5295    let proximity = Proximity::query (&object_a, &object_b);
5296    assert_eq!(proximity.distance, -0.3091811079468084);
5297
5298    let object_a = {
5299      let hull = {
5300        let points = [
5301          [-2.2, -1.4142135623730954, -1.697056274847714],
5302          [-2.2, -1.6970562748477143, -1.4142135623730951],
5303          [-2.2,  1.6970562748477143,  1.4142135623730951],
5304          [-2.2,  1.4142135623730954,  1.697056274847714],
5305          [ 2.2, -1.4142135623730954, -1.697056274847714],
5306          [ 2.2, -1.6970562748477143, -1.4142135623730951],
5307          [ 2.2,  1.6970562748477143,  1.4142135623730951],
5308          [ 2.2,  1.4142135623730954,  1.69705627484771]
5309        ].map (Point3::from);
5310        geometry::Hull3::from_points_with_mesh (&points).unwrap()
5311      };
5312      ( component::Position ([0.0, 4.9, 11.2125].into()),
5313        component::Bound::from (hull)
5314      )
5315    };
5316    let object_b = (
5317      component::Position (
5318        [1.3113904930865399, 3.1609676773871804, 9.819471286970987].into()),
5319      component::Bound::from (shape::Sphere::noisy (0.5))
5320    );
5321    let proximity = Proximity::query (&object_a, &object_b);
5322    assert_eq!(proximity.distance, -0.4529809974587258);
5323
5324    let object_a = {
5325      let hull = {
5326        let points = [
5327          [-2.2, -1.6970562748477138,   1.4142135623730956],
5328          [-2.2, -1.414213562373095,    1.6970562748477145],
5329          [-2.2,  1.414213562373095,   -1.6970562748477145],
5330          [-2.2,  1.6970562748477138,  -1.4142135623730956],
5331          [ 2.2, -1.6970562748477138,   1.4142135623730956],
5332          [ 2.2, -1.414213562373095,    1.6970562748477145],
5333          [ 2.2,  1.414213562373095,   -1.6970562748477145],
5334          [ 2.2,  1.6970562748477138,  -1.4142135623730956]
5335        ].map (Point3::from);
5336        geometry::Hull3::from_points_with_mesh (&points).unwrap()
5337      };
5338      ( component::Position ([0.0, -2.9, 11.2125].into()),
5339        component::Bound::from (hull)
5340      )
5341    };
5342    let object_b = (
5343      component::Position (
5344        [2.256458188002921, -0.9674933916701636, 10.065811222437437].into()),
5345      component::Bound::from (shape::Sphere::noisy (0.5))
5346    );
5347    let proximity = Proximity::query (&object_a, &object_b);
5348    assert_eq!(proximity.distance, -0.13988958350691275);
5349
5350    let object_a = {
5351      let hull = {
5352        let points = [
5353          [-1.8000000000000003, -1.4142135623730954, -1.697056274847714 ],
5354          [-1.8000000000000003, -1.6970562748477143, -1.4142135623730951],
5355          [-1.8000000000000003,  1.6970562748477143,  1.4142135623730951],
5356          [-1.8000000000000003,  1.4142135623730954,  1.697056274847714 ],
5357          [ 1.8000000000000003, -1.4142135623730954, -1.697056274847714 ],
5358          [ 1.8000000000000003, -1.6970562748477143, -1.4142135623730951],
5359          [ 1.8000000000000003,  1.6970562748477143,  1.4142135623730951],
5360          [ 1.8000000000000003,  1.4142135623730954,  1.697056274847714 ]
5361        ].map (Point3::from);
5362        geometry::Hull3::from_points_with_mesh (&points).unwrap()
5363      };
5364      ( component::Position ([0.0, 4.5, 11.2125].into()),
5365        component::Bound::from (hull)
5366      )
5367    };
5368    let object_b = (
5369      component::Position (
5370        [-1.8483359321030037, 3.6499052772728637, 11.360649949330503].into()),
5371      component::Bound::from (shape::Sphere::noisy (0.5))
5372    );
5373    let proximity = Proximity::query (&object_a, &object_b);
5374    assert_eq!(proximity.distance, 0.008169602403322415);
5375  }
5376
5377  #[test]
5378  fn distance_to_triangle_edges() {
5379    let triangle = geometry::Triangle3::noisy (
5380      [-2.0, -1.0, 0.0].into(),
5381      [ 1.0, -1.0, 0.0].into(),
5382      [ 1.0,  5.0, 0.0].into()
5383    );
5384    // origin
5385    let s = 0.5;
5386    let t = 1.0 / 6.0;
5387    let (d0, d1, d2) = triangle_distance_to_edges (triangle, s, t);
5388    approx::assert_relative_eq!(d0, 1.0);
5389    assert_eq!(d1, 3.0 / 5.0.sqrt());
5390    assert_eq!(d2, 1.0);
5391  }
5392
5393} // end tests