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