Skip to main content

linear_sim/collision/
proximity.rs

1use geometry::shape;
2
3use crate::{collision, component, constraint, geometry, math, object};
4
5/// Relation configuration of a pair of bounded objects.
6///
7/// - 'Disjoint' corresponds to a positive distance `> CONTACT_DISTANCE`
8/// - 'Contact' corresponds to a positive distance `< CONTACT_DISTANCE`
9/// - 'Inersect' corresponds to a strictly negative contact distance `< 0.0`
10#[derive(Clone, Copy, Debug, Eq, PartialEq)]
11pub enum Relation {
12  Disjoint,
13  Contact,
14  Intersect
15}
16
17/// Result of a proximity query between a pair of bounded objects.
18///
19/// Note that the *axis vector* points from object A to object B, while the
20/// *normal vector* points from object B to object A, but they should be
21/// parallel when the axis vector is non-zero.
22#[derive(Clone, Debug, PartialEq)]
23pub struct Proximity {
24  /// Signed distance.
25  ///
26  /// A positive distance indicates the separating distance for a disjoint
27  /// result, and a negative distance indicates a penetration depth of an
28  /// intersection result.
29  pub distance  : f64,
30  /// Non-normalized vector pointing along the axis of separation or penetration
31  /// from object A to object B.
32  ///
33  /// For a disjoint configuration, adding this vector to the midpoint gives the
34  /// nearest point on object B and subtracting this vector from the midpoint
35  /// gives the nearest point on object A. Translating object A by this vector
36  /// and translating objet B by its inverse will bring the objects into
37  /// contact.
38  ///
39  /// For an intersecting configuration, translating object A by this vector and
40  /// translating object B by its inverse will resolve the inter-penetration.
41  ///
42  /// Note this vector can be zero if the objects are in contact.
43  pub half_axis : math::Vector3 <f64>,
44  /// Indicates the midpoint of the proximity query
45  pub midpoint  : math::Point3  <f64>,
46  /// The unit normal to the separating plane directed from object B to object
47  /// A
48  pub normal    : math::Unit3 <f64>
49}
50
51impl Proximity {
52  /// Proximity query.
53  ///
54  /// # Examples
55  ///
56  /// **Capsule v. capsule**
57  ///
58  /// By convention if the axes of capsules A and B intersect, the +X axis will
59  /// be chosen for the axis vector and normal:
60  ///
61  /// ```
62  /// # extern crate linear_sim;
63  /// # use linear_sim::*;
64  /// # use collision::*;
65  /// # use geometry::shape;
66  /// # use math;
67  /// # fn main() {
68  /// let a = object::Static {
69  ///   position:   component::Position ([0.0, 0.0, 1.0].into()),
70  ///   bound:      component::Bound (shape::Bounded::from (
71  ///     shape::Capsule::noisy (2.0, 3.0)).into()),
72  ///   material:   component::MATERIAL_STONE,
73  ///   collidable: true
74  /// };
75  /// let b = object::Static {
76  ///   position:   component::Position ([0.0, 0.0, -1.0].into()),
77  ///   bound:      component::Bound (shape::Bounded::from (
78  ///     shape::Capsule::noisy (1.0, 2.0)).into()),
79  ///   material:   component::MATERIAL_STONE,
80  ///   collidable: true
81  /// };
82  /// assert_eq!(
83  ///   Proximity::query (&a, &b),
84  ///   Proximity {
85  ///     distance:  -3.0,
86  ///     half_axis: [ 1.5, 0.0,  0.0].into(),
87  ///     normal:    math::Unit3::axis_x(),
88  ///     midpoint:  [-0.5, 0.0, -0.5].into()
89  ///   }
90  /// );
91  /// # }
92  /// ```
93  pub fn query <A, B> (object_a : &A, object_b : &B) -> Self where
94    A : object::Bounded, B : object::Bounded
95  {
96    let component::Position (position_a) = object_a.position();
97    let component::Position (position_b) = object_b.position();
98    let component::Bound    (shape_a)    = object_a.bound();
99    let component::Bound    (shape_b)    = object_b.bound();
100
101    match (shape_a, shape_b) {
102      //
103      // sphere vs. sphere
104      //
105      ( shape::Variant::Bounded (shape::Bounded::Sphere (sphere_a)),
106        shape::Variant::Bounded (shape::Bounded::Sphere (sphere_b))
107      ) => Self::query_sphere_sphere (
108        position_a, sphere_a, position_b, sphere_b),
109      //
110      // capsule vs. capsule
111      //
112      ( shape::Variant::Bounded (shape::Bounded::Capsule (capsule_a)),
113        shape::Variant::Bounded (shape::Bounded::Capsule (capsule_b))
114      ) => Self::query_capsule_capsule (
115        position_a, capsule_a, position_b, capsule_b),
116      //
117      // cuboid vs. cuboid
118      //
119      ( shape::Variant::Bounded (shape::Bounded::Cuboid (cuboid_a)),
120        shape::Variant::Bounded (shape::Bounded::Cuboid (cuboid_b))
121      ) => Self::query_cuboid_cuboid (
122        position_a, cuboid_a, position_b, cuboid_b),
123      //
124      // sphere vs. capsule
125      //
126      ( shape::Variant::Bounded (shape::Bounded::Sphere (sphere_a)),
127        shape::Variant::Bounded (shape::Bounded::Capsule (capsule_b))
128      ) => Self::query_sphere_capsule (
129        position_a, sphere_a, position_b, capsule_b),
130      ( shape::Variant::Bounded (shape::Bounded::Capsule (capsule_a)),
131        shape::Variant::Bounded (shape::Bounded::Sphere (sphere_b))
132      ) => {
133        let mut proximity = Self::query_sphere_capsule (
134          position_b, sphere_b, position_a, capsule_a);
135        proximity.half_axis *= -1.0;
136        proximity.normal    = proximity.normal.invert();
137        proximity
138      }
139      //
140      // sphere vs. cuboid
141      //
142      ( shape::Variant::Bounded (shape::Bounded::Sphere (sphere_a)),
143        shape::Variant::Bounded (shape::Bounded::Cuboid (cuboid_b))
144      ) => Self::query_sphere_cuboid (
145        position_a, sphere_a, position_b, cuboid_b),
146      ( shape::Variant::Bounded (shape::Bounded::Cuboid (cuboid_a)),
147        shape::Variant::Bounded (shape::Bounded::Sphere (sphere_b))
148      ) => {
149        let mut proximity = Self::query_sphere_cuboid (
150          position_b, sphere_b, position_a, cuboid_a);
151        proximity.half_axis *= -1.0;
152        proximity.normal    = proximity.normal.invert();
153        proximity
154      }
155      //
156      // sphere vs. orthant
157      //
158      ( shape::Variant::Bounded   (shape::Bounded::Sphere    (sphere_a)),
159        shape::Variant::Unbounded (shape::Unbounded::Orthant (orthant_b))
160      ) => Self::query_sphere_orthant (
161        position_a, sphere_a, position_b, orthant_b),
162      ( shape::Variant::Unbounded (shape::Unbounded::Orthant (orthant_a)),
163        shape::Variant::Bounded   (shape::Bounded::Sphere    (sphere_b))
164      ) => {
165        let mut proximity = Self::query_sphere_orthant (
166          position_b, sphere_b, position_a, orthant_a);
167        proximity.half_axis *= -1.0;
168        proximity.normal    = proximity.normal.invert();
169        proximity
170      }
171      //
172      // capsule vs. cuboid
173      //
174      ( shape::Variant::Bounded (shape::Bounded::Capsule (capsule_a)),
175        shape::Variant::Bounded (shape::Bounded::Cuboid  (cuboid_b))
176      ) => Self::query_capsule_cuboid (
177        position_a, capsule_a, position_b, cuboid_b),
178      ( shape::Variant::Bounded (shape::Bounded::Cuboid  (cuboid_a)),
179        shape::Variant::Bounded (shape::Bounded::Capsule (capsule_b))
180      ) => {
181        let mut proximity = Self::query_capsule_cuboid (
182          position_b, capsule_b, position_a, cuboid_a);
183        proximity.half_axis *= -1.0;
184        proximity.normal    = proximity.normal.invert();
185        proximity
186      }
187      //
188      // capsule vs. orthant
189      //
190      ( shape::Variant::Bounded   (shape::Bounded::Capsule   (capsule_a)),
191        shape::Variant::Unbounded (shape::Unbounded::Orthant (orthant_b))
192      ) => Self::query_capsule_orthant (
193        position_a, capsule_a, position_b, orthant_b),
194      ( shape::Variant::Unbounded (shape::Unbounded::Orthant (orthant_a)),
195        shape::Variant::Bounded   (shape::Bounded::Capsule   (capsule_b))
196      ) => {
197        let mut proximity = Self::query_capsule_orthant (
198          position_b, capsule_b, position_a, orthant_a);
199        proximity.half_axis *= -1.0;
200        proximity.normal    = proximity.normal.invert();
201        proximity
202      }
203      //
204      // cuboid vs. orthant
205      //
206      ( shape::Variant::Bounded   (shape::Bounded::Cuboid    (cuboid_a)),
207        shape::Variant::Unbounded (shape::Unbounded::Orthant (orthant_b))
208      ) => Self::query_cuboid_orthant (
209        position_a, cuboid_a, position_b, orthant_b),
210      ( shape::Variant::Unbounded (shape::Unbounded::Orthant (orthant_a)),
211        shape::Variant::Bounded   (shape::Bounded::Cuboid    (cuboid_b))
212      ) => {
213        let mut proximity = Self::query_cuboid_orthant (
214          position_b, cuboid_b, position_a, orthant_a);
215        proximity.half_axis *= -1.0;
216        proximity.normal    = proximity.normal.invert();
217        proximity
218      }
219      _ => unimplemented!("proximity query not implemented for (A, B): {:?}",
220        (shape_a, shape_b))
221    }
222  }
223
224  pub fn relation (&self) -> Relation {
225    if self.distance < 0.0 {
226      Relation::Intersect
227    } else if self.distance <= collision::CONTACT_DISTANCE {
228      Relation::Contact
229    } else {
230      debug_assert!(self.distance > collision::CONTACT_DISTANCE);
231      Relation::Disjoint
232    }
233  }
234
235  #[inline]
236  pub fn constraint_planar (&self) -> constraint::Planar {
237    constraint::Planar::new (self.midpoint, self.normal)
238  }
239
240  pub fn query_sphere_sphere (
241    position_a : &math::Point3 <f64>, sphere_a : &shape::Sphere <f64>,
242    position_b : &math::Point3 <f64>, sphere_b : &shape::Sphere <f64>
243  ) -> Self {
244    use math::num_traits::Zero;
245    let (radius_a, radius_b) = (*sphere_a.radius, *sphere_b.radius);
246    let distance_normal_half_axis = |axis : &math::Vector3 <f64>| {
247      let axis_distance = axis.magnitude();
248      let distance      = axis_distance - radius_a - radius_b;
249      let normal        = if axis.is_zero() {
250        math::Unit3::axis_x()
251      } else {
252        math::Unit3::unchecked_approx (-(axis / axis_distance))
253      };
254      let half_axis     = -0.5 * distance * *normal;
255      (distance, normal, half_axis)
256    };
257    let axis     = position_b - position_a;
258    let (distance, normal, half_axis) = distance_normal_half_axis (&axis);
259    let midpoint = position_a - radius_a * *normal + half_axis;
260    Proximity { distance, half_axis, midpoint, normal }
261  }
262
263  pub fn query_capsule_capsule (
264    position_a : &math::Point3 <f64>, capsule_a : &shape::Capsule <f64>,
265    position_b : &math::Point3 <f64>, capsule_b : &shape::Capsule <f64>
266  ) -> Self {
267    use math::num_traits::Zero;
268    let (half_height_a, half_height_b) =
269      (*capsule_a.half_height, *capsule_b.half_height);
270    let (radius_a, radius_b) = (*capsule_a.radius, *capsule_b.radius);
271    let shaft_max_a = position_a + math::Vector3::new (0.0, 0.0, half_height_a);
272    let shaft_min_a = position_a - math::Vector3::new (0.0, 0.0, half_height_a);
273    let shaft_max_b = position_b + math::Vector3::new (0.0, 0.0, half_height_b);
274    let shaft_min_b = position_b - math::Vector3::new (0.0, 0.0, half_height_b);
275    let distance_normal_half_axis = |axis : &math::Vector3 <f64>| {
276      let axis_distance = axis.magnitude();
277      let distance      = axis_distance - radius_a - radius_b;
278      let normal        = if axis.is_zero() {
279        math::Unit3::axis_x()
280      } else {
281        math::Unit3::unchecked_approx (-(axis / axis_distance))
282      };
283      let half_axis     = -0.5 * distance * *normal;
284      (distance, normal, half_axis)
285    };
286    if shaft_max_a.0.z <= shaft_min_b.0.z {
287      // cylinder shaft of B is above cylinder shaft of A
288      let axis     = shaft_min_b - shaft_max_a;
289      let (distance, normal, half_axis) = distance_normal_half_axis (&axis);
290      let midpoint = shaft_max_a - radius_a * *normal + half_axis;
291      Proximity { distance, half_axis, midpoint, normal }
292    } else if shaft_max_b.0.z <= shaft_min_a.0.z {
293      // cylinder shaft of B is below cylinder shaft of A
294      let axis     = shaft_max_b.0 - shaft_min_a.0;
295      let (distance, normal, half_axis) = distance_normal_half_axis (&axis);
296      let midpoint = shaft_min_a - radius_a * *normal + half_axis;
297      Proximity { distance, half_axis, midpoint, normal }
298    } else {
299      // cylinder shafts are adjacent
300      let axis     =
301        math::Point3::from ([position_b.0.x, position_b.0.y, 0.0]) -
302        math::Point3::from ([position_a.0.x, position_a.0.y, 0.0]);
303      let (distance, normal, half_axis) = distance_normal_half_axis (&axis);
304      let midpoint = {
305        let max_z  = f64::min (shaft_max_a.0.z, shaft_max_b.0.z);
306        let min_z  = f64::max (shaft_min_a.0.z, shaft_min_b.0.z);
307        let mid_z  = 0.5 * (max_z + min_z);
308        let mid_a  = position_a - radius_a * *normal + half_axis;
309        [mid_a.0.x, mid_a.0.y, mid_z].into()
310      };
311      Proximity { distance, half_axis, midpoint, normal }
312    }
313  }
314
315  pub fn query_cuboid_cuboid (
316    position_a : &math::Point3 <f64>, cuboid_a : &shape::Cuboid <f64>,
317    position_b : &math::Point3 <f64>, cuboid_b : &shape::Cuboid <f64>
318  ) -> Self {
319    use math::VectorSpace;
320    use math::num_traits::Zero;
321    fn nearest_ab_to_distance (
322      nearest_a      : &math::Point3  <f64>,
323      nearest_b      : &math::Point3  <f64>,
324      default_normal : &math::Unit3 <f64>
325    ) -> Proximity {
326      let axis      = nearest_b - nearest_a;
327      let distance  = axis.magnitude();
328      let half_axis = 0.5 * (nearest_b - nearest_a);
329      let midpoint  = nearest_a + half_axis;
330      let normal    = if axis.is_zero() {
331        *default_normal
332      } else {
333        math::Unit3::unchecked_approx (-axis / distance)
334      };
335      Proximity { distance, half_axis, midpoint, normal }
336    }
337
338    let max_a = position_a + cuboid_a.max().0;
339    let min_a = position_a + cuboid_a.min().0;
340    let max_b = position_b + cuboid_b.max().0;
341    let min_b = position_b + cuboid_b.min().0;
342    let overlap_x = min_b.0.x < max_a.0.x && min_a.0.x < max_b.0.x;
343    let overlap_y = min_b.0.y < max_a.0.y && min_a.0.y < max_b.0.y;
344    let overlap_z = min_b.0.z < max_a.0.z && min_a.0.z < max_b.0.z;
345    match (overlap_x, overlap_y, overlap_z) {
346      (false, false, false) => {
347        // no overlaps: nearest features are corners
348        let a_to_b = position_b - position_a;
349        debug_assert!(!a_to_b.x.is_zero());
350        debug_assert!(!a_to_b.y.is_zero());
351        debug_assert!(!a_to_b.z.is_zero());
352        let a_to_b_sigvec = a_to_b.map (f64::signum);
353        let nearest_a = position_a + cuboid_a.max().0 * a_to_b_sigvec;
354        let nearest_b = position_b + cuboid_b.max().0 * (-a_to_b_sigvec);
355        nearest_ab_to_distance (&nearest_a, &nearest_b,
356          &math::Unit3::normalize (-a_to_b_sigvec))
357      }
358      (true, false, false)  => {
359        // overlap x only: nearest features are edges
360        let max_x  = f64::min (max_a.0.x, max_b.0.x);
361        let min_x  = f64::max (min_a.0.x, min_b.0.x);
362        debug_assert_ne!(min_x, max_x);
363        let mid_x  = 0.5 * (min_x + max_x);
364        let a_to_b = position_b - position_a;
365        let a_to_b_yz = math::Vector3::new (0.0, a_to_b.y, a_to_b.z);
366        let a_to_b_yz_sigvec = math::Vector3::sigvec (a_to_b_yz);
367        debug_assert!(a_to_b_yz_sigvec.x.is_zero());
368        debug_assert!(!a_to_b_yz_sigvec.y.is_zero());
369        debug_assert!(!a_to_b_yz_sigvec.z.is_zero());
370        let nearest_a = {
371          let y = *cuboid_a.half_extent_y * a_to_b_yz_sigvec.y;
372          let z = *cuboid_a.half_extent_z * a_to_b_yz_sigvec.z;
373          [mid_x, position_a.0.y + y, position_a.0.z + z].into()
374        };
375        let nearest_b = {
376          let y = -*cuboid_b.half_extent_y * a_to_b_yz_sigvec.y;
377          let z = -*cuboid_b.half_extent_z * a_to_b_yz_sigvec.z;
378          [mid_x, position_b.0.y + y, position_b.0.z + z].into()
379        };
380        nearest_ab_to_distance (&nearest_a, &nearest_b,
381          &math::Unit3::normalize (-a_to_b_yz_sigvec))
382      }
383      (false, true, false)  => {
384        // overlap y only: nearest features are edges
385        let max_y  = f64::min (max_a.0.y, max_b.0.y);
386        let min_y  = f64::max (min_a.0.y, min_b.0.y);
387        debug_assert_ne!(min_y, max_y);
388        let mid_y  = 0.5 * (min_y + max_y);
389        let a_to_b = position_b - position_a;
390        let a_to_b_xz = math::Vector3::new (a_to_b.x, 0.0, a_to_b.z);
391        let a_to_b_xz_sigvec = math::Vector3::sigvec (a_to_b_xz);
392        debug_assert!(!a_to_b_xz_sigvec.x.is_zero());
393        debug_assert!(a_to_b_xz_sigvec.y.is_zero());
394        debug_assert!(!a_to_b_xz_sigvec.z.is_zero());
395        let nearest_a = {
396          let x = *cuboid_a.half_extent_x * a_to_b_xz_sigvec.x;
397          let z = *cuboid_a.half_extent_z * a_to_b_xz_sigvec.z;
398          [position_a.0.x + x, mid_y, position_a.0.z + z].into()
399        };
400        let nearest_b = {
401          let x = -*cuboid_b.half_extent_x * a_to_b_xz_sigvec.x;
402          let z = -*cuboid_b.half_extent_z * a_to_b_xz_sigvec.z;
403          [position_b.0.x + x, mid_y, position_b.0.z + z].into()
404        };
405        nearest_ab_to_distance (&nearest_a, &nearest_b,
406          &math::Unit3::normalize (-a_to_b_xz_sigvec))
407      }
408      (false, false, true)  => {
409        // overlap z only: nearest features are edges
410        let max_z  = f64::min (max_a.0.z, max_b.0.z);
411        let min_z  = f64::max (min_a.0.z, min_b.0.z);
412        debug_assert_ne!(min_z, max_z);
413        let mid_z  = 0.5 * (min_z + max_z);
414        let a_to_b = position_b - position_a;
415        let a_to_b_xy = math::Vector3::new (a_to_b.x, a_to_b.y, 0.0);
416        let a_to_b_xy_sigvec = math::Vector3::sigvec (a_to_b_xy);
417        debug_assert!(!a_to_b_xy_sigvec.x.is_zero());
418        debug_assert!(!a_to_b_xy_sigvec.y.is_zero());
419        debug_assert!(a_to_b_xy_sigvec.z.is_zero());
420        let nearest_a = {
421          let x = *cuboid_a.half_extent_x * a_to_b_xy_sigvec.x;
422          let y = *cuboid_a.half_extent_y * a_to_b_xy_sigvec.y;
423          [position_a.0.x + x, position_a.0.y + y, mid_z].into()
424        };
425        let nearest_b = {
426          let x = -*cuboid_b.half_extent_x * a_to_b_xy_sigvec.x;
427          let y = -*cuboid_b.half_extent_y * a_to_b_xy_sigvec.y;
428          [position_b.0.x + x, position_b.0.y + y, mid_z].into()
429        };
430        nearest_ab_to_distance (&nearest_a, &nearest_b,
431          &math::Unit3::normalize (-a_to_b_xy_sigvec))
432      }
433      (true, true, false)   => {
434        // overlap x and y: nearest features are faces
435        let max_x    = f64::min (max_a.0.x, max_b.0.x);
436        let min_x    = f64::max (min_a.0.x, min_b.0.x);
437        let mid_x    = 0.5 * (min_x + max_x);
438        let max_y    = f64::min (max_a.0.y, max_b.0.y);
439        let min_y    = f64::max (min_a.0.y, min_b.0.y);
440        let mid_y    = 0.5 * (min_y + max_y);
441        let a_to_b_z = position_b.0.z - position_a.0.z;
442        let a_to_b_signum_z = a_to_b_z.signum();
443        debug_assert_ne!(a_to_b_signum_z, 0.0);
444        let nearest_a = math::Point3::new (
445          mid_x, mid_y,
446          position_a.0.z + a_to_b_signum_z * *cuboid_a.half_extent_z
447        );
448        let nearest_b = math::Point3::new (
449          mid_x, mid_y,
450          position_b.0.z - a_to_b_signum_z * *cuboid_b.half_extent_z
451        );
452        let nearest_a_to_b_z = nearest_b.0.z - nearest_a.0.z;
453        let distance  = nearest_a_to_b_z.abs();
454        let half_axis = [0.0, 0.0, 0.5 * nearest_a_to_b_z].into();
455        let normal    = math::Unit3::unchecked (
456          [0.0, 0.0, -a_to_b_signum_z].into());
457        let midpoint  = nearest_a + half_axis;
458        Proximity { distance, half_axis, midpoint, normal }
459      }
460      (true, false, true)   => {
461        // overlap x and z: nearest features are faces
462        let max_x    = f64::min (max_a.0.x, max_b.0.x);
463        let min_x    = f64::max (min_a.0.x, min_b.0.x);
464        let mid_x    = 0.5 * (min_x + max_x);
465        let max_z    = f64::min (max_a.0.z, max_b.0.z);
466        let min_z    = f64::max (min_a.0.z, min_b.0.z);
467        let mid_z    = 0.5 * (min_z + max_z);
468        let a_to_b_y = position_b.0.y - position_a.0.y;
469        let a_to_b_signum_y = a_to_b_y.signum();
470        debug_assert_ne!(a_to_b_signum_y, 0.0);
471        let nearest_a = math::Point3::new (
472          mid_x,
473          position_a.0.y + a_to_b_signum_y * *cuboid_a.half_extent_y,
474          mid_z
475        );
476        let nearest_b = math::Point3::new (
477          mid_x,
478          position_b.0.y - a_to_b_signum_y * *cuboid_b.half_extent_y,
479          mid_z
480        );
481        let nearest_a_to_b_y = nearest_b.0.y - nearest_a.0.y;
482        let distance  = nearest_a_to_b_y.abs();
483        let half_axis = [0.0, 0.5 * nearest_a_to_b_y, 0.0].into();
484        let normal    = math::Unit3::unchecked (
485          [0.0, -a_to_b_signum_y, 0.0].into());
486        let midpoint  = nearest_a + half_axis;
487        Proximity { distance, half_axis, midpoint, normal }
488      }
489      (false, true, true)   => {
490        // overlap y and z: nearest features are faces
491        let max_y    = f64::min (max_a.0.y, max_b.0.y);
492        let min_y    = f64::max (min_a.0.y, min_b.0.y);
493        let mid_y    = 0.5 * (min_y + max_y);
494        let max_z    = f64::min (max_a.0.z, max_b.0.z);
495        let min_z    = f64::max (min_a.0.z, min_b.0.z);
496        let mid_z    = 0.5 * (min_z + max_z);
497        let a_to_b_x = position_b.0.x - position_a.0.x;
498        let a_to_b_signum_x = a_to_b_x.signum();
499        debug_assert_ne!(a_to_b_signum_x, 0.0);
500        let nearest_a = math::Point3::new (
501          position_a.0.x + a_to_b_signum_x * *cuboid_a.half_extent_x,
502          mid_y,
503          mid_z
504        );
505        let nearest_b = math::Point3::new (
506          position_b.0.x - a_to_b_signum_x * *cuboid_b.half_extent_x,
507          mid_y,
508          mid_z
509        );
510        let nearest_a_to_b_x = nearest_b.0.x - nearest_a.0.x;
511        let distance  = nearest_a_to_b_x.abs();
512        let half_axis = [0.5 * nearest_a_to_b_x, 0.0, 0.0].into();
513        let normal    = math::Unit3::unchecked (
514          [-a_to_b_signum_x, 0.0, 0.0].into());
515        let midpoint  = nearest_a + half_axis;
516        Proximity { distance, half_axis, midpoint, normal }
517      }
518      (true, true, true)    => {
519        // overlap on all axes: intersection
520        let max_x     = f64::min (max_a.0.x, max_b.0.x);
521        let min_x     = f64::max (min_a.0.x, min_b.0.x);
522        let mid_x     = 0.5 * (min_x + max_x);
523        let max_y     = f64::min (max_a.0.y, max_b.0.y);
524        let min_y     = f64::max (min_a.0.y, min_b.0.y);
525        let mid_y     = 0.5 * (min_y + max_y);
526        let max_z     = f64::min (max_a.0.z, max_b.0.z);
527        let min_z     = f64::max (min_a.0.z, min_b.0.z);
528        let mid_z     = 0.5 * (min_z + max_z);
529        let midpoint  = [mid_x, mid_y, mid_z].into();
530        let resolve_x = if position_a.0.x < position_b.0.x {
531          min_b.0.x - max_a.0.x
532        } else {
533          debug_assert!(position_a.0.x >= position_b.0.x);
534          max_b.0.x - min_a.0.x
535        };
536        let resolve_y = if position_a.0.y < position_b.0.y {
537          min_b.0.y - max_a.0.y
538        } else {
539          debug_assert!(position_a.0.y >= position_b.0.y);
540          max_b.0.y - min_a.0.y
541        };
542        let resolve_z = if position_a.0.z < position_b.0.z {
543          min_b.0.z - max_a.0.z
544        } else {
545          debug_assert!(position_a.0.z >= position_b.0.z);
546          max_b.0.z - min_a.0.z
547        };
548        let resolve_x_abs = resolve_x.abs();
549        let resolve_y_abs = resolve_y.abs();
550        let resolve_z_abs = resolve_z.abs();
551        let (distance, half_axis, normal) : (
552          f64, math::Vector3 <f64>, math::Unit3 <f64>
553        ) = if resolve_x_abs <= resolve_y_abs && resolve_x_abs <= resolve_z_abs {
554          ( -resolve_x_abs,
555            0.5 * math::Vector3::new (resolve_x, 0.0, 0.0),
556            math::Unit3::unchecked ([resolve_x.signum(), 0.0, 0.0].into())
557          )
558        } else if
559          resolve_y_abs <= resolve_x_abs && resolve_y_abs <= resolve_z_abs
560        {
561          ( -resolve_y_abs,
562            0.5 * math::Vector3::new (0.0, resolve_y, 0.0),
563            math::Unit3::unchecked ([0.0, resolve_y.signum(), 0.0].into())
564          )
565        } else {
566          debug_assert!(resolve_z_abs <= resolve_x_abs);
567          debug_assert!(resolve_z_abs <= resolve_y_abs);
568          ( -resolve_z_abs,
569            0.5 * math::Vector3::new (0.0, 0.0, resolve_z),
570            math::Unit3::unchecked ([0.0, 0.0, resolve_z.signum()].into())
571          )
572        };
573        debug_assert!(!half_axis.is_zero());
574        Proximity { distance, half_axis, midpoint, normal }
575      }
576    }
577  }
578
579  /*
580  /// Uses pre-computed sigvec normals with array lookup.
581  ///
582  /// This benchmarked 2-3ns slower than the version using `normalize`.
583  pub fn query_cuboid_cuboid_array (
584    position_a : &math::Point3 <f64>, cuboid_a : &shape::Cuboid <f64>,
585    position_b : &math::Point3 <f64>, cuboid_b : &shape::Cuboid <f64>
586  ) -> Self {
587    use math::{EuclideanSpace, InnerSpace, Zero};
588
589    fn nearest_ab_to_distance (
590      nearest_a      : &math::Point3  <f64>,
591      nearest_b      : &math::Point3  <f64>,
592      default_normal : &math::Vector3 <f64>
593    ) -> Proximity {
594      let axis      = nearest_b - nearest_a;
595      let distance  = axis.magnitude();
596      let half_axis = 0.5 * (nearest_b - nearest_a);
597      let midpoint  = nearest_a + half_axis;
598      let normal    = if axis.is_zero() {
599        *default_normal
600      } else {
601        -axis / distance
602      };
603      Proximity { distance, half_axis, midpoint, normal }
604    }
605
606    let max_a = position_a + cuboid_a.max().0;
607    let min_a = position_a + cuboid_a.min().0;
608    let max_b = position_b + cuboid_b.max().0;
609    let min_b = position_b + cuboid_b.min().0;
610    let overlap_x = min_b.0.x < max_a.0.x && min_a.0.x < max_b.0.x;
611    let overlap_y = min_b.0.y < max_a.0.y && min_a.0.y < max_b.0.y;
612    let overlap_z = min_b.0.z < max_a.0.z && min_a.0.z < max_b.0.z;
613    match (overlap_x, overlap_y, overlap_z) {
614      (false, false, false) => {
615        use math::ElementWise;
616        // no overlaps: nearest features are corners
617        let a_to_b = position_b - position_a;
618        debug_assert!(!a_to_b.x.is_zero());
619        debug_assert!(!a_to_b.y.is_zero());
620        debug_assert!(!a_to_b.z.is_zero());
621        let a_to_b_sigvec = a_to_b.map (f64::signum);
622        let nearest_a = position_a +
623          cuboid_a.half_extents().mul_element_wise (a_to_b_sigvec);
624        let nearest_b = position_b +
625          cuboid_b.half_extents().mul_element_wise (-a_to_b_sigvec);
626        nearest_ab_to_distance (&nearest_a, &nearest_b,
627          &-math::sigvec_unit_f64_unchecked (a_to_b_sigvec))
628      }
629      (true, false, false)  => {
630        // overlap x only: nearest features are edges
631        let max_x  = f64::min (max_a.0.x, max_b.0.x);
632        let min_x  = f64::max (min_a.0.x, min_b.0.x);
633        debug_assert_ne!(min_x, max_x);
634        let mid_x  = 0.5 * (min_x + max_x);
635        let a_to_b = position_b - position_a;
636        let a_to_b_yz = math::Vector3::new (0.0, a_to_b.y, a_to_b.z);
637        let a_to_b_yz_sigvec = math::sigvec (a_to_b_yz);
638        debug_assert!(a_to_b_yz_sigvec.x.is_zero());
639        debug_assert!(!a_to_b_yz_sigvec.y.is_zero());
640        debug_assert!(!a_to_b_yz_sigvec.z.is_zero());
641        let nearest_a = {
642          let y = cuboid_a.half_extent_y * a_to_b_yz_sigvec.y;
643          let z = cuboid_a.half_extent_z * a_to_b_yz_sigvec.z;
644          [mid_x, position_a.0.y + y, position_a.0.z + z].into()
645        };
646        let nearest_b = {
647          let y = -cuboid_b.half_extent_y * a_to_b_yz_sigvec.y;
648          let z = -cuboid_b.half_extent_z * a_to_b_yz_sigvec.z;
649          [mid_x, position_b.0.y + y, position_b.0.z + z].into()
650        };
651        nearest_ab_to_distance (&nearest_a, &nearest_b,
652          &-math::sigvec_unit_f64_unchecked (a_to_b_yz_sigvec))
653      }
654      (false, true, false)  => {
655        // overlap y only: nearest features are edges
656        let max_y  = f64::min (max_a.0.y, max_b.0.y);
657        let min_y  = f64::max (min_a.0.y, min_b.0.y);
658        debug_assert_ne!(min_y, max_y);
659        let mid_y  = 0.5 * (min_y + max_y);
660        let a_to_b = position_b - position_a;
661        let a_to_b_xz = math::Vector3::new (a_to_b.x, 0.0, a_to_b.z);
662        let a_to_b_xz_sigvec = math::sigvec (a_to_b_xz);
663        debug_assert!(!a_to_b_xz_sigvec.x.is_zero());
664        debug_assert!(a_to_b_xz_sigvec.y.is_zero());
665        debug_assert!(!a_to_b_xz_sigvec.z.is_zero());
666        let nearest_a = {
667          let x = cuboid_a.half_extent_x * a_to_b_xz_sigvec.x;
668          let z = cuboid_a.half_extent_z * a_to_b_xz_sigvec.z;
669          [position_a.0.x + x, mid_y, position_a.0.z + z].into()
670        };
671        let nearest_b = {
672          let x = -cuboid_b.half_extent_x * a_to_b_xz_sigvec.x;
673          let z = -cuboid_b.half_extent_z * a_to_b_xz_sigvec.z;
674          [position_b.0.x + x, mid_y, position_b.0.z + z].into()
675        };
676        nearest_ab_to_distance (&nearest_a, &nearest_b,
677          &-math::sigvec_unit_f64_unchecked (a_to_b_xz_sigvec))
678      }
679      (false, false, true)  => {
680        // overlap z only: nearest features are edges
681        let max_z  = f64::min (max_a.0.z, max_b.0.z);
682        let min_z  = f64::max (min_a.0.z, min_b.0.z);
683        debug_assert_ne!(min_z, max_z);
684        let mid_z  = 0.5 * (min_z + max_z);
685        let a_to_b = position_b - position_a;
686        let a_to_b_xy = math::Vector3::new (a_to_b.x, a_to_b.y, 0.0);
687        let a_to_b_xy_sigvec = math::sigvec (a_to_b_xy);
688        debug_assert!(!a_to_b_xy_sigvec.x.is_zero());
689        debug_assert!(!a_to_b_xy_sigvec.y.is_zero());
690        debug_assert!(a_to_b_xy_sigvec.z.is_zero());
691        let nearest_a = {
692          let x = cuboid_a.half_extent_x * a_to_b_xy_sigvec.x;
693          let y = cuboid_a.half_extent_y * a_to_b_xy_sigvec.y;
694          [position_a.0.x + x, position_a.0.y + y, mid_z].into()
695        };
696        let nearest_b = {
697          let x = -cuboid_b.half_extent_x * a_to_b_xy_sigvec.x;
698          let y = -cuboid_b.half_extent_y * a_to_b_xy_sigvec.y;
699          [position_b.0.x + x, position_b.0.y + y, mid_z].into()
700        };
701        nearest_ab_to_distance (&nearest_a, &nearest_b,
702          &-math::sigvec_unit_f64_unchecked (a_to_b_xy_sigvec))
703      }
704      (true, true, false)   => {
705        // overlap x and y: nearest features are faces
706        let max_x    = f64::min (max_a.0.x, max_b.0.x);
707        let min_x    = f64::max (min_a.0.x, min_b.0.x);
708        let mid_x    = 0.5 * (min_x + max_x);
709        let max_y    = f64::min (max_a.0.y, max_b.0.y);
710        let min_y    = f64::max (min_a.0.y, min_b.0.y);
711        let mid_y    = 0.5 * (min_y + max_y);
712        let a_to_b_z = position_b.0.z - position_a.0.z;
713        let a_to_b_signum_z = a_to_b_z.signum();
714        debug_assert_ne!(a_to_b_signum_z, 0.0);
715        let nearest_a = math::Point3::new (
716          mid_x, mid_y,
717          position_a.0.z + a_to_b_signum_z * cuboid_a.half_extent_z
718        );
719        let nearest_b = math::Point3::new (
720          mid_x, mid_y,
721          position_b.0.z - a_to_b_signum_z * cuboid_b.half_extent_z
722        );
723        let nearest_a_to_b_z = nearest_b.0.z - nearest_a.0.z;
724        let distance  = nearest_a_to_b_z.abs();
725        let half_axis = [0.0, 0.0, 0.5 * nearest_a_to_b_z].into();
726        let normal    = [0.0, 0.0, -a_to_b_signum_z].into();
727        let midpoint  = nearest_a + half_axis;
728        Proximity { distance, half_axis, midpoint, normal }
729      }
730      (true, false, true)   => {
731        // overlap x and z: nearest features are faces
732        let max_x    = f64::min (max_a.0.x, max_b.0.x);
733        let min_x    = f64::max (min_a.0.x, min_b.0.x);
734        let mid_x    = 0.5 * (min_x + max_x);
735        let max_z    = f64::min (max_a.0.z, max_b.0.z);
736        let min_z    = f64::max (min_a.0.z, min_b.0.z);
737        let mid_z    = 0.5 * (min_z + max_z);
738        let a_to_b_y = position_b.0.y - position_a.0.y;
739        let a_to_b_signum_y = a_to_b_y.signum();
740        debug_assert_ne!(a_to_b_signum_y, 0.0);
741        let nearest_a = math::Point3::new (
742          mid_x,
743          position_a.0.y + a_to_b_signum_y * cuboid_a.half_extent_y,
744          mid_z
745        );
746        let nearest_b = math::Point3::new (
747          mid_x,
748          position_b.0.y - a_to_b_signum_y * cuboid_b.half_extent_y,
749          mid_z
750        );
751        let nearest_a_to_b_y = nearest_b.0.y - nearest_a.0.y;
752        let distance  = nearest_a_to_b_y.abs();
753        let half_axis = [0.0, 0.5 * nearest_a_to_b_y, 0.0].into();
754        let normal    = [0.0, -a_to_b_signum_y, 0.0].into();
755        let midpoint  = nearest_a + half_axis;
756        Proximity { distance, half_axis, midpoint, normal }
757      }
758      (false, true, true)   => {
759        // overlap y and z: nearest features are faces
760        let max_y    = f64::min (max_a.0.y, max_b.0.y);
761        let min_y    = f64::max (min_a.0.y, min_b.0.y);
762        let mid_y    = 0.5 * (min_y + max_y);
763        let max_z    = f64::min (max_a.0.z, max_b.0.z);
764        let min_z    = f64::max (min_a.0.z, min_b.0.z);
765        let mid_z    = 0.5 * (min_z + max_z);
766        let a_to_b_x = position_b.0.x - position_a.0.x;
767        let a_to_b_signum_x = a_to_b_x.signum();
768        debug_assert_ne!(a_to_b_signum_x, 0.0);
769        let nearest_a = math::Point3::new (
770          position_a.0.x + a_to_b_signum_x * cuboid_a.half_extent_x,
771          mid_y,
772          mid_z
773        );
774        let nearest_b = math::Point3::new (
775          position_b.0.x - a_to_b_signum_x * cuboid_b.half_extent_x,
776          mid_y,
777          mid_z
778        );
779        let nearest_a_to_b_x = nearest_b.0.x - nearest_a.0.x;
780        let distance  = nearest_a_to_b_x.abs();
781        let half_axis = [0.5 * nearest_a_to_b_x, 0.0, 0.0].into();
782        let normal    = [-a_to_b_signum_x, 0.0, 0.0].into();
783        let midpoint  = nearest_a + half_axis;
784        Proximity { distance, half_axis, midpoint, normal }
785      }
786      (true, true, true)    => {
787        // overlap on all axes: intersection
788        let max_x     = f64::min (max_a.0.x, max_b.0.x);
789        let min_x     = f64::max (min_a.0.x, min_b.0.x);
790        let mid_x     = 0.5 * (min_x + max_x);
791        let max_y     = f64::min (max_a.0.y, max_b.0.y);
792        let min_y     = f64::max (min_a.0.y, min_b.0.y);
793        let mid_y     = 0.5 * (min_y + max_y);
794        let max_z     = f64::min (max_a.0.z, max_b.0.z);
795        let min_z     = f64::max (min_a.0.z, min_b.0.z);
796        let mid_z     = 0.5 * (min_z + max_z);
797        let midpoint  = [mid_x, mid_y, mid_z].into();
798        let resolve_x = if position_a.0.x < position_b.0.x {
799          min_b.0.x - max_a.0.x
800        } else {
801          debug_assert!(position_a.0.x >= position_b.0.x);
802          max_b.0.x - min_a.0.x
803        };
804        let resolve_y = if position_a.0.y < position_b.0.y {
805          min_b.0.y - max_a.0.y
806        } else {
807          debug_assert!(position_a.0.y >= position_b.0.y);
808          max_b.0.y - min_a.0.y
809        };
810        let resolve_z = if position_a.0.z < position_b.0.z {
811          min_b.0.z - max_a.0.z
812        } else {
813          debug_assert!(position_a.0.z >= position_b.0.z);
814          max_b.0.z - min_a.0.z
815        };
816        let resolve_x_abs = resolve_x.abs();
817        let resolve_y_abs = resolve_y.abs();
818        let resolve_z_abs = resolve_z.abs();
819        let (distance, half_axis, normal) : (
820          f64, math::Vector3 <f64>, math::Vector3 <f64>
821        ) = if resolve_x_abs <= resolve_y_abs && resolve_x_abs <= resolve_z_abs {
822          ( -resolve_x_abs,
823            0.5 * math::Vector3::new (resolve_x, 0.0, 0.0),
824            [resolve_x.signum(), 0.0, 0.0].into()
825          )
826        } else if
827          resolve_y_abs <= resolve_x_abs && resolve_y_abs <= resolve_z_abs
828        {
829          ( -resolve_y_abs,
830            0.5 * math::Vector3::new (0.0, resolve_y, 0.0),
831            [0.0, resolve_y.signum(), 0.0].into()
832          )
833        } else {
834          debug_assert!(resolve_z_abs <= resolve_x_abs);
835          debug_assert!(resolve_z_abs <= resolve_y_abs);
836          ( -resolve_z_abs,
837            0.5 * math::Vector3::new (0.0, 0.0, resolve_z),
838            [0.0, 0.0, resolve_z.signum()].into()
839          )
840        };
841        debug_assert!(!half_axis.is_zero());
842        Proximity { distance, half_axis, midpoint, normal }
843      }
844    }
845  } // end query_cuboid_cuboid_array
846
847  /// This is a version of the cuboid distance query with the three edge
848  /// cases factored out into a function taking vector indices. This turns out
849  /// to be slower in benchmarks (190ns vs. the 180ns inline versions above).
850  pub fn query_cuboid_cuboid_refactor (
851    position_a : &math::Point3 <f64>, cuboid_a : &shape::Cuboid <f64>,
852    position_b : &math::Point3 <f64>, cuboid_b : &shape::Cuboid <f64>
853  ) -> Self {
854    use math::{EuclideanSpace, InnerSpace, Zero};
855
856    #[inline]
857    fn distance_edge (
858      edge : usize, other1 : usize, other2 : usize,
859      position_a : &math::Point3 <f64>, cuboid_a : &shape::Cuboid <f64>,
860      position_b : &math::Point3 <f64>, cuboid_b : &shape::Cuboid <f64>,
861      max_a      : &math::Point3 <f64>, min_a    : &math::Point3 <f64>,
862      max_b      : &math::Point3 <f64>, min_b    : &math::Point3 <f64>
863    ) -> Proximity {
864      let max_edge  = f64::min (max_a[edge], max_b[edge]);
865      let min_edge  = f64::max (min_a[edge], min_b[edge]);
866      debug_assert_ne!(min_edge, max_edge);
867      let mid_edge  = 0.5 * (min_edge + max_edge);
868      let a_to_b = position_b - position_a;
869      let a_to_b_others = {
870        let mut a_to_b_others = a_to_b;
871        a_to_b_others[edge] = 0.0;
872        a_to_b_others
873      };
874      let a_to_b_others_signum = a_to_b_others.map (math::signum_or_zero);
875      debug_assert!(a_to_b_others_signum[edge].is_zero());
876      debug_assert!(!a_to_b_others_signum[other1].is_zero());
877      debug_assert!(!a_to_b_others_signum[other2].is_zero());
878      let nearest_a = {
879        let other_component1 =
880          cuboid_a.half_extents()[other1] * a_to_b_others_signum[other1];
881        let other_component2 =
882          cuboid_a.half_extents()[other2] * a_to_b_others_signum[other2];
883        let mut nearest_a = [0.0; 3];
884        nearest_a[edge]   = mid_edge;
885        nearest_a[other1] = position_a[other1] + other_component1;
886        nearest_a[other2] = position_a[other2] + other_component2;
887        nearest_a.into()
888      };
889      let nearest_b = {
890        let other_component1 =
891          -cuboid_b.half_extents()[other1] * a_to_b_others_signum[other1];
892        let other_component2 =
893          -cuboid_b.half_extents()[other2] * a_to_b_others_signum[other2];
894        let mut nearest_b = [0.0; 3];
895        nearest_b[edge]   = mid_edge;
896        nearest_b[other1] = position_b[other1] + other_component1;
897        nearest_b[other2] = position_b[other2] + other_component2;
898        nearest_b.into()
899      };
900      nearest_ab_to_distance (&nearest_a, &nearest_b, &a_to_b_others_signum)
901    }
902
903    fn nearest_ab_to_distance (
904      nearest_a     : &math::Point3 <f64>,
905      nearest_b     : &math::Point3 <f64>,
906      a_to_b_signum : &math::Vector3 <f64>
907    ) -> Proximity {
908      let axis      = nearest_b - nearest_a;
909      let distance  = axis.magnitude();
910      let half_axis = 0.5 * (nearest_b - nearest_a);
911      let midpoint  = nearest_a + half_axis;
912      let normal    = if axis.is_zero() {
913        -a_to_b_signum.normalize()
914      } else {
915        -axis / distance
916      };
917      Proximity { distance, half_axis, midpoint, normal }
918    }
919
920    let max_a = position_a + cuboid_a.max().0;
921    let min_a = position_a + cuboid_a.min().0;
922    let max_b = position_b + cuboid_b.max().0;
923    let min_b = position_b + cuboid_b.min().0;
924
925    let overlap_x = min_b.0.x < max_a.0.x && min_a.0.x < max_b.0.x;
926    let overlap_y = min_b.0.y < max_a.0.y && min_a.0.y < max_b.0.y;
927    let overlap_z = min_b.0.z < max_a.0.z && min_a.0.z < max_b.0.z;
928    match (overlap_x, overlap_y, overlap_z) {
929      (false, false, false) => {
930        use math::ElementWise;
931        // no overlaps: nearest features are corners
932        let a_to_b = position_b - position_a;
933        debug_assert!(!a_to_b.x.is_zero());
934        debug_assert!(!a_to_b.y.is_zero());
935        debug_assert!(!a_to_b.z.is_zero());
936        let a_to_b_signum = a_to_b.map (f64::signum);
937        let nearest_a = position_a +
938          cuboid_a.half_extents().mul_element_wise (a_to_b_signum);
939        let nearest_b = position_b +
940          cuboid_b.half_extents().mul_element_wise (-a_to_b_signum);
941        nearest_ab_to_distance (&nearest_a, &nearest_b, &a_to_b_signum)
942      }
943      (true, false, false)  => {
944        // overlap x only: nearest features are edges
945        distance_edge (0, 1, 2,
946          position_a, cuboid_a, position_b, cuboid_b,
947          &max_a, &min_a, &max_b, &min_b
948        )
949      }
950      (false, true, false)  => {
951        // overlap y only: nearest features are edges
952        distance_edge (1, 0, 2,
953          position_a, cuboid_a, position_b, cuboid_b,
954          &max_a, &min_a, &max_b, &min_b
955        )
956      }
957      (false, false, true)  => {
958        // overlap z only: nearest features are edges
959        distance_edge (2, 1, 2,
960          position_a, cuboid_a, position_b, cuboid_b,
961          &max_a, &min_a, &max_b, &min_b
962        )
963      }
964      (true, true, false)   => {
965        // overlap x and y: nearest features are faces
966        unimplemented!()
967      }
968      (true, false, true)   => {
969        // overlap x and z: nearest features are faces
970        unimplemented!()
971      }
972      (false, true, true)   => {
973        // overlap y and z: nearest features are faces
974        unimplemented!()
975      }
976      (true, true, true)    => {
977        // overlap on all axes: intersection
978        unimplemented!()
979      }
980    }
981  } // end query_cuboid_cuboid_refactor
982  */
983
984  pub fn query_sphere_capsule (
985    position_a : &math::Point3 <f64>, sphere_a  : &shape::Sphere <f64>,
986    position_b : &math::Point3 <f64>, capsule_b : &shape::Capsule <f64>
987  ) -> Self {
988    use math::num_traits::Zero;
989    let half_height_b = *capsule_b.half_height;
990    let (radius_a, radius_b) = (*sphere_a.radius, *capsule_b.radius);
991    let shaft_max_b = position_b + math::Vector3::new (0.0, 0.0, half_height_b);
992    let shaft_min_b = position_b - math::Vector3::new (0.0, 0.0, half_height_b);
993    let distance_normal_half_axis = |axis : &math::Vector3 <f64>| {
994      let axis_distance = axis.magnitude();
995      let distance      = axis_distance - radius_a - radius_b;
996      let normal        = if axis.is_zero() {
997        math::Unit3::axis_x()
998      } else {
999        math::Unit3::unchecked_approx (-(axis / axis_distance))
1000      };
1001      let half_axis     = -0.5 * distance * *normal;
1002      (distance, normal, half_axis)
1003    };
1004    if position_a.0.z <= shaft_min_b.0.z {
1005      // cylinder shaft of B is above center of A
1006      let axis     = shaft_min_b - position_a;
1007      let (distance, normal, half_axis) = distance_normal_half_axis (&axis);
1008      let midpoint = position_a - radius_a * *normal + half_axis;
1009      Proximity { distance, half_axis, midpoint, normal }
1010    } else if shaft_max_b.0.z <= position_a.0.z {
1011      // cylinder shaft of B is below center of A
1012      let axis     = shaft_max_b - position_a;
1013      let (distance, normal, half_axis) = distance_normal_half_axis (&axis);
1014      let midpoint = position_a - radius_a * *normal + half_axis;
1015      Proximity { distance, half_axis, midpoint, normal }
1016    } else {
1017      // cylinder shaft of B is adjacent to center of A
1018      let axis     =
1019        math::Point3::from ([position_b.0.x, position_b.0.y, 0.0]) -
1020        math::Point3::from ([position_a.0.x, position_a.0.y, 0.0]);
1021      let (distance, normal, half_axis) = distance_normal_half_axis (&axis);
1022      let midpoint = {
1023        let max_z  = f64::min (position_a.0.z, shaft_max_b.0.z);
1024        let min_z  = f64::max (position_a.0.z, shaft_min_b.0.z);
1025        let mid_z  = 0.5 * (max_z + min_z);
1026        let mid_a  = position_a - radius_a * *normal + half_axis;
1027        [mid_a.0.x, mid_a.0.y, mid_z].into()
1028      };
1029      Proximity { distance, half_axis, midpoint, normal }
1030    }
1031  }
1032
1033  pub fn query_sphere_cuboid (
1034    position_a : &math::Point3 <f64>, sphere_a : &shape::Sphere <f64>,
1035    position_b : &math::Point3 <f64>, cuboid_b : &shape::Cuboid <f64>
1036  ) -> Self {
1037    use math::num_traits::Zero;
1038    let cuboid_max_b = position_b + cuboid_b.max().0;
1039    let cuboid_min_b = position_b - cuboid_b.max().0;
1040
1041    let overlap_x = cuboid_min_b.0.x < position_a.0.x &&
1042      position_a.0.x < cuboid_max_b.0.x;
1043    let overlap_y = cuboid_min_b.0.y < position_a.0.y &&
1044      position_a.0.y < cuboid_max_b.0.y;
1045
1046    let sphere_a_radius        = *sphere_a.radius;
1047    let cuboid_b_half_extent_x = *cuboid_b.half_extent_x;
1048    let cuboid_b_half_extent_y = *cuboid_b.half_extent_y;
1049    let cuboid_b_half_extent_z = *cuboid_b.half_extent_z;
1050    if position_a.0.z >= cuboid_max_b.0.z {
1051      // sphere above cuboid
1052      match (overlap_x, overlap_y) {
1053        (false, false) => {
1054          // nearest/deepest point is a corner of the cuboid
1055          let b_to_a        = position_a - position_b;
1056          debug_assert!(!b_to_a.x.is_zero());
1057          debug_assert!(!b_to_a.y.is_zero());
1058          debug_assert!(!b_to_a.z.is_zero());
1059          let b_to_a_sigvec = b_to_a.map (f64::signum);
1060          let nearest_b     = position_b + cuboid_b.max().0 * b_to_a_sigvec;
1061          let a_to_b        = nearest_b - position_a;
1062          let nearest_a     = position_a + if !a_to_b.is_zero() {
1063            sphere_a_radius * a_to_b.normalized()
1064          } else {
1065            [-b_to_a_sigvec.x * sphere_a_radius, 0.0, 0.0].into()
1066          };
1067          let axis          = nearest_b - nearest_a;
1068          let distance      = axis.magnitude() * if !a_to_b.is_zero() {
1069            a_to_b.dot (axis).signum()
1070          } else {
1071            -1.0
1072          };
1073          let half_axis     = 0.5 * axis;
1074          let normal        = if axis.is_zero() {
1075            math::Unit3::normalize (b_to_a_sigvec)
1076          } else {
1077            math::Unit3::unchecked_approx (-axis / distance)
1078          };
1079          let midpoint      = nearest_a + half_axis;
1080          Proximity { distance, half_axis, midpoint, normal }
1081        }
1082        (true, false) => {
1083          // nearest/deepest point is on an edge parallel to the X axis
1084          let signum_y  = (position_a.0.y - position_b.0.y).signum();
1085          let nearest_b = math::Point3::new (
1086            position_a.0.x,
1087            position_b.0.y + signum_y * cuboid_b_half_extent_y,
1088            cuboid_max_b.0.z
1089          );
1090          let a_to_b    = nearest_b - position_a;
1091          let nearest_a = position_a + if a_to_b.is_zero() {
1092            -signum_y * math::Vector3::new (0.0, sphere_a_radius, 0.0)
1093          } else {
1094            sphere_a_radius * a_to_b.normalized()
1095          };
1096          let axis      = nearest_b - nearest_a;
1097          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1098            a_to_b.dot (axis).signum()
1099          } else {
1100            -1.0
1101          };
1102          let half_axis = 0.5 * axis;
1103          let normal    = if axis.is_zero() {
1104            math::Unit3::normalize (math::Vector3::new (0.0, signum_y, 1.0))
1105          } else {
1106            math::Unit3::unchecked_approx (-axis / distance)
1107          };
1108          let midpoint  = nearest_a + half_axis;
1109          Proximity { distance, half_axis, midpoint, normal }
1110        }
1111        (false, true) => {
1112          // nearest/deepest point is on an edge parallel to the Y axis
1113          let signum_x  = (position_a.0.x - position_b.0.x).signum();
1114          let nearest_b = math::Point3::new (
1115            position_b.0.x + signum_x * cuboid_b_half_extent_x,
1116            position_a.0.y,
1117            cuboid_max_b.0.z
1118          );
1119          let a_to_b    = nearest_b - position_a;
1120          let nearest_a = position_a + if a_to_b.is_zero() {
1121            -signum_x * math::Vector3::new (sphere_a_radius, 0.0, 0.0)
1122          } else {
1123            sphere_a_radius * a_to_b.normalized()
1124          };
1125          let axis      = nearest_b - nearest_a;
1126          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1127            a_to_b.dot (axis).signum()
1128          } else {
1129            -1.0
1130          };
1131          let half_axis = 0.5 * axis;
1132          let normal    = if axis.is_zero() {
1133            math::Unit3::normalize (math::Vector3::new (signum_x, 0.0, 1.0))
1134          } else {
1135            math::Unit3::unchecked_approx (-axis / distance)
1136          };
1137          let midpoint  = nearest_a + half_axis;
1138          Proximity { distance, half_axis, midpoint, normal }
1139        }
1140        (true, true) => {
1141          // nearest/deepest point is in the -Z direction
1142          let nearest_a : math::Point3 <f64> =
1143            position_a - math::Vector3::new (0.0, 0.0, sphere_a_radius);
1144          let nearest_b : math::Point3 <f64> =
1145            [position_a.0.x, position_a.0.y, cuboid_max_b.0.z].into();
1146          let axis      = nearest_b - nearest_a;
1147          let normal    = math::Unit3::axis_z();
1148          let half_axis = 0.5 * axis;
1149          let distance  = nearest_a.0.z - nearest_b.0.z;
1150          let midpoint  = nearest_a + half_axis;
1151          Proximity { distance, half_axis, midpoint, normal }
1152        }
1153      }
1154    } else if position_a.0.z <= cuboid_min_b.0.z {
1155      // sphere below cuboid
1156      match (overlap_x, overlap_y) {
1157        (false, false) => {
1158          // nearest/deepest point is a corner of the cuboid
1159          let b_to_a        = position_a - position_b;
1160          debug_assert!(!b_to_a.x.is_zero());
1161          debug_assert!(!b_to_a.y.is_zero());
1162          debug_assert!(!b_to_a.z.is_zero());
1163          let b_to_a_sigvec = b_to_a.map (f64::signum);
1164          let nearest_b     = position_b + cuboid_b.max().0 * b_to_a_sigvec;
1165          let a_to_b        = nearest_b - position_a;
1166          let nearest_a     = position_a + if !a_to_b.is_zero() {
1167            sphere_a_radius * a_to_b.normalized()
1168          } else {
1169            [-b_to_a_sigvec.x * sphere_a_radius, 0.0, 0.0].into()
1170          };
1171          let axis          = nearest_b - nearest_a;
1172          let distance      = axis.magnitude() * if !a_to_b.is_zero() {
1173            a_to_b.dot (axis).signum()
1174          } else {
1175            -1.0
1176          };
1177          let half_axis     = 0.5 * axis;
1178          let normal        = if axis.is_zero() {
1179            math::Unit3::normalize (b_to_a_sigvec)
1180          } else {
1181            math::Unit3::unchecked_approx (-axis / distance)
1182          };
1183          let midpoint      = nearest_a + half_axis;
1184          Proximity { distance, half_axis, midpoint, normal }
1185        }
1186        (true, false) => {
1187          // nearest/deepest point is on an edge parallel to the X axis
1188          let signum_y  = (position_a.0.y - position_b.0.y).signum();
1189          let nearest_b = math::Point3::new (
1190            position_a.0.x,
1191            position_b.0.y + signum_y * cuboid_b_half_extent_y,
1192            cuboid_min_b.0.z
1193          );
1194          let a_to_b    = nearest_b - position_a;
1195          let nearest_a = position_a + if a_to_b.is_zero() {
1196            -signum_y * math::Vector3::new (0.0, sphere_a_radius, 0.0)
1197          } else {
1198            sphere_a_radius * a_to_b.normalized()
1199          };
1200          let axis      = nearest_b - nearest_a;
1201          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1202            a_to_b.dot (axis).signum()
1203          } else {
1204            -1.0
1205          };
1206          let half_axis = 0.5 * axis;
1207          let normal    = if axis.is_zero() {
1208            math::Unit3::normalize (math::Vector3::new (0.0, signum_y, -1.0))
1209          } else {
1210            math::Unit3::unchecked_approx (-axis / distance)
1211          };
1212          let midpoint  = nearest_a + half_axis;
1213          Proximity { distance, half_axis, midpoint, normal }
1214        }
1215        (false, true) => {
1216          // nearest/deepest point is on an edge parallel to the Y axis
1217          let signum_x  = (position_a.0.x - position_b.0.x).signum();
1218          let nearest_b = math::Point3::new (
1219            position_b.0.x + signum_x * cuboid_b_half_extent_x,
1220            position_a.0.y,
1221            cuboid_min_b.0.z
1222          );
1223          let a_to_b    = nearest_b - position_a;
1224          let nearest_a = position_a + if a_to_b.is_zero() {
1225            -signum_x * math::Vector3::new (sphere_a_radius, 0.0, 0.0)
1226          } else {
1227            sphere_a_radius * a_to_b.normalized()
1228          };
1229          let axis      = nearest_b - nearest_a;
1230          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1231            a_to_b.dot (axis).signum()
1232          } else {
1233            -1.0
1234          };
1235          let half_axis = 0.5 * axis;
1236          let normal    = if axis.is_zero() {
1237            math::Unit3::normalize (math::Vector3::new (signum_x, 0.0, -1.0))
1238          } else {
1239            math::Unit3::unchecked_approx (-axis / distance)
1240          };
1241          let midpoint  = nearest_a + half_axis;
1242          Proximity { distance, half_axis, midpoint, normal }
1243        }
1244        (true, true) => {
1245          // nearest/deepest point is in the -Z direction
1246          let nearest_a : math::Point3 <f64> =
1247            position_a + math::Vector3::new (0.0, 0.0, sphere_a_radius);
1248          let nearest_b : math::Point3 <f64> =
1249            [position_a.0.x, position_a.0.y, cuboid_min_b.0.z].into();
1250          let axis      = nearest_b - nearest_a;
1251          let normal    = math::Unit3::axis_z().invert();
1252          let half_axis = 0.5 * axis;
1253          let distance  = nearest_b.0.z - nearest_a.0.z;
1254          let midpoint  = nearest_a + half_axis;
1255          Proximity { distance, half_axis, midpoint, normal }
1256        }
1257      }
1258    } else {
1259      // sphere center overlaps on z axis
1260      match (overlap_x, overlap_y) {
1261        (false, false) => {
1262          // nearest point is on a Z parallel edge of the cuboid
1263          let b_to_a_signum_x = (position_a.0.x - position_b.0.x).signum();
1264          let b_to_a_signum_y = (position_a.0.y - position_b.0.y).signum();
1265          let b_to_a_unit_sigvec =
1266            math::Vector3::new (b_to_a_signum_x, b_to_a_signum_y, 0.0)
1267              .normalized();
1268          let nearest_b = math::Point3::from ([
1269            position_b.0.x + b_to_a_signum_x * cuboid_b_half_extent_x,
1270            position_b.0.y + b_to_a_signum_y * cuboid_b_half_extent_y,
1271            position_a.0.z
1272          ]);
1273          let a_to_b    = math::Vector3::new (
1274            nearest_b.0.x - position_a.0.x,
1275            nearest_b.0.y - position_a.0.y,
1276            0.0
1277          );
1278          let nearest_a = math::Point3::new (
1279            position_a.0.x, position_a.0.y, position_a.0.z
1280          ) + sphere_a_radius * if !a_to_b.is_zero() {
1281            a_to_b.normalized()
1282          } else {
1283            -b_to_a_unit_sigvec
1284          };
1285          let axis      = nearest_b - nearest_a;
1286          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1287            a_to_b.dot (axis).signum()
1288          } else {
1289            -1.0
1290          };
1291          let half_axis = 0.5 * axis;
1292          let normal    = math::Unit3::unchecked_approx (if axis.is_zero() {
1293            b_to_a_unit_sigvec
1294          } else {
1295            -axis / distance
1296          });
1297          let midpoint  = nearest_a + half_axis;
1298          Proximity { distance, half_axis, midpoint, normal }
1299        }
1300        (true, false)  => {
1301          // nearest point is on a Z/X parallel face of the cuboid
1302          let b_to_a_signum_y    = (position_a.0.y - position_b.0.y).signum();
1303          let b_to_a_unit_sigvec =
1304            math::Vector3::new (0.0, b_to_a_signum_y, 0.0);
1305          let nearest_b = math::Point3::from ([
1306            position_a.0.x,
1307            position_b.0.y + b_to_a_signum_y * cuboid_b_half_extent_y,
1308            position_a.0.z
1309          ]);
1310          let a_to_b    =
1311            math::Vector3::new (0.0, nearest_b.0.y - position_a.0.y, 0.0);
1312          let nearest_a = math::Point3::new (
1313            position_a.0.x, position_a.0.y, position_a.0.z
1314          ) + math::Vector3::new (0.0, -b_to_a_signum_y * sphere_a_radius, 0.0);
1315          let axis      = nearest_b - nearest_a;
1316          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1317            a_to_b.dot (axis).signum()
1318          } else {
1319            -1.0
1320          };
1321          let half_axis = 0.5 * axis;
1322          let normal    = math::Unit3::unchecked_approx (if axis.is_zero() {
1323            b_to_a_unit_sigvec
1324          } else {
1325            -axis / distance
1326          });
1327          let midpoint  = nearest_a + half_axis;
1328          Proximity { distance, half_axis, midpoint, normal }
1329        }
1330        (false, true)  => {
1331          // nearest point is on a Z/Y parallel face of the cuboid
1332          let b_to_a_signum_x    = (position_a.0.x - position_b.0.x).signum();
1333          let b_to_a_unit_sigvec =
1334            math::Vector3::new (b_to_a_signum_x, 0.0, 0.0);
1335          let nearest_b = math::Point3::from ([
1336            position_b.0.x + b_to_a_signum_x * cuboid_b_half_extent_x,
1337            position_a.0.y,
1338            position_a.0.z
1339          ]);
1340          let a_to_b    =
1341            math::Vector3::new (nearest_b.0.x - position_a.0.x, 0.0, 0.0);
1342          let nearest_a = math::Point3::new (
1343            position_a.0.x, position_a.0.y, position_a.0.z
1344          ) + math::Vector3::new (-b_to_a_signum_x * sphere_a_radius, 0.0, 0.0);
1345          let axis      = nearest_b - nearest_a;
1346          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1347            a_to_b.dot (axis).signum()
1348          } else {
1349            -1.0
1350          };
1351          let half_axis = 0.5 * axis;
1352          let normal    = math::Unit3::unchecked_approx (if axis.is_zero() {
1353            b_to_a_unit_sigvec
1354          } else {
1355            -axis / distance
1356          });
1357          let midpoint  = nearest_a + half_axis;
1358          Proximity { distance, half_axis, midpoint, normal }
1359        }
1360        (true, true) => {
1361          // center inside X/Y bounds
1362          if position_a == position_b {
1363            let depth_x = cuboid_b_half_extent_x + sphere_a_radius;
1364            let depth_y = cuboid_b_half_extent_y + sphere_a_radius;
1365            let depth_z = cuboid_b_half_extent_z + sphere_a_radius;
1366            if depth_x <= depth_y && depth_x <= depth_z {
1367              let distance  = -depth_x;
1368              let half_axis = [0.5 * depth_x, 0.0, 0.0].into();
1369              let normal    = math::Unit3::axis_x();
1370              let midpoint  = position_a + half_axis -
1371                math::Vector3::new (sphere_a_radius, 0.0, 0.0);
1372              Proximity { distance, half_axis, midpoint, normal }
1373            } else if depth_y <= depth_x && depth_y <= depth_z {
1374              let distance = -depth_y;
1375              let half_axis = [0.0, 0.5 * depth_y, 0.0].into();
1376              let normal    = math::Unit3::axis_y();
1377              let midpoint  = position_a + half_axis -
1378                math::Vector3::new (0.0, sphere_a_radius, 0.0);
1379              Proximity { distance, half_axis, midpoint, normal }
1380            } else {
1381              debug_assert!(depth_z < depth_x);
1382              debug_assert!(depth_z < depth_y);
1383              let distance = -depth_z;
1384              let half_axis = [0.0, 0.0, 0.5 * depth_z].into();
1385              let normal    = math::Unit3::axis_z();
1386              let midpoint  = position_a + half_axis -
1387                math::Vector3::new (0.0, 0.0, sphere_a_radius);
1388              Proximity { distance, half_axis, midpoint, normal }
1389            }
1390          } else {
1391            // NB: we want the following to map zero values to positive 1.0
1392            // so we use f64::signum instead of math::sigvec
1393            let b_to_a_sigvec = (position_a - position_b).map (f64::signum);
1394            let corner_b      = position_b + cuboid_b.max().0 * b_to_a_sigvec;
1395            let depth_x       = (corner_b.0.x - position_a.0.x).abs() +
1396              sphere_a_radius;
1397            let depth_y       = (corner_b.0.y - position_a.0.y).abs() +
1398              sphere_a_radius;
1399            let depth_z       = (corner_b.0.z - position_a.0.z).abs() +
1400              sphere_a_radius -
1401              if position_a.0.z.abs() > corner_b.0.z.abs() {
1402                2.0 * (position_a.0.z.abs() - corner_b.0.z.abs())
1403              } else {
1404                0.0
1405              };
1406
1407            if depth_x <= depth_y && depth_x <= depth_z {
1408              let distance  = -depth_x;
1409              let half_axis = [0.5 * b_to_a_sigvec.x * depth_x, 0.0, 0.0].into();
1410              let normal    = math::Unit3::unchecked (
1411                [b_to_a_sigvec.x, 0.0, 0.0].into());
1412              let midpoint  = position_a + half_axis +
1413                math::Vector3::new (
1414                  -b_to_a_sigvec.x * sphere_a_radius, 0.0, 0.0);
1415              Proximity { distance, half_axis, midpoint, normal }
1416            } else if depth_y <= depth_x && depth_y <= depth_z {
1417              let distance  = -depth_y;
1418              let half_axis = [0.0, 0.5 * b_to_a_sigvec.y * depth_y, 0.0].into();
1419              let normal    = math::Unit3::unchecked (
1420                [0.0, b_to_a_sigvec.y, 0.0].into());
1421              let midpoint  = position_a + half_axis +
1422                math::Vector3::new (
1423                  0.0, -b_to_a_sigvec.y * sphere_a_radius, 0.0);
1424              Proximity { distance, half_axis, midpoint, normal }
1425            } else {
1426              debug_assert!(depth_z < depth_x);
1427              debug_assert!(depth_z < depth_y);
1428              let nearest_a = math::Point3::new (
1429                position_a.0.x,
1430                position_a.0.y,
1431                position_a.0.z - b_to_a_sigvec.z * sphere_a_radius
1432              );
1433              let nearest_b = math::Point3::new (
1434                position_a.0.x,
1435                position_a.0.y,
1436                position_b.0.z + b_to_a_sigvec.z * cuboid_b_half_extent_z
1437              );
1438              let axis      = nearest_b - nearest_a;
1439              debug_assert_eq!(axis.x, 0.0);
1440              debug_assert_eq!(axis.y, 0.0);
1441              let distance  = -axis.z.abs();
1442              let half_axis = 0.5 * axis;
1443              let normal    = math::Unit3::unchecked (
1444                [0.0, 0.0, b_to_a_sigvec.z].into());
1445              let midpoint  = nearest_a + half_axis;
1446              Proximity { distance, half_axis, midpoint, normal }
1447            }
1448          }
1449        }
1450      }
1451    }
1452  } // end query_sphere_cuboid
1453
1454  pub fn query_sphere_orthant (
1455    position_a : &math::Point3 <f64>, sphere_a : &shape::Sphere <f64>,
1456    position_b : &math::Point3 <f64>, orthant_b : &shape::Orthant
1457  ) -> Self {
1458    let radius_a    = *sphere_a.radius;
1459    let normal      = orthant_b.normal_axis.to_vec::<f64>();
1460    let normal_abs  = normal.map (f64::abs);
1461    let surface_sigvec = math::Vector3::new (1.0, 1.0, 1.0) - normal_abs;
1462    let nearest_a   = position_a -
1463      math::Vector3::new (radius_a, radius_a, radius_a) * normal;
1464    let nearest_b   =
1465      math::Point3 (position_a.0 * surface_sigvec + position_b.0 * normal_abs);
1466    let axis        = nearest_b - nearest_a;
1467    debug_assert_eq!(axis.sum().abs(), axis.magnitude());
1468    let distance    = -normal.dot (axis).signum() * axis.sum().abs();
1469    let half_axis   = 0.5 * axis;
1470    let midpoint    = nearest_a + half_axis;
1471    let normal      = math::Unit3::unchecked (normal);
1472    Proximity { distance, half_axis, midpoint, normal }
1473  } // end query_sphere_orthant
1474
1475  pub fn query_capsule_cuboid (
1476    position_a : &math::Point3 <f64>, capsule_a : &shape::Capsule <f64>,
1477    position_b : &math::Point3 <f64>, cuboid_b  : &shape::Cuboid  <f64>
1478  ) -> Self {
1479    use math::num_traits::Zero;
1480    let shaft_max_a  = position_a +
1481      math::Vector3::new (0.0, 0.0, *capsule_a.half_height);
1482    let shaft_min_a  = position_a -
1483      math::Vector3::new (0.0, 0.0, *capsule_a.half_height);
1484    let cuboid_max_b = position_b + cuboid_b.max().0;
1485    let cuboid_min_b = position_b - cuboid_b.max().0;
1486
1487    let overlap_x = cuboid_min_b.0.x < position_a.0.x &&
1488      position_a.0.x < cuboid_max_b.0.x;
1489    let overlap_y = cuboid_min_b.0.y < position_a.0.y &&
1490      position_a.0.y < cuboid_max_b.0.y;
1491
1492    let capsule_a_radius       = *capsule_a.radius;
1493    let capsule_a_half_height  = *capsule_a.half_height;
1494    let cuboid_b_half_extent_x = *cuboid_b.half_extent_x;
1495    let cuboid_b_half_extent_y = *cuboid_b.half_extent_y;
1496    let cuboid_b_half_extent_z = *cuboid_b.half_extent_z;
1497    if shaft_min_a.0.z >= cuboid_max_b.0.z {
1498      // capsule above cuboid
1499      match (overlap_x, overlap_y) {
1500        (false, false) => {
1501          // nearest/deepest point is a corner of the cuboid
1502          let b_to_a        = shaft_min_a - position_b;
1503          debug_assert!(!b_to_a.x.is_zero());
1504          debug_assert!(!b_to_a.y.is_zero());
1505          debug_assert!(!b_to_a.z.is_zero());
1506          let b_to_a_sigvec = b_to_a.map (f64::signum);
1507          let nearest_b     = position_b + cuboid_b.max().0 * b_to_a_sigvec;
1508          let a_to_b        = nearest_b - shaft_min_a;
1509          let nearest_a     = shaft_min_a + if !a_to_b.is_zero() {
1510            capsule_a_radius * a_to_b.normalized()
1511          } else {
1512            [-b_to_a_sigvec.x * capsule_a_radius, 0.0, 0.0].into()
1513          };
1514          let axis          = nearest_b - nearest_a;
1515          let distance      = axis.magnitude() * if !a_to_b.is_zero() {
1516            a_to_b.dot (axis).signum()
1517          } else {
1518            -1.0
1519          };
1520          let half_axis     = 0.5 * axis;
1521          let normal        = if axis.is_zero() {
1522            math::Unit3::normalize (b_to_a_sigvec)
1523          } else {
1524            math::Unit3::unchecked_approx (-axis / distance)
1525          };
1526          let midpoint      = nearest_a + half_axis;
1527          Proximity { distance, half_axis, midpoint, normal }
1528        }
1529        (true, false) => {
1530          // nearest/deepest point is on an edge parallel to the X axis
1531          let signum_y  = (shaft_min_a.0.y - position_b.0.y).signum();
1532          let nearest_b = math::Point3::new (
1533            shaft_min_a.0.x,
1534            position_b.0.y + signum_y * cuboid_b_half_extent_y,
1535            cuboid_max_b.0.z
1536          );
1537          let a_to_b    = nearest_b - shaft_min_a;
1538          let nearest_a = shaft_min_a + if a_to_b.is_zero() {
1539            -signum_y * math::Vector3::new (0.0, capsule_a_radius, 0.0)
1540          } else {
1541            capsule_a_radius * a_to_b.normalized()
1542          };
1543          let axis      = nearest_b - nearest_a;
1544          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1545            a_to_b.dot (axis).signum()
1546          } else {
1547            -1.0
1548          };
1549          let half_axis = 0.5 * axis;
1550          let normal    = if axis.is_zero() {
1551            math::Unit3::normalize (math::Vector3::new (0.0, signum_y, 1.0))
1552          } else {
1553            math::Unit3::unchecked_approx (-axis / distance)
1554          };
1555          let midpoint  = nearest_a + half_axis;
1556          Proximity { distance, half_axis, midpoint, normal }
1557        }
1558        (false, true) => {
1559          // nearest/deepest point is on an edge parallel to the Y axis
1560          let signum_x  = (shaft_min_a.0.x - position_b.0.x).signum();
1561          let nearest_b = math::Point3::new (
1562            position_b.0.x + signum_x * cuboid_b_half_extent_x,
1563            shaft_min_a.0.y,
1564            cuboid_max_b.0.z
1565          );
1566          let a_to_b    = nearest_b - shaft_min_a;
1567          let nearest_a = shaft_min_a + if a_to_b.is_zero() {
1568            -signum_x * math::Vector3::new (capsule_a_radius, 0.0, 0.0)
1569          } else {
1570            capsule_a_radius * a_to_b.normalized()
1571          };
1572          let axis      = nearest_b - nearest_a;
1573          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1574            a_to_b.dot (axis).signum()
1575          } else {
1576            -1.0
1577          };
1578          let half_axis = 0.5 * axis;
1579          let normal    = if axis.is_zero() {
1580            math::Unit3::normalize (math::Vector3::new (signum_x, 0.0, 1.0))
1581          } else {
1582            math::Unit3::unchecked_approx (-axis / distance)
1583          };
1584          let midpoint  = nearest_a + half_axis;
1585          Proximity { distance, half_axis, midpoint, normal }
1586        }
1587        (true, true) => {
1588          // nearest/deepest point is in the -Z direction
1589          let nearest_a : math::Point3 <f64> =
1590            shaft_min_a - math::Vector3::new (0.0, 0.0, capsule_a_radius);
1591          let nearest_b : math::Point3 <f64> =
1592            [position_a.0.x, position_a.0.y, cuboid_max_b.0.z].into();
1593          let axis      = nearest_b - nearest_a;
1594          let normal    = math::Unit3::axis_z();
1595          let half_axis = 0.5 * axis;
1596          let distance  = nearest_a.0.z - nearest_b.0.z;
1597          let midpoint  = nearest_a + half_axis;
1598          Proximity { distance, half_axis, midpoint, normal }
1599        }
1600      }
1601    } else if shaft_max_a.0.z <= cuboid_min_b.0.z {
1602      // capsule below cuboid
1603      match (overlap_x, overlap_y) {
1604        (false, false) => {
1605          // nearest/deepest point is a corner of the cuboid
1606          let b_to_a        = shaft_max_a - position_b;
1607          debug_assert!(!b_to_a.x.is_zero());
1608          debug_assert!(!b_to_a.y.is_zero());
1609          debug_assert!(!b_to_a.z.is_zero());
1610          let b_to_a_sigvec = b_to_a.map (f64::signum);
1611          let nearest_b     = position_b + cuboid_b.max().0 * b_to_a_sigvec;
1612          let a_to_b        = nearest_b - shaft_max_a;
1613          let nearest_a     = shaft_max_a + if !a_to_b.is_zero() {
1614            capsule_a_radius * a_to_b.normalized()
1615          } else {
1616            [-b_to_a_sigvec.x * capsule_a_radius, 0.0, 0.0].into()
1617          };
1618          let axis          = nearest_b - nearest_a;
1619          let distance      = axis.magnitude() * if !a_to_b.is_zero() {
1620            a_to_b.dot (axis).signum()
1621          } else {
1622            -1.0
1623          };
1624          let half_axis     = 0.5 * axis;
1625          let normal        = if axis.is_zero() {
1626            math::Unit3::normalize (b_to_a_sigvec)
1627          } else {
1628            math::Unit3::unchecked_approx (-axis / distance)
1629          };
1630          let midpoint      = nearest_a + half_axis;
1631          Proximity { distance, half_axis, midpoint, normal }
1632        }
1633        (true, false) => {
1634          // nearest/deepest point is on an edge parallel to the X axis
1635          let signum_y  = (shaft_max_a.0.y - position_b.0.y).signum();
1636          let nearest_b = math::Point3::new (
1637            shaft_max_a.0.x,
1638            position_b.0.y + signum_y * cuboid_b_half_extent_y,
1639            cuboid_min_b.0.z
1640          );
1641          let a_to_b    = nearest_b - shaft_max_a;
1642          let nearest_a = shaft_max_a + if a_to_b.is_zero() {
1643            -signum_y * math::Vector3::new (0.0, capsule_a_radius, 0.0)
1644          } else {
1645            capsule_a_radius * a_to_b.normalized()
1646          };
1647          let axis      = nearest_b - nearest_a;
1648          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1649            a_to_b.dot (axis).signum()
1650          } else {
1651            -1.0
1652          };
1653          let half_axis = 0.5 * axis;
1654          let normal    = if axis.is_zero() {
1655            math::Unit3::normalize (math::Vector3::new (0.0, signum_y, -1.0))
1656          } else {
1657            math::Unit3::unchecked_approx (-axis / distance)
1658          };
1659          let midpoint  = nearest_a + half_axis;
1660          Proximity { distance, half_axis, midpoint, normal }
1661        }
1662        (false, true) => {
1663          // nearest/deepest point is on an edge parallel to the Y axis
1664          let signum_x  = (shaft_max_a.0.x - position_b.0.x).signum();
1665          let nearest_b = math::Point3::new (
1666            position_b.0.x + signum_x * cuboid_b_half_extent_x,
1667            shaft_max_a.0.y,
1668            cuboid_min_b.0.z
1669          );
1670          let a_to_b    = nearest_b - shaft_max_a;
1671          let nearest_a = shaft_max_a + if a_to_b.is_zero() {
1672            -signum_x * math::Vector3::new (capsule_a_radius, 0.0, 0.0)
1673          } else {
1674            capsule_a_radius * a_to_b.normalized()
1675          };
1676          let axis      = nearest_b - nearest_a;
1677          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1678            a_to_b.dot (axis).signum()
1679          } else {
1680            -1.0
1681          };
1682          let half_axis = 0.5 * axis;
1683          let normal    = if axis.is_zero() {
1684            math::Unit3::normalize (math::Vector3::new (signum_x, 0.0, -1.0))
1685          } else {
1686            math::Unit3::unchecked_approx (-axis / distance)
1687          };
1688          let midpoint  = nearest_a + half_axis;
1689          Proximity { distance, half_axis, midpoint, normal }
1690        }
1691        (true, true) => {
1692          // nearest/deepest point is in the -Z direction
1693          let nearest_a : math::Point3 <f64> =
1694            shaft_max_a + math::Vector3::new (0.0, 0.0, capsule_a_radius);
1695          let nearest_b : math::Point3 <f64> =
1696            [position_a.0.x, position_a.0.y, cuboid_min_b.0.z].into();
1697          let axis      = nearest_b - nearest_a;
1698          let normal    = math::Unit3::axis_z().invert();
1699          let half_axis = 0.5 * axis;
1700          let distance  = nearest_b.0.z - nearest_a.0.z;
1701          let midpoint  = nearest_a + half_axis;
1702          Proximity { distance, half_axis, midpoint, normal }
1703        }
1704      }
1705    } else {
1706      // capsule cylinder shaft overlaps on z axis
1707      let max_z = f64::min (shaft_max_a.0.z, cuboid_max_b.0.z);
1708      let min_z = f64::max (shaft_min_a.0.z, cuboid_min_b.0.z);
1709      debug_assert_ne!(min_z, max_z);
1710      let mid_z = 0.5 * (max_z + min_z);
1711      match (overlap_x, overlap_y) {
1712        (false, false) => {
1713          // nearest point is on a Z parallel edge of the cuboid
1714          let b_to_a_signum_x = (position_a.0.x - position_b.0.x).signum();
1715          let b_to_a_signum_y = (position_a.0.y - position_b.0.y).signum();
1716          let b_to_a_unit_sigvec =
1717            math::Vector3::new (b_to_a_signum_x, b_to_a_signum_y, 0.0)
1718              .normalized();
1719          let nearest_b = math::Point3::from ([
1720            position_b.0.x + b_to_a_signum_x * cuboid_b_half_extent_x,
1721            position_b.0.y + b_to_a_signum_y * cuboid_b_half_extent_y,
1722            mid_z
1723          ]);
1724          let a_to_b    = math::Vector3::new (
1725            nearest_b.0.x - position_a.0.x,
1726            nearest_b.0.y - position_a.0.y,
1727            0.0
1728          );
1729          let nearest_a = math::Point3::new (
1730            position_a.0.x, position_a.0.y, mid_z
1731          ) + capsule_a_radius * if !a_to_b.is_zero() {
1732            a_to_b.normalized()
1733          } else {
1734            -b_to_a_unit_sigvec
1735          };
1736          let axis      = nearest_b - nearest_a;
1737          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1738            a_to_b.dot (axis).signum()
1739          } else {
1740            -1.0
1741          };
1742          let half_axis = 0.5 * axis;
1743          let normal    = math::Unit3::unchecked_approx (if axis.is_zero() {
1744            b_to_a_unit_sigvec
1745          } else {
1746            -axis / distance
1747          });
1748          let midpoint  = nearest_a + half_axis;
1749          Proximity { distance, half_axis, midpoint, normal }
1750        }
1751        (true, false)  => {
1752          // nearest point is on a Z/X parallel face of the cuboid
1753          let b_to_a_signum_y    = (position_a.0.y - position_b.0.y).signum();
1754          let b_to_a_unit_sigvec =
1755            math::Vector3::new (0.0, b_to_a_signum_y, 0.0);
1756          let nearest_b = math::Point3::from ([
1757            position_a.0.x,
1758            position_b.0.y + b_to_a_signum_y * cuboid_b_half_extent_y,
1759            mid_z
1760          ]);
1761          let a_to_b    =
1762            math::Vector3::new (0.0, nearest_b.0.y - position_a.0.y, 0.0);
1763          let nearest_a = math::Point3::new (
1764            position_a.0.x, position_a.0.y, mid_z
1765          ) + math::Vector3::new (0.0, -b_to_a_signum_y * capsule_a_radius, 0.0);
1766          let axis      = nearest_b - nearest_a;
1767          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1768            a_to_b.dot (axis).signum()
1769          } else {
1770            -1.0
1771          };
1772          let half_axis = 0.5 * axis;
1773          let normal    = math::Unit3::unchecked_approx (if axis.is_zero() {
1774            b_to_a_unit_sigvec
1775          } else {
1776            -axis / distance
1777          });
1778          let midpoint  = nearest_a + half_axis;
1779          Proximity { distance, half_axis, midpoint, normal }
1780        }
1781        (false, true)  => {
1782          // nearest point is on a Z/Y parallel face of the cuboid
1783          let b_to_a_signum_x    = (position_a.0.x - position_b.0.x).signum();
1784          let b_to_a_unit_sigvec =
1785            math::Vector3::new (b_to_a_signum_x, 0.0, 0.0);
1786          let nearest_b = math::Point3::from ([
1787            position_b.0.x + b_to_a_signum_x * cuboid_b_half_extent_x,
1788            position_a.0.y,
1789            mid_z
1790          ]);
1791          let a_to_b    =
1792            math::Vector3::new (nearest_b.0.x - position_a.0.x, 0.0, 0.0);
1793          let nearest_a = math::Point3::new (
1794            position_a.0.x, position_a.0.y, mid_z
1795          ) + math::Vector3::new (-b_to_a_signum_x * capsule_a_radius, 0.0, 0.0);
1796          let axis      = nearest_b - nearest_a;
1797          let distance  = axis.magnitude() * if !a_to_b.is_zero() {
1798            a_to_b.dot (axis).signum()
1799          } else {
1800            -1.0
1801          };
1802          let half_axis = 0.5 * axis;
1803          let normal    = math::Unit3::unchecked_approx (if axis.is_zero() {
1804            b_to_a_unit_sigvec
1805          } else {
1806            -axis / distance
1807          });
1808          let midpoint  = nearest_a + half_axis;
1809          Proximity { distance, half_axis, midpoint, normal }
1810        }
1811        (true, true) => {
1812          // cylinder shaft inside X/Y bounds
1813          if position_a == position_b {
1814            let depth_x = cuboid_b_half_extent_x + capsule_a_radius;
1815            let depth_y = cuboid_b_half_extent_y + capsule_a_radius;
1816            let depth_z = cuboid_b_half_extent_z + capsule_a_half_height +
1817              capsule_a_radius;
1818            if depth_x <= depth_y && depth_x <= depth_z {
1819              let distance  = -depth_x;
1820              let half_axis = [0.5 * depth_x, 0.0, 0.0].into();
1821              let normal    = math::Unit3::axis_x();
1822              let midpoint  = position_a + half_axis -
1823                math::Vector3::new (capsule_a_radius, 0.0, 0.0);
1824              Proximity { distance, half_axis, midpoint, normal }
1825            } else if depth_y <= depth_x && depth_y <= depth_z {
1826              let distance = -depth_y;
1827              let half_axis = [0.0, 0.5 * depth_y, 0.0].into();
1828              let normal    = math::Unit3::axis_y();
1829              let midpoint  = position_a + half_axis -
1830                math::Vector3::new (0.0, capsule_a_radius, 0.0);
1831              Proximity { distance, half_axis, midpoint, normal }
1832            } else {
1833              debug_assert!(depth_z < depth_x);
1834              debug_assert!(depth_z < depth_y);
1835              let distance = -depth_z;
1836              let half_axis = [0.0, 0.0, 0.5 * depth_z].into();
1837              let normal    = math::Unit3::axis_z();
1838              let midpoint  = position_a + half_axis - math::Vector3::new (
1839                0.0, 0.0, capsule_a_radius + capsule_a_half_height);
1840              Proximity { distance, half_axis, midpoint, normal }
1841            }
1842          } else {
1843            // NB: we want the following to map zero values to positive 1.0
1844            // so we use f64::signum instead of math::sigvec
1845            let b_to_a_sigvec = (position_a - position_b).map (f64::signum);
1846            let corner_b      = position_b + cuboid_b.max().0 * b_to_a_sigvec;
1847            let depth_x       = (corner_b.0.x - position_a.0.x).abs() +
1848              capsule_a_radius;
1849            let depth_y       = (corner_b.0.y - position_a.0.y).abs() +
1850              capsule_a_radius;
1851            let depth_z       = (corner_b.0.z - position_a.0.z).abs() +
1852              capsule_a_radius + capsule_a_half_height -
1853              if position_a.0.z.abs() > corner_b.0.z.abs() {
1854                2.0 * (position_a.0.z.abs() - corner_b.0.z.abs())
1855              } else {
1856                0.0
1857              };
1858
1859            if depth_x <= depth_y && depth_x <= depth_z {
1860              let distance  = -depth_x;
1861              let half_axis = [0.5 * b_to_a_sigvec.x * depth_x, 0.0, 0.0].into();
1862              let normal    = math::Unit3::unchecked (
1863                [b_to_a_sigvec.x, 0.0, 0.0].into());
1864              let midpoint  = position_a + half_axis +
1865                math::Vector3::new (
1866                  -b_to_a_sigvec.x * capsule_a_radius, 0.0, 0.0);
1867              Proximity { distance, half_axis, midpoint, normal }
1868            } else if depth_y <= depth_x && depth_y <= depth_z {
1869              let distance  = -depth_y;
1870              let half_axis = [0.0, 0.5 * b_to_a_sigvec.y * depth_y, 0.0].into();
1871              let normal    = math::Unit3::unchecked (
1872                [0.0, b_to_a_sigvec.y, 0.0].into());
1873              let midpoint  = position_a + half_axis +
1874                math::Vector3::new (
1875                  0.0, -b_to_a_sigvec.y * capsule_a_radius, 0.0);
1876              Proximity { distance, half_axis, midpoint, normal }
1877            } else {
1878              debug_assert!(depth_z < depth_x);
1879              debug_assert!(depth_z < depth_y);
1880              let nearest_a = math::Point3::new (
1881                position_a.0.x,
1882                position_a.0.y,
1883                position_a.0.z - b_to_a_sigvec.z *
1884                  (capsule_a_radius + capsule_a_half_height)
1885              );
1886              let nearest_b = math::Point3::new (
1887                position_a.0.x,
1888                position_a.0.y,
1889                position_b.0.z + b_to_a_sigvec.z * cuboid_b_half_extent_z
1890              );
1891              let axis      = nearest_b - nearest_a;
1892              debug_assert_eq!(axis.x, 0.0);
1893              debug_assert_eq!(axis.y, 0.0);
1894              let distance  = -axis.z.abs();
1895              let half_axis = 0.5 * axis;
1896              let normal    = math::Unit3::unchecked (
1897                [0.0, 0.0, b_to_a_sigvec.z].into());
1898              let midpoint  = nearest_a + half_axis;
1899              Proximity { distance, half_axis, midpoint, normal }
1900            }
1901          }
1902        }
1903      }
1904    }
1905  } // end query_capsule_cuboid
1906
1907  pub fn query_capsule_orthant (
1908    position_a : &math::Point3 <f64>, capsule_a : &shape::Capsule <f64>,
1909    position_b : &math::Point3 <f64>, orthant_b : &shape::Orthant
1910  ) -> Self {
1911    let radius_a    = *capsule_a.radius;
1912    let half_height = *capsule_a.half_height;
1913    let normal      = orthant_b.normal_axis.to_vec::<f64>();
1914    let normal_abs  = normal.map (f64::abs);
1915    let surface_sigvec = math::Vector3::new (1.0, 1.0, 1.0) - normal_abs;
1916    let nearest_a   = position_a -
1917      math::Vector3::new (radius_a, radius_a, radius_a + half_height) * normal;
1918    let nearest_b   =
1919      math::Point3 (position_a.0 * surface_sigvec + position_b.0 * normal_abs);
1920    let axis        = nearest_b - nearest_a;
1921    debug_assert_eq!(axis.sum().abs(), axis.magnitude());
1922    let distance    = -normal.dot (axis).signum() * axis.sum().abs();
1923    let half_axis   = 0.5 * axis;
1924    let midpoint    = nearest_a + half_axis;
1925    let normal      = math::Unit3::unchecked (normal);
1926    Proximity { distance, half_axis, midpoint, normal }
1927  } // end query_capsule_orthant
1928
1929  pub fn query_cuboid_orthant (
1930    position_a : &math::Point3 <f64>, cuboid_a  : &shape::Cuboid <f64>,
1931    position_b : &math::Point3 <f64>, orthant_b : &shape::Orthant
1932  ) -> Self {
1933    let normal      = orthant_b.normal_axis.to_vec::<f64>();
1934    let normal_abs  = normal.map (f64::abs);
1935    let surface_sigvec = math::Vector3::new (1.0, 1.0, 1.0) - normal_abs;
1936    let nearest_a   = position_a - cuboid_a.half_extents_vec() * normal;
1937    let nearest_b   =
1938      math::Point3 (position_a.0 * surface_sigvec + position_b.0 * normal_abs);
1939    let axis        = nearest_b - nearest_a;
1940    debug_assert_eq!(axis.sum().abs(), axis.magnitude());
1941    let distance    = -normal.dot (axis).signum() * axis.sum().abs();
1942    let half_axis   = 0.5 * axis;
1943    let midpoint    = nearest_a + half_axis;
1944    let normal      = math::Unit3::unchecked (normal);
1945    Proximity { distance, half_axis, midpoint, normal }
1946  } // end query_cuboid_orthant
1947
1948} // end impl Distance
1949
1950#[cfg(test)]
1951mod tests {
1952  extern crate test;
1953  use {std, rand};
1954  use rand_xorshift::XorShiftRng;
1955  use std::f64::consts::*;
1956  use super::*;
1957  //
1958  //  test_distance_query
1959  //
1960  #[test]
1961  fn test_distance_query() {
1962    use rand::{Rng, SeedableRng};
1963    use geometry::shape;
1964    use math::approx::{assert_relative_eq, assert_ulps_eq};
1965
1966    //
1967    //  capsule v. capsule
1968    //
1969
1970    // intersection: positions of capsules A and B are identical-- half_axis and
1971    // normal parallel to +X
1972    let a = object::Static {
1973      position: component::Position ([0.0, 0.0, 0.0].into()),
1974      bound:    component::Bound (shape::Bounded::from (
1975        shape::Capsule::noisy (2.0, 3.0)).into()),
1976      material: component::MATERIAL_STONE,
1977      collidable: true
1978    };
1979    let b = object::Static {
1980      position: component::Position ([0.0, 0.0, 0.0].into()),
1981      bound:    component::Bound (shape::Bounded::from (
1982        shape::Capsule::noisy (1.0, 2.0)).into()),
1983      material: component::MATERIAL_STONE,
1984      collidable: true
1985    };
1986    assert_eq!(
1987      Proximity::query (&a, &b),
1988      Proximity {
1989        distance:  -3.0,
1990        half_axis: [ 1.5, 0.0, 0.0].into(),
1991        normal:    math::Unit3::axis_x(),
1992        midpoint:  [-0.5, 0.0, 0.0].into()
1993      }
1994    );
1995
1996    // intersection: cylinder shafts of A and B overlap-- half_axis and normal
1997    // parallel to +X
1998    let a = object::Static {
1999      position: component::Position ([0.0, 0.0, 1.0].into()),  .. a
2000    };
2001    let b = object::Static {
2002      position: component::Position ([0.0, 0.0, -1.0].into()), .. b
2003    };
2004    assert_eq!(
2005      Proximity::query (&a, &b),
2006      Proximity {
2007        distance:  -3.0,
2008        half_axis: [ 1.5, 0.0,  0.0].into(),
2009        normal:    math::Unit3::axis_x(),
2010        midpoint:  [-0.5, 0.0, -0.5].into()
2011      }
2012    );
2013
2014    // intersection: cylinder shaft max of A coincides with cylinder shaft min
2015    // of B
2016    let a = object::Static {
2017      position: component::Position ([0.0, 0.0, -3.0].into()), .. a
2018    };
2019    let b = object::Static {
2020      position: component::Position ([0.0, 0.0, 2.0].into()),  .. b
2021    };
2022    assert_eq!(
2023      Proximity::query (&a, &b),
2024      Proximity {
2025        distance:  -3.0,
2026        half_axis: [ 1.5, 0.0, 0.0].into(),
2027        normal:    math::Unit3::axis_x(),
2028        midpoint:  [-0.5, 0.0, 0.0].into()
2029      }
2030    );
2031
2032    // intersection: cylinder shaft min of A coincides with cylinder shaft max of B
2033    let a = object::Static {
2034      position: component::Position ([0.0, 0.0, 3.0].into()),  .. a
2035    };
2036    let b = object::Static {
2037      position: component::Position ([0.0, 0.0, -2.0].into()), .. b
2038    };
2039    assert_eq!(
2040      Proximity::query (&a, &b),
2041      Proximity {
2042        distance:  -3.0,
2043        half_axis: [ 1.5, 0.0, 0.0].into(),
2044        normal:    math::Unit3::axis_x(),
2045        midpoint:  [-0.5, 0.0, 0.0].into()
2046      }
2047    );
2048
2049    // intersection: cylinder shaft of A adjacent to cylinder shaft of B
2050    let a = object::Static {
2051      position: component::Position ([-1.0, 0.0, 1.0].into()), .. a
2052    };
2053    let b = object::Static {
2054      position: component::Position ([0.0, 0.0, -1.0].into()), .. b
2055    };
2056    assert_eq!(
2057      Proximity::query (&a, &b),
2058      Proximity {
2059        distance:  -2.0,
2060        half_axis: [-1.0, 0.0,  0.0].into(),
2061        normal:    math::Unit3::axis_x().invert(),
2062        midpoint:  [ 0.0, 0.0, -0.5].into()
2063      }
2064    );
2065
2066    // intersection: cylinder shaft of B above cylinder shaft of A
2067    let a = object::Static {
2068      position: component::Position ([0.0, 0.0, -3.0].into()), .. a
2069    };
2070    let b = object::Static {
2071      position: component::Position ([0.0, 0.0,  3.0].into()), .. b
2072    };
2073    assert_eq!(
2074      Proximity::query (&a, &b),
2075      Proximity {
2076        distance:  -2.0,
2077        half_axis: [ 0.0, 0.0, -1.0].into(),
2078        normal:    math::Unit3::axis_z().invert(),
2079        midpoint:  [ 0.0, 0.0,  1.0].into()
2080      }
2081    );
2082
2083    // intersection: cylinder shaft of A above cylinder shaft of B
2084    let a = object::Static {
2085      position: component::Position ([0.0, 0.0,  3.0].into()), .. a
2086    };
2087    let b = object::Static {
2088      position: component::Position ([0.0, 0.0, -3.0].into()), .. b
2089    };
2090    assert_eq!(
2091      Proximity::query (&a, &b),
2092      Proximity {
2093        distance:  -2.0,
2094        half_axis: [ 0.0, 0.0,  1.0].into(),
2095        normal:    math::Unit3::axis_z(),
2096        midpoint:  [ 0.0, 0.0, -1.0].into()
2097      }
2098    );
2099
2100    // intersection: cylinder shafts of A and B are adjacent
2101    let a = object::Static {
2102      position: component::Position ([ 1.0, 0.0, 0.0].into()), .. a
2103    };
2104    let b = object::Static {
2105      position: component::Position ([-1.0, 0.0, 0.0].into()), .. b
2106    };
2107    assert_eq!(
2108      Proximity::query (&a, &b),
2109      Proximity {
2110        distance:  -1.0,
2111        half_axis: [ 0.5, 0.0, 0.0].into(),
2112        normal:    math::Unit3::axis_x(),
2113        midpoint:  [-0.5, 0.0, 0.0].into()
2114      }
2115    );
2116
2117    // disjoint: capsule A above and to the right (+X) of capsule B
2118    let a = object::Static {
2119      position: component::Position ([ 2.0, 0.0,  5.0].into()), .. a
2120    };
2121    let b = object::Static {
2122      position: component::Position ([-1.0, 0.0, -3.0].into()), .. b
2123    };
2124
2125    let proximity = Proximity::query (&a, &b);
2126    let distance_manual = {
2127      let distance  = math::Vector3::new (3.0, 0.0, 3.0).magnitude() - 3.0;
2128      let half_axis : math::Vector3 <_> =
2129        0.5 * distance * math::Vector3::new (-1.0, 0.0, -1.0).normalized();
2130      let normal    = math::Unit3::normalize ([1.0, 0.0, 1.0].into());
2131      let midpoint  = math::Point3::from ([-1.0, 0.0, -1.0]) +
2132        *normal - half_axis;
2133      Proximity { distance, half_axis, midpoint, normal }
2134    };
2135    assert_eq!(proximity.distance, distance_manual.distance);
2136    assert_relative_eq!(proximity.half_axis, distance_manual.half_axis);
2137    assert_relative_eq!(*proximity.normal,   *distance_manual.normal);
2138    assert_relative_eq!(proximity.midpoint,  distance_manual.midpoint,
2139      epsilon = 2.0 * std::f64::EPSILON);
2140
2141    // disjoint: cylinder shafts of A and B are adjacent
2142    let a = object::Static {
2143      position: component::Position ([ 3.0, 0.0, 3.0].into()), .. a
2144    };
2145    let b = object::Static {
2146      position: component::Position ([-1.0, 0.0, 0.0].into()), .. b
2147    };
2148    assert_eq!(
2149      Proximity::query (&a, &b),
2150      Proximity {
2151        distance:  1.0,
2152        half_axis: [-0.5, 0.0, 0.0].into(),
2153        normal:    math::Unit3::axis_x(),
2154        midpoint:  [ 0.5, 0.0, 1.0].into()
2155      }
2156    );
2157
2158    //
2159    //  cuboid v. cuboid
2160    //
2161
2162    // disjoint: nearest points are corners
2163    let a = object::Static {
2164      position: component::Position ([-5.0, -5.0, -5.0].into()),
2165      bound:    component::Bound (shape::Bounded::from (
2166        shape::Cuboid::noisy ([1.0, 2.0, 3.0])).into()),
2167      .. a
2168    };
2169    let b = object::Static {
2170      position: component::Position ([5.0, 5.0, 5.0].into()),
2171      bound:    component::Bound (shape::Bounded::from (
2172        shape::Cuboid::noisy ([3.0, 1.0, 2.0])).into()),
2173      .. b
2174    };
2175    let proximity = Proximity::query (&a, &b);
2176    assert_eq!(proximity.distance, f64::sqrt (110.0));
2177    assert_eq!(proximity.half_axis, [3.0, 3.5, 2.5].into());
2178    assert_ulps_eq!(*proximity.normal,
2179      -math::Vector3::new (3.0, 3.5, 2.5).normalized());
2180    assert_eq!(proximity.midpoint,  [-1.0, 0.5, 0.5].into());
2181
2182    // disjoint: overlap on X axis only
2183    let a = object::Static {
2184      position: component::Position ([-5.0, 0.0, -5.0].into()), .. a
2185    };
2186    let b = object::Static {
2187      position: component::Position ([5.0, 0.0, 5.0].into()), .. b
2188    };
2189    let proximity = Proximity::query (&a, &b);
2190    assert_eq!(proximity.distance, f64::sqrt (61.0));
2191    assert_eq!(proximity.half_axis, [3.0, 0.0, 2.5].into());
2192    assert_ulps_eq!(*proximity.normal,
2193      -math::Vector3::new (3.0, 0.0, 2.5).normalized());
2194    assert_eq!(proximity.midpoint,  [-1.0, 0.0, 0.5].into());
2195
2196    //
2197    //  capsule v. cuboid
2198    //
2199
2200    // disjoint: capsule nearest cuboid max corner
2201    let a = object::Static {
2202      position: component::Position ([5.0, 5.0, 3.0].into()),
2203      bound:    component::Bound (shape::Bounded::from (
2204        shape::Capsule::noisy (SQRT_2, 2.0)).into()),
2205      .. a
2206    };
2207    let b = object::Static {
2208      position: component::Position ([0.0, 0.0, 0.0].into()),
2209      bound:    component::Bound (shape::Bounded::from (
2210        shape::Cuboid::noisy ([1.0, 1.0, 1.0])).into()),
2211      .. b
2212    };
2213    let proximity = Proximity::query (&a, &b);
2214    assert_ulps_eq!(proximity.distance, 3.0 * SQRT_2);
2215    assert_eq!(proximity.half_axis, [-1.5, -1.5, 0.0].into());
2216    assert_ulps_eq!(*proximity.normal,
2217      math::Vector3::new (1.0, 1.0, 0.0).normalized());
2218    assert_eq!(proximity.midpoint,  [ 2.5, 2.5, 1.0].into());
2219
2220    // disjoint: capsule nearest cuboid min corner
2221    let a = object::Static {
2222      position: component::Position ([-5.0, -5.0, -3.0].into()),
2223      bound:    component::Bound (shape::Bounded::from (
2224        shape::Capsule::noisy (SQRT_2, 2.0)).into()),
2225      .. a
2226    };
2227    let b = object::Static {
2228      position: component::Position ([0.0, 0.0, 0.0].into()),
2229      bound:    component::Bound (shape::Bounded::from (
2230        shape::Cuboid::noisy ([1.0, 1.0, 1.0])).into()),
2231      .. b
2232    };
2233    let proximity = Proximity::query (&a, &b);
2234    assert_ulps_eq!(proximity.distance, 3.0 * SQRT_2);
2235    assert_eq!(proximity.half_axis, [ 1.5,  1.5, 0.0].into());
2236    assert_ulps_eq!(*proximity.normal,
2237      math::Vector3::new (-1.0, -1.0, 0.0).normalized());
2238    assert_eq!(proximity.midpoint,  [-2.5,-2.5,-1.0].into());
2239
2240    // disjoint: capsule nearest cuboid max Y edge
2241    let a = object::Static {
2242      position: component::Position ([4.0, 0.5, 4.0].into()),
2243      bound:    component::Bound (shape::Bounded::from (
2244        shape::Capsule::noisy (1.0, 2.0)).into()),
2245      .. a
2246    };
2247    let b = object::Static {
2248      position: component::Position ([0.0, 0.0, 0.0].into()),
2249      bound:    component::Bound (shape::Bounded::from (
2250        shape::Cuboid::noisy ([2.0, 2.0, 2.0])).into()),
2251      .. b
2252    };
2253    assert_eq!(
2254      Proximity::query (&a, &b),
2255      Proximity {
2256        distance:  1.0,
2257        half_axis: [-0.5, 0.0, 0.0].into(),
2258        normal:    math::Unit3::axis_x(),
2259        midpoint:  [ 2.5, 0.5, 2.0].into()
2260      }
2261    );
2262
2263    // disjoint: capsule nearest cuboid min Y edge
2264    let a = object::Static {
2265      position: component::Position ([4.0, 0.5, -4.0].into()), .. a
2266    };
2267    let b = object::Static {
2268      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
2269    };
2270    assert_eq!(
2271      Proximity::query (&a, &b),
2272      Proximity {
2273        distance:  1.0,
2274        half_axis: [-0.5, 0.0,  0.0].into(),
2275        normal:    math::Unit3::axis_x(),
2276        midpoint:  [ 2.5, 0.5, -2.0].into()
2277      }
2278    );
2279
2280    // disjoint: capsule nearest cuboid max X edge
2281    let a = object::Static {
2282      position: component::Position ([0.5, 4.0, 4.0].into()), .. a
2283    };
2284    let b = object::Static {
2285      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
2286    };
2287    assert_eq!(
2288      Proximity::query (&a, &b),
2289      Proximity {
2290        distance:  1.0,
2291        half_axis: [ 0.0, -0.5, 0.0].into(),
2292        normal:    math::Unit3::axis_y(),
2293        midpoint:  [ 0.5,  2.5, 2.0].into()
2294      }
2295    );
2296
2297    // disjoint: capsule nearest cuboid min X edge
2298    let a = object::Static {
2299      position: component::Position ([0.5, 4.0, -4.0].into()), .. a
2300    };
2301    let b = object::Static {
2302      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
2303    };
2304    assert_eq!(
2305      Proximity::query (&a, &b),
2306      Proximity {
2307        distance:  1.0,
2308        half_axis: [ 0.0, -0.5,  0.0].into(),
2309        normal:    math::Unit3::axis_y(),
2310        midpoint:  [ 0.5,  2.5, -2.0].into()
2311      }
2312    );
2313
2314    // disjoint: capsule A above cuboid B
2315    let a = object::Static {
2316      position: component::Position ([0.5, 0.5, 6.0].into()), .. a
2317    };
2318    let b = object::Static {
2319      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
2320    };
2321    assert_eq!(
2322      Proximity::query (&a, &b),
2323      Proximity {
2324        distance:  1.0,
2325        half_axis: [ 0.0, 0.0, -0.5].into(),
2326        normal:    math::Unit3::axis_z(),
2327        midpoint:  [ 0.5, 0.5,  2.5].into()
2328      }
2329    );
2330
2331    // disjoint: capsule A below cuboid B
2332    let a = object::Static {
2333      position: component::Position ([0.5, 0.5, -6.0].into()), .. a
2334    };
2335    let b = object::Static {
2336      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
2337    };
2338    assert_eq!(
2339      Proximity::query (&a, &b),
2340      Proximity {
2341        distance:  1.0,
2342        half_axis: [ 0.0, 0.0,  0.5].into(),
2343        normal:    math::Unit3::axis_z().invert(),
2344        midpoint:  [ 0.5, 0.5, -2.5].into()
2345      }
2346    );
2347
2348    // disjoint: capsule A cylinder nearest max Z edge of cuboid B
2349    let a = object::Static {
2350      position: component::Position ([3.0, 3.0, 2.0].into()), .. a
2351    };
2352    let b = object::Static {
2353      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
2354    };
2355    let proximity = Proximity::query (&a, &b);
2356    assert_eq!(proximity.distance, SQRT_2 - 1.0);
2357    assert_eq!(proximity.half_axis, [
2358      -0.5 * f64::sqrt (0.5 * (SQRT_2 - 1.0).powf (2.0)),
2359      -0.5 * f64::sqrt (0.5 * (SQRT_2 - 1.0).powf (2.0)),
2360       0.0
2361    ].into());
2362    assert_ulps_eq!(*proximity.normal,
2363      [FRAC_1_SQRT_2, FRAC_1_SQRT_2, 0.0].into());
2364    assert_eq!(proximity.midpoint, [
2365      2.0 + 0.5 * f64::sqrt (0.5 * (SQRT_2 - 1.0).powf (2.0)),
2366      2.0 + 0.5 * f64::sqrt (0.5 * (SQRT_2 - 1.0).powf (2.0)),
2367      1.0
2368    ].into());
2369
2370    // disjoint: capsule A cylinder nearest min Z edge of cuboid B
2371    let a = object::Static {
2372      position: component::Position ([-3.0, -3.0, -2.0].into()), .. a
2373    };
2374    let b = object::Static {
2375      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
2376    };
2377    let proximity = Proximity::query (&a, &b);
2378    assert_eq!(proximity.distance, SQRT_2 - 1.0);
2379    assert_eq!(proximity.half_axis, [
2380       0.5 * f64::sqrt (0.5 * (SQRT_2 - 1.0).powf (2.0)),
2381       0.5 * f64::sqrt (0.5 * (SQRT_2 - 1.0).powf (2.0)),
2382       0.0
2383    ].into());
2384    assert_ulps_eq!(*proximity.normal,
2385      [-FRAC_1_SQRT_2, -FRAC_1_SQRT_2, 0.0].into());
2386    assert_eq!(proximity.midpoint, [
2387      -2.0 - 0.5 * f64::sqrt (0.5 * (SQRT_2 - 1.0).powf (2.0)),
2388      -2.0 - 0.5 * f64::sqrt (0.5 * (SQRT_2 - 1.0).powf (2.0)),
2389      -1.0
2390    ].into());
2391
2392    // disjoint: capsule A cylinder nearest max X/Z face of cuboid B
2393    let a = object::Static {
2394      position: component::Position ([1.0, 4.0, 2.0].into()), .. a
2395    };
2396    let b = object::Static {
2397      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
2398    };
2399    assert_eq!(
2400      Proximity::query (&a, &b),
2401      Proximity {
2402        distance:  1.0,
2403        half_axis: [ 0.0, -0.5,  0.0].into(),
2404        normal:    math::Unit3::axis_y(),
2405        midpoint:  [ 1.0,  2.5,  1.0].into()
2406      }
2407    );
2408
2409    // disjoint: capsule A cylinder nearest min X/Z face of cuboid B
2410    let a = object::Static {
2411      position: component::Position ([-1.0, -4.0, -2.0].into()), .. a
2412    };
2413    let b = object::Static {
2414      position: component::Position ([0.0, 0.0, 0.0].into()),    .. b
2415    };
2416    assert_eq!(
2417      Proximity::query (&a, &b),
2418      Proximity {
2419        distance:  1.0,
2420        half_axis: [ 0.0,  0.5,  0.0].into(),
2421        normal:    math::Unit3::axis_y().invert(),
2422        midpoint:  [-1.0, -2.5, -1.0].into()
2423      }
2424    );
2425
2426    // disjoint: capsule A cylinder nearest max Y/Z face of cuboid B
2427    let a = object::Static {
2428      position: component::Position ([4.0, 1.0, 2.0].into()), .. a
2429    };
2430    let b = object::Static {
2431      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
2432    };
2433    assert_eq!(
2434      Proximity::query (&a, &b),
2435      Proximity {
2436        distance:  1.0,
2437        half_axis: [-0.5, 0.0, 0.0].into(),
2438        normal:    math::Unit3::axis_x(),
2439        midpoint:  [ 2.5, 1.0, 1.0].into()
2440      }
2441    );
2442
2443    // disjoint: capsule A cylinder nearest min Y/Z face of cuboid B
2444    let a = object::Static {
2445      position: component::Position ([-4.0, -1.0, -2.0].into()), .. a
2446    };
2447    let b = object::Static {
2448      position: component::Position ([0.0, 0.0, 0.0].into()),    .. b
2449    };
2450    assert_eq!(
2451      Proximity::query (&a, &b),
2452      Proximity {
2453        distance:  1.0,
2454        half_axis: [ 0.5,  0.0,  0.0].into(),
2455        normal:    math::Unit3::axis_x().invert(),
2456        midpoint:  [-2.5, -1.0, -1.0].into()
2457      }
2458    );
2459
2460    // intersect: capsule cylinder shaft min equal to cuboid max corner
2461    let a = object::Static {
2462      position: component::Position ([2.0, 2.0, 4.0].into()),
2463      bound:    component::Bound (shape::Bounded::from (
2464        shape::Capsule::noisy (1.0, 2.0)).into()),
2465      .. a
2466    };
2467    let b = object::Static {
2468      position: component::Position ([0.0, 0.0, 0.0].into()),
2469      bound:    component::Bound (shape::Bounded::from (
2470        shape::Cuboid::noisy ([2.0, 2.0, 2.0])).into()),
2471      .. b
2472    };
2473    assert_eq!(
2474      Proximity::query (&a, &b),
2475      Proximity {
2476        distance:  -1.0,
2477        half_axis: [ 0.5, 0.0, 0.0].into(),
2478        normal:    math::Unit3::axis_x(),
2479        midpoint:  [ 1.5, 2.0, 2.0].into()
2480      }
2481    );
2482
2483    // intersect: capsule cylinder shaft max equal to cuboid min corner
2484    let a = object::Static {
2485      position: component::Position ([-2.0, -2.0, -4.0].into()), .. a
2486    };
2487    let b = object::Static {
2488      position: component::Position ([0.0, 0.0, 0.0].into()),  .. b
2489    };
2490    assert_eq!(
2491      Proximity::query (&a, &b),
2492      Proximity {
2493        distance:  -1.0,
2494        half_axis: [-0.5,  0.0, 0.0].into(),
2495        normal:    math::Unit3::axis_x().invert(),
2496        midpoint:  [-1.5, -2.0,-2.0].into()
2497      }
2498    );
2499
2500    // intersect: capsule cylinder shaft min equal to cuboid max Y edge
2501    let a = object::Static {
2502      position: component::Position ([2.0, 1.0, 4.0].into()), .. a
2503    };
2504    let b = object::Static {
2505      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
2506    };
2507    assert_eq!(
2508      Proximity::query (&a, &b),
2509      Proximity {
2510        distance:  -1.0,
2511        half_axis: [ 0.5, 0.0, 0.0].into(),
2512        normal:    math::Unit3::axis_x(),
2513        midpoint:  [ 1.5, 1.0, 2.0].into()
2514      }
2515    );
2516
2517    // intersect: capsule cylinder shaft max equal to cuboid min Y edge
2518    let a = object::Static {
2519      position: component::Position ([-2.0, 1.0, -4.0].into()), .. a
2520    };
2521    let b = object::Static {
2522      position: component::Position ([0.0, 0.0, 0.0].into()),  .. b
2523    };
2524    assert_eq!(
2525      Proximity::query (&a, &b),
2526      Proximity {
2527        distance:  -1.0,
2528        half_axis: [-0.5, 0.0,  0.0].into(),
2529        normal:    math::Unit3::axis_x().invert(),
2530        midpoint:  [-1.5, 1.0, -2.0].into()
2531      }
2532    );
2533
2534    // intersect: capsule cylinder shaft min equal to cuboid max X edge
2535    let a = object::Static {
2536      position: component::Position ([1.0, 2.0, 4.0].into()), .. a
2537    };
2538    let b = object::Static {
2539      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
2540    };
2541    assert_eq!(
2542      Proximity::query (&a, &b),
2543      Proximity {
2544        distance:  -1.0,
2545        half_axis: [ 0.0, 0.5, 0.0].into(),
2546        normal:    math::Unit3::axis_y(),
2547        midpoint:  [ 1.0, 1.5, 2.0].into()
2548      }
2549    );
2550
2551    // intersect: capsule cylinder shaft max equal to cuboid min X edge
2552    let a = object::Static {
2553      position: component::Position ([ 1.0, -2.0, -4.0].into()), .. a
2554    };
2555    let b = object::Static {
2556      position: component::Position ([0.0, 0.0, 0.0].into()),  .. b
2557    };
2558    assert_eq!(
2559      Proximity::query (&a, &b),
2560      Proximity {
2561        distance:  -1.0,
2562        half_axis: [0.0, -0.5,  0.0].into(),
2563        normal:    math::Unit3::axis_y().invert(),
2564        midpoint:  [1.0, -1.5, -2.0].into()
2565      }
2566    );
2567
2568    // intersect: capsule cylinder shaft min equal to cuboid max Z face
2569    let a = object::Static {
2570      position: component::Position ([1.0, 1.0, 4.0].into()), .. a
2571    };
2572    let b = object::Static {
2573      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
2574    };
2575    assert_eq!(
2576      Proximity::query (&a, &b),
2577      Proximity {
2578        distance:  -1.0,
2579        half_axis: [ 0.0, 0.0, 0.5].into(),
2580        normal:    math::Unit3::axis_z(),
2581        midpoint:  [ 1.0, 1.0, 1.5].into()
2582      }
2583    );
2584
2585    // intersect: capsule cylinder shaft max equal to cuboid min Z face
2586    let a = object::Static {
2587      position: component::Position ([-1.0, -1.0, -4.0].into()), .. a
2588    };
2589    let b = object::Static {
2590      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
2591    };
2592    assert_eq!(
2593      Proximity::query (&a, &b),
2594      Proximity {
2595        distance:  -1.0,
2596        half_axis: [ 0.0,  0.0, -0.5].into(),
2597        normal:    math::Unit3::axis_z().invert(),
2598        midpoint:  [-1.0, -1.0, -1.5].into()
2599      }
2600    );
2601
2602    // intersect: capsule and cuboid position coincide
2603    let a = object::Static {
2604      position: component::Position ([0.0, 0.0, 0.0].into()), .. a
2605    };
2606    let b = object::Static {
2607      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
2608    };
2609    assert_eq!(
2610      Proximity::query (&a, &b),
2611      Proximity {
2612        distance:  -3.0,
2613        half_axis: [ 1.5,  0.0,  0.0].into(),
2614        normal:    math::Unit3::axis_x(),
2615        midpoint:  [ 0.5,  0.0,  0.0].into()
2616      }
2617    );
2618
2619    // intersect: capsule nearest positive X face of cuboid
2620    let a = object::Static {
2621      position: component::Position ([1.0, 0.0, 0.0].into()), .. a
2622    };
2623    let b = object::Static {
2624      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
2625    };
2626    assert_eq!(
2627      Proximity::query (&a, &b),
2628      Proximity {
2629        distance:  -2.0,
2630        half_axis: [ 1.0,  0.0,  0.0].into(),
2631        normal:    math::Unit3::axis_x(),
2632        midpoint:  [ 1.0,  0.0,  0.0].into()
2633      }
2634    );
2635
2636    // intersect: capsule nearest negative X face of cuboid
2637    let a = object::Static {
2638      position: component::Position ([-1.0, 0.0, 0.0].into()), .. a
2639    };
2640    let b = object::Static {
2641      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
2642    };
2643    assert_eq!(
2644      Proximity::query (&a, &b),
2645      Proximity {
2646        distance:  -2.0,
2647        half_axis: [-1.0,  0.0,  0.0].into(),
2648        normal:    math::Unit3::axis_x().invert(),
2649        midpoint:  [-1.0,  0.0,  0.0].into()
2650      }
2651    );
2652
2653    // intersect: capsule nearest positive Y face of cuboid
2654    let a = object::Static {
2655      position: component::Position ([0.0, 1.0, 0.0].into()), .. a
2656    };
2657    let b = object::Static {
2658      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
2659    };
2660    assert_eq!(
2661      Proximity::query (&a, &b),
2662      Proximity {
2663        distance:  -2.0,
2664        half_axis: [0.0, 1.0, 0.0].into(),
2665        normal:    math::Unit3::axis_y(),
2666        midpoint:  [0.0, 1.0, 0.0].into()
2667      }
2668    );
2669
2670    // intersect: capsule nearest negative Y face of cuboid
2671    let a = object::Static {
2672      position: component::Position ([0.0, -1.0, 0.0].into()), .. a
2673    };
2674    let b = object::Static {
2675      position: component::Position ([0.0, 0.0, 0.0].into()), .. b
2676    };
2677    assert_eq!(
2678      Proximity::query (&a, &b),
2679      Proximity {
2680        distance:  -2.0,
2681        half_axis: [0.0, -1.0, 0.0].into(),
2682        normal:    math::Unit3::axis_y().invert(),
2683        midpoint:  [0.0, -1.0, 0.0].into()
2684      }
2685    );
2686
2687    // intersect: capsule nearest positive Z face of cuboid
2688    let a = object::Static {
2689      position: component::Position ([0.0, 0.0, 3.0].into()),
2690      bound:    component::Bound (shape::Bounded::from (
2691        shape::Capsule::noisy (1.0, 2.0)).into()),
2692      .. a
2693    };
2694    let b = object::Static {
2695      position: component::Position ([0.0, 0.0, 0.0].into()),
2696      bound:    component::Bound (shape::Bounded::from (
2697        shape::Cuboid::noisy ([4.0, 4.0, 2.0])).into()),
2698      .. b
2699    };
2700    assert_eq!(
2701      Proximity::query (&a, &b),
2702      Proximity {
2703        distance:  -2.0,
2704        half_axis: [0.0, 0.0, 1.0].into(),
2705        normal:    math::Unit3::axis_z(),
2706        midpoint:  [0.0, 0.0, 1.0].into()
2707      }
2708    );
2709
2710    // intersect: capsule nearest positive Z face of cuboid
2711    let a = object::Static {
2712      position: component::Position ([0.0, 0.0, 1.0].into()),
2713      bound:    component::Bound (shape::Bounded::from (
2714        shape::Capsule::noisy (1.0, 2.0)).into()),
2715      .. a
2716    };
2717    let b = object::Static {
2718      position: component::Position ([0.0, 0.0, 0.0].into()),
2719      bound:    component::Bound (shape::Bounded::from (
2720        shape::Cuboid::noisy ([4.0, 4.0, 2.0])).into()),
2721      .. b
2722    };
2723    assert_eq!(
2724      Proximity::query (&a, &b),
2725      Proximity {
2726        distance:  -4.0,
2727        half_axis: [0.0, 0.0, 2.0].into(),
2728        normal:    math::Unit3::axis_z(),
2729        midpoint:  [0.0, 0.0, 0.0].into()
2730      }
2731    );
2732
2733    // intersect: capsule nearest negative Z face of cuboid
2734    let a = object::Static {
2735      position: component::Position ([0.0, 0.0, -3.0].into()),
2736      bound:    component::Bound (shape::Bounded::from (
2737        shape::Capsule::noisy (1.0, 2.0)).into()),
2738      .. a
2739    };
2740    let b = object::Static {
2741      position: component::Position ([0.0, 0.0, 0.0].into()),
2742      bound:    component::Bound (shape::Bounded::from (
2743        shape::Cuboid::noisy ([4.0, 4.0, 2.0])).into()),
2744      .. b
2745    };
2746    assert_eq!(
2747      Proximity::query (&a, &b),
2748      Proximity {
2749        distance:  -2.0,
2750        half_axis: [0.0, 0.0, -1.0].into(),
2751        normal:    math::Unit3::axis_z().invert(),
2752        midpoint:  [0.0, 0.0, -1.0].into()
2753      }
2754    );
2755
2756    // intersect: capsule nearest negative Z face of cuboid
2757    let a = object::Static {
2758      position: component::Position ([0.0, 0.0, -1.0].into()),
2759      bound:    component::Bound (shape::Bounded::from (
2760        shape::Capsule::noisy (1.0, 2.0)).into()),
2761      .. a
2762    };
2763    let b = object::Static {
2764      position: component::Position ([0.0, 0.0, 0.0].into()),
2765      bound:    component::Bound (shape::Bounded::from (
2766        shape::Cuboid::noisy ([4.0, 4.0, 2.0])).into()),
2767      .. b
2768    };
2769    assert_eq!(
2770      Proximity::query (&a, &b),
2771      Proximity {
2772        distance:  -4.0,
2773        half_axis: [0.0, 0.0, -2.0].into(),
2774        normal:    math::Unit3::axis_z().invert(),
2775        midpoint:  [0.0, 0.0,  0.0].into()
2776      }
2777    );
2778
2779    // random queries
2780    let mut rng = XorShiftRng::seed_from_u64 (0);
2781    let a = object::Static {
2782      position: component::Position ([0.0, 0.0, 0.0].into()),
2783      bound:    component::Bound (shape::Bounded::from (
2784        shape::Capsule::noisy (1.5, 1.5)).into()),
2785      .. a
2786    };
2787    for _ in 0..100 {
2788      let position = component::Position ([
2789        rng.random_range (-40.0..40.0),
2790        rng.random_range (-40.0..40.0),
2791        rng.random_range (-40.0..40.0)
2792      ].into());
2793      let bound    = component::Bound (shape::Bounded::from (
2794        shape::Cuboid::noisy ([
2795          rng.random_range (0.5..5.0),
2796          rng.random_range (0.5..5.0),
2797          rng.random_range (0.5..5.0)
2798        ])
2799      ).into());
2800      let b = object::Static {
2801        position, bound, material: component::MATERIAL_STONE, collidable: true
2802      };
2803      let _ = Proximity::query (&a, &b);
2804    }
2805
2806    //
2807    //  capsule v. orthant
2808    //
2809    use math::SignedAxis3;
2810
2811    // disjoint: capsule outside of orthant +Z
2812    let a = object::Static {
2813      position: component::Position ([0.0, 0.0, 4.0].into()),
2814      bound:    component::Bound (shape::Bounded::from (
2815        shape::Capsule::noisy (1.0, 2.0)).into()),
2816      .. a
2817    };
2818    let b = object::Static {
2819      position: component::Position ([0.0, 0.0, 0.0].into()),
2820      bound:    component::Bound (shape::Unbounded::from (
2821        shape::Orthant { normal_axis: SignedAxis3::PosZ }).into()),
2822      .. b
2823    };
2824    assert_eq!(
2825      Proximity::query (&a, &b),
2826      Proximity {
2827        distance:  1.0,
2828        half_axis: [0.0, 0.0, -0.5].into(),
2829        normal:    math::Unit3::axis_z(),
2830        midpoint:  [0.0, 0.0,  0.5].into()
2831      }
2832    );
2833
2834    // disjoint: capsule outside of orthant -Z
2835    let a = object::Static {
2836      position: component::Position ([0.0, 0.0, -4.0].into()),
2837      bound:    component::Bound (shape::Bounded::from (
2838        shape::Capsule::noisy (1.0, 2.0)).into()),
2839      .. a
2840    };
2841    let b = object::Static {
2842      position: component::Position ([0.0, 0.0, 0.0].into()),
2843      bound:    component::Bound (shape::Unbounded::from (
2844        shape::Orthant { normal_axis: SignedAxis3::NegZ }).into()),
2845      .. b
2846    };
2847    assert_eq!(
2848      Proximity::query (&a, &b),
2849      Proximity {
2850        distance:  1.0,
2851        half_axis: [0.0, 0.0,  0.5].into(),
2852        normal:    math::Unit3::axis_z().invert(),
2853        midpoint:  [0.0, 0.0, -0.5].into()
2854      }
2855    );
2856
2857    // disjoint: capsule outside orthant +Y
2858    let a = object::Static {
2859      position: component::Position ([0.0, 2.0, 0.0].into()),
2860      bound:    component::Bound (shape::Bounded::from (
2861        shape::Capsule::noisy (1.0, 2.0)).into()),
2862      .. a
2863    };
2864    let b = object::Static {
2865      position: component::Position ([0.0, 0.0, 0.0].into()),
2866      bound:    component::Bound (shape::Unbounded::from (
2867        shape::Orthant { normal_axis: SignedAxis3::PosY }).into()),
2868      .. b
2869    };
2870    assert_eq!(
2871      Proximity::query (&a, &b),
2872      Proximity {
2873        distance:  1.0,
2874        half_axis: [0.0, -0.5, 0.0].into(),
2875        normal:    math::Unit3::axis_y(),
2876        midpoint:  [0.0,  0.5, 0.0].into()
2877      }
2878    );
2879
2880    // disjoint: capsule outside orthant -Y
2881    let a = object::Static {
2882      position: component::Position ([0.0, -2.0, 0.0].into()),
2883      bound:    component::Bound (shape::Bounded::from (
2884        shape::Capsule::noisy (1.0, 2.0)).into()),
2885      .. a
2886    };
2887    let b = object::Static {
2888      position: component::Position ([0.0, 0.0, 0.0].into()),
2889      bound:    component::Bound (shape::Unbounded::from (
2890        shape::Orthant { normal_axis: SignedAxis3::NegY }).into()),
2891      .. b
2892    };
2893    assert_eq!(
2894      Proximity::query (&a, &b),
2895      Proximity {
2896        distance:  1.0,
2897        half_axis: [0.0,  0.5, 0.0].into(),
2898        normal:    math::Unit3::axis_y().invert(),
2899        midpoint:  [0.0, -0.5, 0.0].into()
2900      }
2901    );
2902
2903    // disjoint: capsule outside orthant +X
2904    let a = object::Static {
2905      position: component::Position ([2.0, 0.0, 0.0].into()),
2906      bound:    component::Bound (shape::Bounded::from (
2907        shape::Capsule::noisy (1.0, 2.0)).into()),
2908      .. a
2909    };
2910    let b = object::Static {
2911      position: component::Position ([0.0, 0.0, 0.0].into()),
2912      bound:    component::Bound (shape::Unbounded::from (
2913        shape::Orthant { normal_axis: SignedAxis3::PosX }).into()),
2914      .. b
2915    };
2916    assert_eq!(
2917      Proximity::query (&a, &b),
2918      Proximity {
2919        distance:  1.0,
2920        half_axis: [-0.5, 0.0, 0.0].into(),
2921        normal:    math::Unit3::axis_x(),
2922        midpoint:  [ 0.5, 0.0, 0.0].into()
2923      }
2924    );
2925
2926    // disjoint: outside orthant -X
2927    let a = object::Static {
2928      position: component::Position ([-2.0, 0.0, 0.0].into()),
2929      bound:    component::Bound (shape::Bounded::from (
2930        shape::Capsule::noisy (1.0, 2.0)).into()),
2931      .. a
2932    };
2933    let b = object::Static {
2934      position: component::Position ([0.0, 0.0, 0.0].into()),
2935      bound:    component::Bound (shape::Unbounded::from (
2936        shape::Orthant { normal_axis: SignedAxis3::NegX }).into()),
2937      .. b
2938    };
2939    assert_eq!(
2940      Proximity::query (&a, &b),
2941      Proximity {
2942        distance:  1.0,
2943        half_axis: [ 0.5, 0.0, 0.0].into(),
2944        normal:    math::Unit3::axis_x().invert(),
2945        midpoint:  [-0.5, 0.0, 0.0].into()
2946      }
2947    );
2948
2949    // intersect: capsule and orthant position coincide; normal +Z
2950    let a = object::Static {
2951      position: component::Position ([0.0, 0.0, 0.0].into()),
2952      bound:    component::Bound (shape::Bounded::from (
2953        shape::Capsule::noisy (1.0, 2.0)).into()),
2954      .. a
2955    };
2956    let b = object::Static {
2957      position: component::Position ([0.0, 0.0, 0.0].into()),
2958      bound:    component::Bound (shape::Unbounded::from (
2959        shape::Orthant { normal_axis: SignedAxis3::PosZ }).into()),
2960      .. b
2961    };
2962    assert_eq!(
2963      Proximity::query (&a, &b),
2964      Proximity {
2965        distance:  -3.0,
2966        half_axis: [0.0, 0.0,  1.5].into(),
2967        normal:    math::Unit3::axis_z(),
2968        midpoint:  [0.0, 0.0, -1.5].into()
2969      }
2970    );
2971
2972    // intersect: capsule and orthant position coincide; normal -Z
2973    let a = object::Static {
2974      position: component::Position ([0.0, 0.0, 0.0].into()),
2975      bound:    component::Bound (shape::Bounded::from (
2976        shape::Capsule::noisy (1.0, 2.0)).into()),
2977      .. a
2978    };
2979    let b = object::Static {
2980      position: component::Position ([0.0, 0.0, 0.0].into()),
2981      bound:    component::Bound (shape::Unbounded::from (
2982        shape::Orthant { normal_axis: SignedAxis3::NegZ }).into()),
2983      .. b
2984    };
2985    assert_eq!(
2986      Proximity::query (&a, &b),
2987      Proximity {
2988        distance:  -3.0,
2989        half_axis: [0.0, 0.0, -1.5].into(),
2990        normal:    math::Unit3::axis_z().invert(),
2991        midpoint:  [0.0, 0.0,  1.5].into()
2992      }
2993    );
2994
2995    // intersect: capsule and orthant position coincide; normal +Y
2996    let a = object::Static {
2997      position: component::Position ([0.0, 0.0, 0.0].into()),
2998      bound:    component::Bound (shape::Bounded::from (
2999        shape::Capsule::noisy (1.0, 2.0)).into()),
3000      .. a
3001    };
3002    let b = object::Static {
3003      position: component::Position ([0.0, 0.0, 0.0].into()),
3004      bound:    component::Bound (shape::Unbounded::from (
3005        shape::Orthant { normal_axis: SignedAxis3::PosY }).into()),
3006      .. b
3007    };
3008    assert_eq!(
3009      Proximity::query (&a, &b),
3010      Proximity {
3011        distance:  -1.0,
3012        half_axis: [0.0,  0.5, 0.0].into(),
3013        normal:    math::Unit3::axis_y(),
3014        midpoint:  [0.0, -0.5, 0.0].into()
3015      }
3016    );
3017
3018    // intersect: capsule and orthant position coincide; normal -Y
3019    let a = object::Static {
3020      position: component::Position ([0.0, 0.0, 0.0].into()),
3021      bound:    component::Bound (shape::Bounded::from (
3022        shape::Capsule::noisy (1.0, 2.0)).into()),
3023      .. a
3024    };
3025    let b = object::Static {
3026      position: component::Position ([0.0, 0.0, 0.0].into()),
3027      bound:    component::Bound (shape::Unbounded::from (
3028        shape::Orthant { normal_axis: SignedAxis3::NegY }).into()),
3029      .. b
3030    };
3031    assert_eq!(
3032      Proximity::query (&a, &b),
3033      Proximity {
3034        distance:  -1.0,
3035        half_axis: [0.0, -0.5, 0.0].into(),
3036        normal:    math::Unit3::axis_y().invert(),
3037        midpoint:  [0.0,  0.5, 0.0].into()
3038      }
3039    );
3040
3041    // intersect: capsule and orthant position coincide; normal +X
3042    let a = object::Static {
3043      position: component::Position ([0.0, 0.0, 0.0].into()),
3044      bound:    component::Bound (shape::Bounded::from (
3045        shape::Capsule::noisy (1.0, 2.0)).into()),
3046      .. a
3047    };
3048    let b = object::Static {
3049      position: component::Position ([0.0, 0.0, 0.0].into()),
3050      bound:    component::Bound (shape::Unbounded::from (
3051        shape::Orthant { normal_axis: SignedAxis3::PosX }).into()),
3052      .. b
3053    };
3054    assert_eq!(
3055      Proximity::query (&a, &b),
3056      Proximity {
3057        distance:  -1.0,
3058        half_axis: [ 0.5, 0.0, 0.0].into(),
3059        normal:    math::Unit3::axis_x(),
3060        midpoint:  [-0.5, 0.0, 0.0].into()
3061      }
3062    );
3063
3064    // intersect: capsule and orthant position coincide; normal -X
3065    let a = object::Static {
3066      position: component::Position ([0.0, 0.0, 0.0].into()),
3067      bound:    component::Bound (shape::Bounded::from (
3068        shape::Capsule::noisy (1.0, 2.0)).into()),
3069      .. a
3070    };
3071    let b = object::Static {
3072      position: component::Position ([0.0, 0.0, 0.0].into()),
3073      bound:    component::Bound (shape::Unbounded::from (
3074        shape::Orthant { normal_axis: SignedAxis3::NegX }).into()),
3075      .. b
3076    };
3077    assert_eq!(
3078      Proximity::query (&a, &b),
3079      Proximity {
3080        distance:  -1.0,
3081        half_axis: [-0.5, 0.0, 0.0].into(),
3082        normal:    math::Unit3::axis_x().invert(),
3083        midpoint:  [ 0.5, 0.0, 0.0].into()
3084      }
3085    );
3086
3087    //
3088    //  sphere v. orthant
3089    //
3090
3091    // disjoint: sphere outside of orthant +Z
3092    let a = object::Static {
3093      position: component::Position ([0.0, 0.0, 4.0].into()),
3094      bound:    component::Bound (shape::Bounded::from (
3095        shape::Sphere::noisy (1.0)).into()),
3096      .. a
3097    };
3098    let b = object::Static {
3099      position: component::Position ([0.0, 0.0, 0.0].into()),
3100      bound:    component::Bound (shape::Unbounded::from (
3101        shape::Orthant { normal_axis: SignedAxis3::PosZ }).into()),
3102      .. b
3103    };
3104    assert_eq!(
3105      Proximity::query (&a, &b),
3106      Proximity {
3107        distance:  3.0,
3108        half_axis: [0.0, 0.0, -1.5].into(),
3109        normal:    math::Unit3::axis_z(),
3110        midpoint:  [0.0, 0.0,  1.5].into()
3111      }
3112    );
3113
3114    // disjoint: sphere outside of orthant -Z
3115    let a = object::Static {
3116      position: component::Position ([0.0, 0.0, -4.0].into()),
3117      bound:    component::Bound (shape::Bounded::from (
3118        shape::Sphere::noisy (1.0)).into()),
3119      .. a
3120    };
3121    let b = object::Static {
3122      position: component::Position ([0.0, 0.0, 0.0].into()),
3123      bound:    component::Bound (shape::Unbounded::from (
3124        shape::Orthant { normal_axis: SignedAxis3::NegZ }).into()),
3125      .. b
3126    };
3127    assert_eq!(
3128      Proximity::query (&a, &b),
3129      Proximity {
3130        distance:  3.0,
3131        half_axis: [0.0, 0.0,  1.5].into(),
3132        normal:    math::Unit3::axis_z().invert(),
3133        midpoint:  [0.0, 0.0, -1.5].into()
3134      }
3135    );
3136
3137    // disjoint: sphere outside orthant +Y
3138    let a = object::Static {
3139      position: component::Position ([0.0, 2.0, 0.0].into()),
3140      bound:    component::Bound (shape::Bounded::from (
3141        shape::Sphere::noisy (1.0)).into()),
3142      .. a
3143    };
3144    let b = object::Static {
3145      position: component::Position ([0.0, 0.0, 0.0].into()),
3146      bound:    component::Bound (shape::Unbounded::from (
3147        shape::Orthant { normal_axis: SignedAxis3::PosY }).into()),
3148      .. b
3149    };
3150    assert_eq!(
3151      Proximity::query (&a, &b),
3152      Proximity {
3153        distance:  1.0,
3154        half_axis: [0.0, -0.5, 0.0].into(),
3155        normal:    math::Unit3::axis_y(),
3156        midpoint:  [0.0,  0.5, 0.0].into()
3157      }
3158    );
3159
3160    // disjoint: sphere outside orthant -Y
3161    let a = object::Static {
3162      position: component::Position ([0.0, -2.0, 0.0].into()),
3163      bound:    component::Bound (shape::Bounded::from (
3164        shape::Sphere::noisy (1.0)).into()),
3165      .. a
3166    };
3167    let b = object::Static {
3168      position: component::Position ([0.0, 0.0, 0.0].into()),
3169      bound:    component::Bound (shape::Unbounded::from (
3170        shape::Orthant { normal_axis: SignedAxis3::NegY }).into()),
3171      .. b
3172    };
3173    assert_eq!(
3174      Proximity::query (&a, &b),
3175      Proximity {
3176        distance:  1.0,
3177        half_axis: [0.0,  0.5, 0.0].into(),
3178        normal:    math::Unit3::axis_y().invert(),
3179        midpoint:  [0.0, -0.5, 0.0].into()
3180      }
3181    );
3182
3183    // disjoint: sphere outside orthant +X
3184    let a = object::Static {
3185      position: component::Position ([2.0, 0.0, 0.0].into()),
3186      bound:    component::Bound (shape::Bounded::from (
3187        shape::Sphere::noisy (1.0)).into()),
3188      .. a
3189    };
3190    let b = object::Static {
3191      position: component::Position ([0.0, 0.0, 0.0].into()),
3192      bound:    component::Bound (shape::Unbounded::from (
3193        shape::Orthant { normal_axis: SignedAxis3::PosX }).into()),
3194      .. b
3195    };
3196    assert_eq!(
3197      Proximity::query (&a, &b),
3198      Proximity {
3199        distance:  1.0,
3200        half_axis: [-0.5, 0.0, 0.0].into(),
3201        normal:    math::Unit3::axis_x(),
3202        midpoint:  [ 0.5, 0.0, 0.0].into()
3203      }
3204    );
3205
3206    // disjoint: outside orthant -X
3207    let a = object::Static {
3208      position: component::Position ([-2.0, 0.0, 0.0].into()),
3209      bound:    component::Bound (shape::Bounded::from (
3210        shape::Sphere::noisy (1.0)).into()),
3211      .. a
3212    };
3213    let b = object::Static {
3214      position: component::Position ([0.0, 0.0, 0.0].into()),
3215      bound:    component::Bound (shape::Unbounded::from (
3216        shape::Orthant { normal_axis: SignedAxis3::NegX }).into()),
3217      .. b
3218    };
3219    assert_eq!(
3220      Proximity::query (&a, &b),
3221      Proximity {
3222        distance:  1.0,
3223        half_axis: [ 0.5, 0.0, 0.0].into(),
3224        normal:    math::Unit3::axis_x().invert(),
3225        midpoint:  [-0.5, 0.0, 0.0].into()
3226      }
3227    );
3228
3229    // intersect: sphere and orthant position coincide; normal +Z
3230    let a = object::Static {
3231      position: component::Position ([0.0, 0.0, 0.0].into()),
3232      bound:    component::Bound (shape::Bounded::from (
3233        shape::Sphere::noisy (1.0)).into()),
3234      .. a
3235    };
3236    let b = object::Static {
3237      position: component::Position ([0.0, 0.0, 0.0].into()),
3238      bound:    component::Bound (shape::Unbounded::from (
3239        shape::Orthant { normal_axis: SignedAxis3::PosZ }).into()),
3240      .. b
3241    };
3242    assert_eq!(
3243      Proximity::query (&a, &b),
3244      Proximity {
3245        distance:  -1.0,
3246        half_axis: [0.0, 0.0,  0.5].into(),
3247        normal:    math::Unit3::axis_z(),
3248        midpoint:  [0.0, 0.0, -0.5].into()
3249      }
3250    );
3251
3252    // intersect: sphere and orthant position coincide; normal -Z
3253    let a = object::Static {
3254      position: component::Position ([0.0, 0.0, 0.0].into()),
3255      bound:    component::Bound (shape::Bounded::from (
3256        shape::Sphere::noisy (1.0)).into()),
3257      .. a
3258    };
3259    let b = object::Static {
3260      position: component::Position ([0.0, 0.0, 0.0].into()),
3261      bound:    component::Bound (shape::Unbounded::from (
3262        shape::Orthant { normal_axis: SignedAxis3::NegZ }).into()),
3263      .. b
3264    };
3265    assert_eq!(
3266      Proximity::query (&a, &b),
3267      Proximity {
3268        distance:  -1.0,
3269        half_axis: [0.0, 0.0, -0.5].into(),
3270        normal:    math::Unit3::axis_z().invert(),
3271        midpoint:  [0.0, 0.0,  0.5].into()
3272      }
3273    );
3274
3275    // intersect: sphere and orthant position coincide; normal +Y
3276    let a = object::Static {
3277      position: component::Position ([0.0, 0.0, 0.0].into()),
3278      bound:    component::Bound (shape::Bounded::from (
3279        shape::Sphere::noisy (1.0)).into()),
3280      .. a
3281    };
3282    let b = object::Static {
3283      position: component::Position ([0.0, 0.0, 0.0].into()),
3284      bound:    component::Bound (shape::Unbounded::from (
3285        shape::Orthant { normal_axis: SignedAxis3::PosY }).into()),
3286      .. b
3287    };
3288    assert_eq!(
3289      Proximity::query (&a, &b),
3290      Proximity {
3291        distance:  -1.0,
3292        half_axis: [0.0,  0.5, 0.0].into(),
3293        normal:    math::Unit3::axis_y(),
3294        midpoint:  [0.0, -0.5, 0.0].into()
3295      }
3296    );
3297
3298    // intersect: sphere and orthant position coincide; normal -Y
3299    let a = object::Static {
3300      position: component::Position ([0.0, 0.0, 0.0].into()),
3301      bound:    component::Bound (shape::Bounded::from (
3302        shape::Sphere::noisy (1.0)).into()),
3303      .. a
3304    };
3305    let b = object::Static {
3306      position: component::Position ([0.0, 0.0, 0.0].into()),
3307      bound:    component::Bound (shape::Unbounded::from (
3308        shape::Orthant { normal_axis: SignedAxis3::NegY }).into()),
3309      .. b
3310    };
3311    assert_eq!(
3312      Proximity::query (&a, &b),
3313      Proximity {
3314        distance:  -1.0,
3315        half_axis: [0.0, -0.5, 0.0].into(),
3316        normal:    math::Unit3::axis_y().invert(),
3317        midpoint:  [0.0,  0.5, 0.0].into()
3318      }
3319    );
3320
3321    // intersect: sphere and orthant position coincide; normal +X
3322    let a = object::Static {
3323      position: component::Position ([0.0, 0.0, 0.0].into()),
3324      bound:    component::Bound (shape::Bounded::from (
3325        shape::Sphere::noisy (1.0)).into()),
3326      .. a
3327    };
3328    let b = object::Static {
3329      position: component::Position ([0.0, 0.0, 0.0].into()),
3330      bound:    component::Bound (shape::Unbounded::from (
3331        shape::Orthant { normal_axis: SignedAxis3::PosX }).into()),
3332      .. b
3333    };
3334    assert_eq!(
3335      Proximity::query (&a, &b),
3336      Proximity {
3337        distance:  -1.0,
3338        half_axis: [ 0.5, 0.0, 0.0].into(),
3339        normal:    math::Unit3::axis_x(),
3340        midpoint:  [-0.5, 0.0, 0.0].into()
3341      }
3342    );
3343
3344    // intersect: sphere and orthant position coincide; normal -X
3345    let a = object::Static {
3346      position: component::Position ([0.0, 0.0, 0.0].into()),
3347      bound:    component::Bound (shape::Bounded::from (
3348        shape::Sphere::noisy (1.0)).into()),
3349      .. a
3350    };
3351    let b = object::Static {
3352      position: component::Position ([0.0, 0.0, 0.0].into()),
3353      bound:    component::Bound (shape::Unbounded::from (
3354        shape::Orthant { normal_axis: SignedAxis3::NegX }).into()),
3355      .. b
3356    };
3357    assert_eq!(
3358      Proximity::query (&a, &b),
3359      Proximity {
3360        distance:  -1.0,
3361        half_axis: [-0.5, 0.0, 0.0].into(),
3362        normal:    math::Unit3::axis_x().invert(),
3363        midpoint:  [ 0.5, 0.0, 0.0].into()
3364      }
3365    );
3366
3367    //
3368    //  cuboid v. orthant
3369    //
3370
3371    // disjoint: cuboid outside of orthant +Z
3372    let a = object::Static {
3373      position: component::Position ([0.0, 0.0, 4.0].into()),
3374      bound:    component::Bound (shape::Bounded::from (
3375        shape::Cuboid::noisy ([1.0, 1.0, 1.0])).into()),
3376      .. a
3377    };
3378    let b = object::Static {
3379      position: component::Position ([0.0, 0.0, 0.0].into()),
3380      bound:    component::Bound (shape::Unbounded::from (
3381        shape::Orthant { normal_axis: SignedAxis3::PosZ }).into()),
3382      .. b
3383    };
3384    assert_eq!(
3385      Proximity::query (&a, &b),
3386      Proximity {
3387        distance:  3.0,
3388        half_axis: [0.0, 0.0, -1.5].into(),
3389        normal:    math::Unit3::axis_z(),
3390        midpoint:  [0.0, 0.0,  1.5].into()
3391      }
3392    );
3393
3394    // disjoint: cuboid outside of orthant -Z
3395    let a = object::Static {
3396      position: component::Position ([0.0, 0.0, -4.0].into()),
3397      bound:    component::Bound (shape::Bounded::from (
3398        shape::Cuboid::noisy ([1.0, 1.0, 1.0])).into()),
3399      .. a
3400    };
3401    let b = object::Static {
3402      position: component::Position ([0.0, 0.0, 0.0].into()),
3403      bound:    component::Bound (shape::Unbounded::from (
3404        shape::Orthant { normal_axis: SignedAxis3::NegZ }).into()),
3405      .. b
3406    };
3407    assert_eq!(
3408      Proximity::query (&a, &b),
3409      Proximity {
3410        distance:  3.0,
3411        half_axis: [0.0, 0.0,  1.5].into(),
3412        normal:    math::Unit3::axis_z().invert(),
3413        midpoint:  [0.0, 0.0, -1.5].into()
3414      }
3415    );
3416
3417    // disjoint: cuboid outside orthant +Y
3418    let a = object::Static {
3419      position: component::Position ([0.0, 2.0, 0.0].into()),
3420      bound:    component::Bound (shape::Bounded::from (
3421        shape::Cuboid::noisy ([1.0, 1.0, 1.0])).into()),
3422      .. a
3423    };
3424    let b = object::Static {
3425      position: component::Position ([0.0, 0.0, 0.0].into()),
3426      bound:    component::Bound (shape::Unbounded::from (
3427        shape::Orthant { normal_axis: SignedAxis3::PosY }).into()),
3428      .. b
3429    };
3430    assert_eq!(
3431      Proximity::query (&a, &b),
3432      Proximity {
3433        distance:  1.0,
3434        half_axis: [0.0, -0.5, 0.0].into(),
3435        normal:    math::Unit3::axis_y(),
3436        midpoint:  [0.0,  0.5, 0.0].into()
3437      }
3438    );
3439
3440    // disjoint: cuboid outside orthant -Y
3441    let a = object::Static {
3442      position: component::Position ([0.0, -2.0, 0.0].into()),
3443      bound:    component::Bound (shape::Bounded::from (
3444        shape::Cuboid::noisy ([1.0, 1.0, 1.0])).into()),
3445      .. a
3446    };
3447    let b = object::Static {
3448      position: component::Position ([0.0, 0.0, 0.0].into()),
3449      bound:    component::Bound (shape::Unbounded::from (
3450        shape::Orthant { normal_axis: SignedAxis3::NegY }).into()),
3451      .. b
3452    };
3453    assert_eq!(
3454      Proximity::query (&a, &b),
3455      Proximity {
3456        distance:  1.0,
3457        half_axis: [0.0,  0.5, 0.0].into(),
3458        normal:    math::Unit3::axis_y().invert(),
3459        midpoint:  [0.0, -0.5, 0.0].into()
3460      }
3461    );
3462
3463    // disjoint: cuboid outside orthant +X
3464    let a = object::Static {
3465      position: component::Position ([2.0, 0.0, 0.0].into()),
3466      bound:    component::Bound (shape::Bounded::from (
3467        shape::Cuboid::noisy ([1.0, 1.0, 1.0])).into()),
3468      .. a
3469    };
3470    let b = object::Static {
3471      position: component::Position ([0.0, 0.0, 0.0].into()),
3472      bound:    component::Bound (shape::Unbounded::from (
3473        shape::Orthant { normal_axis: SignedAxis3::PosX }).into()),
3474      .. b
3475    };
3476    assert_eq!(
3477      Proximity::query (&a, &b),
3478      Proximity {
3479        distance:  1.0,
3480        half_axis: [-0.5, 0.0, 0.0].into(),
3481        normal:    math::Unit3::axis_x(),
3482        midpoint:  [ 0.5, 0.0, 0.0].into()
3483      }
3484    );
3485
3486    // disjoint: outside orthant -X
3487    let a = object::Static {
3488      position: component::Position ([-2.0, 0.0, 0.0].into()),
3489      bound:    component::Bound (shape::Bounded::from (
3490        shape::Cuboid::noisy ([1.0, 1.0, 1.0])).into()),
3491      .. a
3492    };
3493    let b = object::Static {
3494      position: component::Position ([0.0, 0.0, 0.0].into()),
3495      bound:    component::Bound (shape::Unbounded::from (
3496        shape::Orthant { normal_axis: SignedAxis3::NegX }).into()),
3497      .. b
3498    };
3499    assert_eq!(
3500      Proximity::query (&a, &b),
3501      Proximity {
3502        distance:  1.0,
3503        half_axis: [ 0.5, 0.0, 0.0].into(),
3504        normal:    math::Unit3::axis_x().invert(),
3505        midpoint:  [-0.5, 0.0, 0.0].into()
3506      }
3507    );
3508
3509    // intersect: cuboid and orthant position coincide; normal +Z
3510    let a = object::Static {
3511      position: component::Position ([0.0, 0.0, 0.0].into()),
3512      bound:    component::Bound (shape::Bounded::from (
3513        shape::Cuboid::noisy ([1.0, 1.0, 1.0])).into()),
3514      .. a
3515    };
3516    let b = object::Static {
3517      position: component::Position ([0.0, 0.0, 0.0].into()),
3518      bound:    component::Bound (shape::Unbounded::from (
3519        shape::Orthant { normal_axis: SignedAxis3::PosZ }).into()),
3520      .. b
3521    };
3522    assert_eq!(
3523      Proximity::query (&a, &b),
3524      Proximity {
3525        distance:  -1.0,
3526        half_axis: [0.0, 0.0,  0.5].into(),
3527        normal:    math::Unit3::axis_z(),
3528        midpoint:  [0.0, 0.0, -0.5].into()
3529      }
3530    );
3531
3532    // intersect: cuboid and orthant position coincide; normal -Z
3533    let a = object::Static {
3534      position: component::Position ([0.0, 0.0, 0.0].into()),
3535      bound:    component::Bound (shape::Bounded::from (
3536        shape::Cuboid::noisy ([1.0, 1.0, 1.0])).into()),
3537      .. a
3538    };
3539    let b = object::Static {
3540      position: component::Position ([0.0, 0.0, 0.0].into()),
3541      bound:    component::Bound (shape::Unbounded::from (
3542        shape::Orthant { normal_axis: SignedAxis3::NegZ }).into()),
3543      .. b
3544    };
3545    assert_eq!(
3546      Proximity::query (&a, &b),
3547      Proximity {
3548        distance:  -1.0,
3549        half_axis: [0.0, 0.0, -0.5].into(),
3550        normal:    math::Unit3::axis_z().invert(),
3551        midpoint:  [0.0, 0.0,  0.5].into()
3552      }
3553    );
3554
3555    // intersect: cuboid and orthant position coincide; normal +Y
3556    let a = object::Static {
3557      position: component::Position ([0.0, 0.0, 0.0].into()),
3558      bound:    component::Bound (shape::Bounded::from (
3559        shape::Cuboid::noisy ([1.0, 1.0, 1.0])).into()),
3560      .. a
3561    };
3562    let b = object::Static {
3563      position: component::Position ([0.0, 0.0, 0.0].into()),
3564      bound:    component::Bound (shape::Unbounded::from (
3565        shape::Orthant { normal_axis: SignedAxis3::PosY }).into()),
3566      .. b
3567    };
3568    assert_eq!(
3569      Proximity::query (&a, &b),
3570      Proximity {
3571        distance:  -1.0,
3572        half_axis: [0.0,  0.5, 0.0].into(),
3573        normal:    math::Unit3::axis_y(),
3574        midpoint:  [0.0, -0.5, 0.0].into()
3575      }
3576    );
3577
3578    // intersect: cuboid and orthant position coincide; normal -Y
3579    let a = object::Static {
3580      position: component::Position ([0.0, 0.0, 0.0].into()),
3581      bound:    component::Bound (shape::Bounded::from (
3582        shape::Cuboid::noisy ([1.0, 1.0, 1.0])).into()),
3583      .. a
3584    };
3585    let b = object::Static {
3586      position: component::Position ([0.0, 0.0, 0.0].into()),
3587      bound:    component::Bound (shape::Unbounded::from (
3588        shape::Orthant { normal_axis: SignedAxis3::NegY }).into()),
3589      .. b
3590    };
3591    assert_eq!(
3592      Proximity::query (&a, &b),
3593      Proximity {
3594        distance:  -1.0,
3595        half_axis: [0.0, -0.5, 0.0].into(),
3596        normal:    math::Unit3::axis_y().invert(),
3597        midpoint:  [0.0,  0.5, 0.0].into()
3598      }
3599    );
3600
3601    // intersect: cuboid and orthant position coincide; normal +X
3602    let a = object::Static {
3603      position: component::Position ([0.0, 0.0, 0.0].into()),
3604      bound:    component::Bound (shape::Bounded::from (
3605        shape::Cuboid::noisy ([1.0, 1.0, 1.0])).into()),
3606      .. a
3607    };
3608    let b = object::Static {
3609      position: component::Position ([0.0, 0.0, 0.0].into()),
3610      bound:    component::Bound (shape::Unbounded::from (
3611        shape::Orthant { normal_axis: SignedAxis3::PosX }).into()),
3612      .. b
3613    };
3614    assert_eq!(
3615      Proximity::query (&a, &b),
3616      Proximity {
3617        distance:  -1.0,
3618        half_axis: [ 0.5, 0.0, 0.0].into(),
3619        normal:    math::Unit3::axis_x(),
3620        midpoint:  [-0.5, 0.0, 0.0].into()
3621      }
3622    );
3623
3624    // intersect: cuboid and orthant position coincide; normal -X
3625    let a = object::Static {
3626      position: component::Position ([0.0, 0.0, 0.0].into()),
3627      bound:    component::Bound (shape::Bounded::from (
3628        shape::Cuboid::noisy ([1.0, 1.0, 1.0])).into()),
3629      .. a
3630    };
3631    let b = object::Static {
3632      position: component::Position ([0.0, 0.0, 0.0].into()),
3633      bound:    component::Bound (shape::Unbounded::from (
3634        shape::Orthant { normal_axis: SignedAxis3::NegX }).into()),
3635      .. b
3636    };
3637    assert_eq!(
3638      Proximity::query (&a, &b),
3639      Proximity {
3640        distance:  -1.0,
3641        half_axis: [-0.5, 0.0, 0.0].into(),
3642        normal:    math::Unit3::axis_x().invert(),
3643        midpoint:  [ 0.5, 0.0, 0.0].into()
3644      }
3645    );
3646
3647  } // end test_distance_query
3648
3649  #[bench]
3650  /// 180ns
3651  fn bench_distance_query_cuboid_edges_normalize (b : &mut test::Bencher) {
3652    use rand::SeedableRng;
3653    let mut rng = XorShiftRng::seed_from_u64 (0);
3654    b.iter (||{
3655      let (position_a, cuboid_a, position_b, cuboid_b) =
3656        distance_query_cuboid_edge_case (&mut rng);
3657      let proximity = Proximity::query_cuboid_cuboid (
3658        &position_a, &cuboid_a, &position_b, &cuboid_b);
3659      assert!(proximity.distance > 0.0);
3660    });
3661  }
3662
3663  /*
3664  #[bench]
3665  /// 180ns
3666  fn bench_distance_query_cuboid_edges_array (b : &mut test::Bencher) {
3667    let mut rng = rs_utils::numeric::xorshift_rng_unseeded();
3668    b.iter (||{
3669      let (position_a, cuboid_a, position_b, cuboid_b) =
3670        distance_query_cuboid_edge_case (&mut rng);
3671      let proximity = Proximity::query_cuboid_cuboid_array (
3672        &position_a, &cuboid_a, &position_b, &cuboid_b);
3673      assert!(proximity.distance > 0.0);
3674    });
3675  }
3676
3677  /// 190ns
3678  #[bench]
3679  fn bench_distance_query_cuboid_edges_refactor (b : &mut test::Bencher) {
3680    let mut rng = rs_utils::numeric::xorshift_rng_unseeded();
3681    b.iter (||{
3682      let (position_a, cuboid_a, position_b, cuboid_b) =
3683        distance_query_cuboid_edge_case (&mut rng);
3684      let proximity = Proximity::query_cuboid_cuboid_refactor (
3685        &position_a, &cuboid_a, &position_b, &cuboid_b);
3686      assert!(proximity.distance > 0.0);
3687    });
3688  }
3689  */
3690
3691  /// Generate an edge case for cuboid/cuboid distance benchmarks
3692  fn distance_query_cuboid_edge_case <RNG : rand::Rng> (rng : &mut RNG) -> (
3693    math::Point3 <f64>, shape::Cuboid <f64>,
3694    math::Point3 <f64>, shape::Cuboid <f64>
3695  ) {
3696    // overlapping range
3697    let base_min_a : f64 = rng.random_range (-10.0..10.0);
3698    let base_max_a : f64 = rng.random_range (base_min_a + 0.01..10.01);
3699    let base_min_b : f64 = rng.random_range (-10.01..base_max_a);
3700    let base_max_b : f64 = if base_min_b < base_min_a {
3701      rng.random_range (base_min_a + 0.01..10.02)
3702    } else {
3703      rng.random_range (base_min_b + 0.01..10.02)
3704    };
3705    debug_assert!(base_min_a < base_max_b);
3706    debug_assert!(base_min_b < base_max_a);
3707    let base_pos_a = 0.5 * (base_min_a + base_max_a);
3708    let base_half_extent_a = base_max_a - base_pos_a;
3709    let base_pos_b = 0.5 * (base_min_b + base_max_b);
3710    let base_half_extent_b = base_max_b - base_pos_b;
3711    debug_assert!(base_half_extent_a > 0.0);
3712    debug_assert!(base_half_extent_b > 0.0);
3713    // non-overlapping ranges
3714    let other1_min_a = rng.random_range (-10.0..10.0);
3715    let other1_max_a = rng.random_range (other1_min_a + 0.01..10.01);
3716    let (other1_min_b, other1_max_b) = if rng.random_bool (0.5) {
3717      // greater than
3718      let other1_min_b = rng.random_range (other1_max_a + 0.01..10.02);
3719      let other1_max_b = rng.random_range (other1_min_b + 0.01..10.03);
3720      debug_assert!(other1_max_a < other1_min_b);
3721      (other1_min_b, other1_max_b)
3722    } else {
3723      // less than
3724      let other1_min_b = rng.random_range (-10.03..other1_min_a - 0.03);
3725      let other1_max_b = rng.random_range (other1_min_b + 0.01..other1_min_a - 0.01);
3726      debug_assert!(other1_max_b < other1_min_a);
3727      (other1_min_b, other1_max_b)
3728    };
3729    let other1_pos_a = 0.5 * (other1_min_a + other1_max_a);
3730    let other1_half_extent_a = other1_max_a - other1_pos_a;
3731    let other1_pos_b = 0.5 * (other1_min_b + other1_max_b);
3732    let other1_half_extent_b = other1_max_b - other1_pos_b;
3733    debug_assert!(other1_half_extent_a > 0.0);
3734    debug_assert!(other1_half_extent_b > 0.0);
3735
3736    let other2_min_a = rng.random_range (-10.0..10.0);
3737    let other2_max_a = rng.random_range (other2_min_a + 0.01..10.01);
3738    let (other2_min_b, other2_max_b) = if rng.random_bool (0.5) {
3739      // greater than
3740      let other2_min_b = rng.random_range (other2_max_a + 0.01..10.02);
3741      let other2_max_b = rng.random_range (other2_min_b + 0.01..10.03);
3742      debug_assert!(other2_max_a < other2_min_b);
3743      (other2_min_b, other2_max_b)
3744    } else {
3745      // less than
3746      let other2_min_b = rng.random_range (-10.03..other2_min_a - 0.03);
3747      let other2_max_b = rng.random_range (other2_min_b + 0.01..other2_min_a - 0.01);
3748      debug_assert!(other2_max_b < other2_min_a);
3749      (other2_min_b, other2_max_b)
3750    };
3751    let other2_pos_a = 0.5 * (other2_min_a + other2_max_a);
3752    let other2_half_extent_a = other2_max_a - other2_pos_a;
3753    let other2_pos_b = 0.5 * (other2_min_b + other2_max_b);
3754    let other2_half_extent_b = other2_max_b - other2_pos_b;
3755    debug_assert!(other2_half_extent_a > 0.0);
3756    debug_assert!(other2_half_extent_b > 0.0);
3757
3758    let axis : usize = rng.random_range (0..3);
3759
3760    if axis == 0 {
3761      // x
3762      (
3763        [base_pos_a, other1_pos_a, other2_pos_a].into(),
3764        shape::Cuboid::noisy ([
3765          base_half_extent_a, other1_half_extent_a, other2_half_extent_a]),
3766        [base_pos_b, other1_pos_b, other2_pos_b].into(),
3767        shape::Cuboid::noisy ([
3768          base_half_extent_b, other1_half_extent_b, other2_half_extent_b])
3769      )
3770    } else if axis == 1 {
3771      // y
3772      (
3773        [other1_pos_a, base_pos_a, other2_pos_a].into(),
3774        shape::Cuboid::noisy ([
3775          other1_half_extent_a, base_half_extent_a, other2_half_extent_a]),
3776        [other1_pos_b, base_pos_b, other2_pos_b].into(),
3777        shape::Cuboid::noisy ([
3778          other1_half_extent_b, base_half_extent_b, other2_half_extent_b])
3779      )
3780    } else {
3781      // z
3782      debug_assert_eq!(axis, 2);
3783      (
3784        [other1_pos_a, other2_pos_a, base_pos_a].into(),
3785        shape::Cuboid::noisy ([
3786          other1_half_extent_a, other2_half_extent_a, base_half_extent_a]),
3787        [other1_pos_b, other2_pos_b, base_pos_b].into(),
3788        shape::Cuboid::noisy ([
3789          other1_half_extent_b, other2_half_extent_b, base_half_extent_b])
3790      )
3791    }
3792  }
3793
3794} // end tests