linear_sim/collision/
mod.rs

1//! Collision detection and resolution
2
3use std;
4#[cfg(feature = "debug_dump")]
5use std::sync::atomic;
6use std::cmp::Reverse;
7use either::Either;
8use derive_more::Into;
9use log;
10use sorted_vec::{SortedSet, ReverseSortedSet};
11use stash::Stash;
12use vec_map::VecMap;
13#[cfg(feature = "derive_serdes")]
14use serde::{Deserialize, Serialize};
15
16use crate::{component, event, geometry, math, object};
17use math::*;
18use geometry::Aabb3;
19
20pub mod contact;
21pub mod proximity;
22pub mod intersection;
23mod broad;
24
25pub use self::contact::Contact;
26pub use self::proximity::Proximity;
27pub use self::intersection::Intersection;
28use self::broad::Broad;
29
30////////////////////////////////////////////////////////////////////////////////
31//  constants                                                                 //
32////////////////////////////////////////////////////////////////////////////////
33
34/// Distance criterea for contacts and collisions (TOI contacts).
35///
36/// Continuous collision detection will aim to return the TOI for when the
37/// objects are at a distance approximately `0.5 * CONTACT_DISTANCE` in order to
38/// prevent intersection due to numerical error.
39///
40/// Post-impulse position correction will also move objects apart to a distance
41/// of `0.5 * CONTACT_DISTANCE`.
42pub const CONTACT_DISTANCE : f64 = 0.005;   // 5mm
43/// Velocity for determination of whether a normal impulse should be applied
44pub const RESTING_VELOCITY : f64 = (1.0/120.0) * CONTACT_DISTANCE;
45/// The value of the dynamic bit also gives the maximum key value allowed for
46/// objects in the collision system.
47pub const OBJECT_KEY_MAX   : object::KeyType = INTERNAL_ID_DYNAMIC_BIT - 1;
48
49/// Used by 'InternalId' to indicate a dynamic object identifier, otherwise if
50/// this bit is zero the object identifier is for a static object
51pub (crate) const INTERNAL_ID_DYNAMIC_BIT : object::KeyType =
52  1 << (object::KeyType::BITS - 1);   // most significant bit
53
54#[cfg(feature = "debug_dump")]
55pub static DEBUG_DUMP : atomic::AtomicBool = atomic::AtomicBool::new (false);
56#[cfg(feature = "debug_dump")]
57pub const DEBUG_DUMP_DETECT_RESOLVE_MAX_ITERS : u64 = 200;
58
59////////////////////////////////////////////////////////////////////////////////
60//  structs                                                                   //
61////////////////////////////////////////////////////////////////////////////////
62
63/// Collision subsystem
64#[cfg_attr(feature = "derive_serdes", derive(Deserialize, Serialize))]
65#[derive(Clone, Debug, Default)]
66pub struct Collision {
67  /// Broad-phase detection subsystem
68  broad             : Broad,
69  /// Pseudo-velocities.
70  ///
71  /// Post-impulse position corrections utilize this velocity which is reset to
72  /// zero at the beginning of each collision step.
73  pseudo_velocities : VecMap <Vector3 <f64>>,
74  /// Collision pipeline data: broad, mid, and narrow phase results.
75  ///
76  /// The pipeline should always be empty at the beginning and end of each
77  /// collision step and is skipped when serializing the collision state
78  /// resulting in a default initialized (empty) pipeline when deserialized.
79  #[cfg_attr(feature = "derive_serdes", serde(skip))]
80  pipeline          : Pipeline,
81  /// Persistent contact groups
82  persistent        : contact::Manager,
83  /// Count maximum iterations of collision detect/resolve main loop
84  #[cfg(debug_assertions)]
85  detect_resolve_max_iter_count : u64,
86  /// Count maximum iterations of narrow phase loop
87  #[cfg(debug_assertions)]
88  narrow_toi_max_iter_count     : u64
89}
90
91/// Collision pipeline data.
92///
93/// A collision detection step should begin and end with all vectors empty.
94#[derive(Clone, Debug, Default, PartialEq)]
95struct Pipeline {
96  /// Counts iterations of the `detect_resolve` loop; reset to zero at the
97  /// beginning of a collision step
98  pub detect_resolve_iter : u64,
99  /// Broad phase AABB overlaps on all three axes
100  pub broad_overlap_pairs : Vec <ObjectPair>,
101  /// Sorted in *reverse order* by start time
102  pub mid_toi_pairs       :
103    ReverseSortedSet <(Normalized <f64>, Normalized <f64>, ObjectPair)>,
104  /// Sorted in *reverse order* by TOI
105  pub narrow_toi_contacts :
106    ReverseSortedSet <(Normalized <f64>, ObjectPair, contact::Colliding)>,
107  pub resolved_collisions : Vec <event::CollisionResolve>,
108  /// Nocollide intersections
109  pub overlaps            : Vec <event::Overlap>,
110}
111
112/// (Sorted) static/dynamic or dynamic/dynamic pair
113#[cfg_attr(feature = "derive_serdes", derive(Deserialize, Serialize))]
114#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Into)]
115pub (crate) struct ObjectPair (InternalId, InternalId);
116
117/// Represents a static or dynamic object identifier by using the most
118/// significant bit of the object key type (0 for static, 1 for dynamic).
119#[cfg_attr(feature = "derive_serdes", derive(Deserialize, Serialize))]
120#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
121pub (crate) struct InternalId (object::KeyType);
122
123#[derive(Debug)]
124struct ContactImpulses {
125  pub impulse_normal_a  : Vector3 <f64>,
126  pub impulse_normal_b  : Vector3 <f64>,
127  pub impulse_tangent_a : Vector3 <f64>,
128  pub impulse_tangent_b : Vector3 <f64>
129}
130
131#[derive(Debug)]
132struct ContactPostImpulses {
133  pub pseudo_impulse_a : Vector3 <f64>,
134  pub pseudo_impulse_b : Vector3 <f64>
135}
136
137////////////////////////////////////////////////////////////////////////////////
138//  functions                                                                 //
139////////////////////////////////////////////////////////////////////////////////
140
141pub fn report_sizes() {
142  use std::mem::size_of;
143  println!("collision report sizes...");
144
145  println!("  size of Collision: {}", size_of::<Collision>());
146
147  println!("...collision report sizes");
148}
149
150/// Linear-interpolate an object with the given pseudovelocity
151fn lerp_object <O : object::Temporal> (
152  object : &mut O, pseudovelocity : Vector3 <f64>, t_relative : f64
153) {
154  let component::Position (position) = object.position().clone();
155  object.position_mut().0 = position +
156    t_relative * object.derivatives().velocity + t_relative * pseudovelocity;
157}
158
159fn contact_constrain_velocity (
160  contact          : contact::Contact,
161  restitution      : f64,   // 0.0 for a resting contact
162  mut object_a     : Either <&object::Static, &mut object::Dynamic>,
163  object_b         : &mut object::Dynamic,
164  pseudovelocity_a : Option <Vector3 <f64>>,
165  pseudovelocity_b : Vector3 <f64>
166) -> Option <ContactImpulses> {
167  let velocity_effective_a = match &object_a {
168    Either::Left (_) => {
169      debug_assert!(pseudovelocity_a.is_none());
170      Vector3::zero()
171    }
172    Either::Right (object_a) => object_a.derivatives.velocity +
173      pseudovelocity_a.unwrap_or (Vector3::zero())
174  };
175  let velocity_effective_b =
176    object_b.derivatives.velocity + pseudovelocity_b;
177  let velocity_effective_relative =
178    velocity_effective_a - velocity_effective_b;
179  let velocity_normal_component =
180    velocity_effective_relative.dot (*contact.constraint.normal());
181  let mut impulses        = None;
182  let mut mass_relative_a = 0.0;
183  let mut mass_relative_b = 0.0;
184  if velocity_normal_component < 0.0 {
185    let velocity_normal =
186      velocity_normal_component * *contact.constraint.normal();
187    // normal impulse
188    let impulse_normal = velocity_normal + restitution * velocity_normal;
189    let impulse_normal_magnitude2 = impulse_normal.magnitude_squared();
190    if impulse_normal_magnitude2 < RESTING_VELOCITY * RESTING_VELOCITY {
191      return None
192    }
193    let (impulse_normal_a, impulse_normal_b) = match &mut object_a {
194      Either::Right (object_a) => {
195        let mass_combined    = object_a.mass.clone() + object_b.mass.clone();
196        mass_relative_a      =
197          object_a.mass.mass() * mass_combined.mass_reciprocal();
198        mass_relative_b      =
199          object_b.mass.mass() * mass_combined.mass_reciprocal();
200        let impulse_normal_a = -mass_relative_b * impulse_normal;
201        let impulse_normal_b = mass_relative_a * impulse_normal;
202        object_a.derivatives.velocity += impulse_normal_a;
203        object_b.derivatives.velocity += impulse_normal_b;
204        (impulse_normal_a, impulse_normal_b)
205      }
206      Either::Left (_) => {
207        object_b.derivatives.velocity += impulse_normal;
208        (Vector3::zero(), impulse_normal)
209      }
210    };
211    // friction (tangent) impulse
212    let (impulse_tangent_a, impulse_tangent_b) = {
213      let velocity_tangent = velocity_effective_relative - velocity_normal;
214      let velocity_tangent_magnitude2 = velocity_tangent.magnitude_squared();
215      if velocity_tangent_magnitude2 > 0.0 {
216        let friction_a = match &object_a {
217          Either::Left  (x) => x.material.friction,
218          Either::Right (x) => x.material.friction
219        };
220        let friction_coefficient = friction_a * object_b.material.friction;
221        let impulse_tangent_potential_magnitude2 =
222          friction_coefficient * impulse_normal_magnitude2;
223        let impulse_tangent = if impulse_tangent_potential_magnitude2 <
224          velocity_tangent_magnitude2
225        {
226          (impulse_tangent_potential_magnitude2.sqrt()
227          / velocity_tangent_magnitude2.sqrt()) * velocity_tangent
228        } else {
229          velocity_tangent
230        };
231        match object_a {
232          Either::Right (object_a) => {
233            let impulse_tangent_a = -mass_relative_b * impulse_tangent;
234            let impulse_tangent_b = mass_relative_a * impulse_tangent;
235            object_a.derivatives.velocity += impulse_tangent_a;
236            object_b.derivatives.velocity += impulse_tangent_b;
237            (impulse_tangent_a, impulse_tangent_b)
238          }
239          Either::Left (_) => {
240            object_b.derivatives.velocity += impulse_tangent;
241            (Vector3::zero(), impulse_tangent)
242          }
243        }
244      } else {
245        (Vector3::zero(), Vector3::zero())
246      }
247    };
248    impulses = Some (ContactImpulses {
249      impulse_normal_a, impulse_normal_b,
250      impulse_tangent_a, impulse_tangent_b
251    });
252  }
253  impulses
254}
255
256fn contact_constrain_position (
257  t_reverse        : f64,
258  mut object_a     : Either <&object::Static, &mut object::Dynamic>,
259  object_b         : &mut object::Dynamic,
260  pseudovelocity_a : Option <&mut Vector3 <f64>>,
261  pseudovelocity_b : &mut Vector3 <f64>
262) -> (Option <ContactPostImpulses>, Proximity) {
263  let t_forward = -t_reverse;
264  let mut impulses  = None;
265  let mut proximity = match &object_a {
266    Either::Left  (object_a) => Proximity::query (*object_a, object_b),
267    Either::Right (object_a) => Proximity::query (*object_a, object_b)
268  };
269  log::trace!(distance=proximity.distance; "post impulse contact @ T==1.0");
270  if proximity.distance < 0.0 {
271    let (pseudo_impulse_a, pseudo_impulse_b) = match &mut object_a {
272      Either::Left  (object_a) => {
273        lerp_object (object_b, *pseudovelocity_b, t_reverse);
274        let pseudo_impulse_b = -(2.0 * proximity.half_axis +
275          0.5 * CONTACT_DISTANCE * *proximity.normal) / t_forward;
276        *pseudovelocity_b += pseudo_impulse_b;
277        lerp_object (object_b, *pseudovelocity_b, t_forward);
278        proximity = Proximity::query (*object_a, object_b);
279        (Vector3::zero(), pseudo_impulse_b)
280      }
281      Either::Right (object_a) => {
282        let pseudovelocity_a = pseudovelocity_a.unwrap();
283        lerp_object (*object_a, *pseudovelocity_a, t_reverse);
284        lerp_object (object_b, *pseudovelocity_b, t_reverse);
285        let mass_combined    = object_a.mass.clone() + object_b.mass.clone();
286        let mass_relative_a  =
287          object_a.mass.mass() * mass_combined.mass_reciprocal();
288        let mass_relative_b  =
289          object_b.mass.mass() * mass_combined.mass_reciprocal();
290        let pseudo_impulse   = (2.0 * proximity.half_axis +
291          0.5 * CONTACT_DISTANCE * *proximity.normal) / t_forward;
292        let pseudo_impulse_a = mass_relative_b * pseudo_impulse;
293        let pseudo_impulse_b = -mass_relative_a * pseudo_impulse;
294        *pseudovelocity_a += pseudo_impulse_a;
295        *pseudovelocity_b += pseudo_impulse_b;
296        lerp_object (*object_a, *pseudovelocity_a, t_forward);
297        lerp_object (object_b, *pseudovelocity_b, t_forward);
298        proximity = Proximity::query (*object_a, object_b);
299        (pseudo_impulse_a, pseudo_impulse_b)
300      }
301    };
302    log::trace!(distance=proximity.distance;
303      "position corrected distance @ T==1.0");
304    debug_assert!(proximity.distance >= 0.0);
305    if cfg!(debug_assertions) {
306      math::approx::assert_abs_diff_eq!(
307        proximity.distance, 0.5 * CONTACT_DISTANCE, epsilon=0.000000001);
308    }
309    impulses = Some (ContactPostImpulses { pseudo_impulse_a, pseudo_impulse_b });
310  }
311  (impulses, proximity)
312}
313
314////////////////////////////////////////////////////////////////////////////////
315//  impls                                                                     //
316////////////////////////////////////////////////////////////////////////////////
317
318impl Collision {
319  pub (crate) fn broad (&self) -> &Broad {
320    &self.broad
321  }
322  /// Attempt to add a new static object to the collision subsystem.
323  ///
324  /// Queries distance to each dynamic object and if any intersections are
325  /// found, the object is not added and the intersections are returned.
326  ///
327  /// Only counts intersections if both objects are collidable.
328  pub fn try_add_object_static (&mut self,
329    objects_dynamic : &VecMap <object::Dynamic>,
330    object          : &object::Static,
331    key             : object::Key
332  ) -> Result <(), Vec <(object::Id, Intersection)>> {
333    let mut intersections = Vec::new();
334    if object.collidable {
335      let aabb_discrete = object.aabb_dilated();
336      let overlaps : Vec <object::Id> = self.broad
337        .overlaps_discrete (aabb_discrete).into_iter().map (|id| id.into())
338        .collect::<Vec <_>>();
339      for object_id in overlaps {
340        let proximity = match object_id.kind {
341          object::Kind::Static  => continue,  // skip static/static checks
342          object::Kind::Dynamic => {
343            let object_dynamic = &objects_dynamic[object_id.key.index()];
344            if !object_dynamic.collidable {
345              continue                    // skip non-collidable dynamic objects
346            }
347            Proximity::query (object_dynamic, object)
348          }
349          _ => unreachable!()
350        };
351        if let Ok (intersection) = proximity.try_into() {
352          intersections.push ((object_id, intersection));
353        }
354      }
355    }
356    if intersections.is_empty() {
357      self.add_object_static (object, key);
358      Ok  (())
359    } else {
360      Err (intersections)
361    }
362  }
363
364  #[inline]
365  pub fn remove_object_static (&mut self, object_key : object::Key) {
366    debug_assert!(self.pipeline.is_empty());
367    let id = InternalId::new_static (object_key);
368    self.broad.remove_object (id);
369    let _ = self.persistent.remove_object (id);
370  }
371
372  /// Attempts to add a new dynamic object.
373  ///
374  /// Queries distance to each static and dynamic object and if any
375  /// intersections are found, the object is not added and the intersections are
376  /// returned.
377  ///
378  /// Only counts intersections if both objects are collidable.
379  pub fn try_add_object_dynamic (&mut self,
380    objects_static  : &VecMap <object::Static>,
381    objects_dynamic : &VecMap <object::Dynamic>,
382    object          : &object::Dynamic,
383    key             : object::Key
384  ) -> Result <(), Vec <(object::Id, Intersection)>> {
385    let mut intersections = Vec::new();
386    if object.collidable {
387      let aabb_discrete     = object.aabb_dilated();
388      let overlaps : Vec <object::Id> = self.broad
389        .overlaps_discrete (aabb_discrete).into_iter().map (|id| id.into())
390        .collect::<Vec <_>>();
391      for object_id in overlaps {
392        let proximity = match object_id.kind {
393          object::Kind::Static  => {
394            let object_static = &objects_static[object_id.key.index()];
395            if !object_static.collidable {
396              continue  // skip non-collidable static objects
397            }
398            Proximity::query (object_static, object)
399          }
400          object::Kind::Dynamic => {
401            let object_dynamic = &objects_dynamic[object_id.key.index()];
402            if !object_dynamic.collidable {
403              continue  // skip non-collidable static objects
404            }
405            Proximity::query (object_dynamic, object)
406          }
407          _ => unreachable!()
408        };
409        if let Ok (intersection) = proximity.try_into() {
410          intersections.push ((object_id, intersection));
411        }
412      }
413    }
414    if intersections.is_empty() {
415      self.add_object_dynamic (object, key);
416      Ok  (())
417    } else {
418      Err (intersections)
419    }
420  }
421
422  #[inline]
423  pub fn remove_object_dynamic (&mut self, key : object::Key) {
424    debug_assert!(self.pipeline.is_empty());
425    let id = InternalId::new_dynamic (key);
426    self.broad.remove_object (id);
427    self.pseudo_velocities.remove (key.index()).unwrap();
428    let _ = self.persistent.remove_object (id);
429  }
430
431  #[inline]
432  pub fn update_object_static (&mut self,
433    object : &object::Static, key : object::Key
434  ) {
435    debug_assert!(self.pipeline.is_empty());
436    self.broad.update_aabb_static (object.aabb_dilated(), key);
437    unimplemented!("TODO: collision detect and resolve any overlaps caused by \
438      updating the static object")
439  }
440
441  #[inline]
442  pub fn update_object_dynamic (&mut self,
443    object : &object::Dynamic, key : object::Key
444  ) {
445    debug_assert!(self.pipeline.is_empty());
446    let aabb = object.aabb_dilated();
447    self.broad.update_aabb_dynamic_discrete (aabb, key);
448    // TODO: why is the continuous AABB the same as discrete here?
449    self.broad.update_aabb_dynamic_continuous (aabb, key);
450    log::warn!("TODO: collision detect and resolve any overlaps caused by \
451      updating the dynamic object")
452  }
453
454  //
455  //  Collision::detect_resolve_loop
456  //
457  /// Main collision loop.
458  ///
459  /// Before the loop starts, `begin_step()`:
460  ///
461  /// 1. Update broad-phase AABB data
462  ///
463  /// Inside the collision loop:
464  ///
465  /// 1. Perform continuous detection
466  /// 2. Resolve earliest detected collision
467  ///
468  /// After collision loop:
469  ///
470  /// 1. Drain pipeline outputs (collisions, overlaps, contacts)
471  /// 2. Zero pseudovelocities
472  pub fn detect_resolve_loop (&mut self,
473    objects_static  : &mut VecMap <object::Static>,
474    objects_dynamic : &mut VecMap <object::Dynamic>,
475    step            : u64,
476    output          : &mut Vec <event::Output>
477  ) {
478    log::trace!(step; "detect/resolve loop");
479
480    // initialize collision for the current step
481    debug_assert!(self.pipeline.is_empty());
482    self.pipeline.detect_resolve_iter = 0;
483
484    // broad: update AABBs based on the given dynamic objects state
485    //
486    // the continuous (swept) AABBs are calculated from min/max of the previous
487    // 'discrete' (instantaneous) AABB and the new "current" discrete AABB
488    self.broad.begin_step (&objects_dynamic, step);
489
490    // main detect/resolve loop: resolve one collision per iteration
491    loop {
492      log::debug!(iter=self.pipeline.detect_resolve_iter;
493        "detect/resolve loop iteration");
494      // detect collisions (TOI contacts)
495      self.detect_continuous (objects_static, objects_dynamic);
496      // resolve earliest collision: resolve a velocity constraint at TOI and a
497      // position constraint at t==1.0
498      if !self.resolve_collision (objects_static, objects_dynamic) {
499        debug_assert!(self.pipeline.mid_toi_pairs.is_empty());
500        debug_assert!(self.pipeline.narrow_toi_contacts.is_empty());
501        break
502      }
503      self.pipeline.detect_resolve_iter += 1;
504    }
505
506    log::trace!(iters=self.pipeline.detect_resolve_iter+1;
507      "detect/resolve loop complete");
508    #[cfg(debug_assertions)]
509    {
510      if self.detect_resolve_max_iter_count
511        < (self.pipeline.detect_resolve_iter + 1)
512      {
513        #[cfg(feature = "debug_dump")]
514        if self.pipeline.detect_resolve_iter >
515          DEBUG_DUMP_DETECT_RESOLVE_MAX_ITERS
516        {
517          DEBUG_DUMP.store (true, atomic::Ordering::SeqCst);
518        }
519        self.detect_resolve_max_iter_count = self.pipeline.detect_resolve_iter;
520      }
521      log::debug!(max_iter_count=self.detect_resolve_max_iter_count;
522        "detect/resolve loop max iters");
523    }
524
525    // output collision events
526    for collision_resolve in self.pipeline.resolved_collisions.drain (..) {
527      output.push (collision_resolve.into());
528    }
529    for overlap in self.pipeline.overlaps.drain (..) {
530      output.push (overlap.into());
531    }
532
533    // output contacts
534    self.persistent.output_contacts (output);
535
536    // zero pseudovelocities
537    for (_, pseudovelocity) in self.pseudo_velocities.iter_mut() {
538      *pseudovelocity = Vector3::zero();
539    }
540
541    // done
542    debug_assert!(self.pipeline.is_empty());
543  }
544
545  /// Solve positions constraints for persistent contact groups
546  pub fn constrain_contact_positions (&mut self,
547    objects_static  : &VecMap <object::Static>,
548    objects_dynamic : &mut VecMap <object::Dynamic>
549  ) {
550    let mut remove_groups  = vec![];
551    let mut new_groups     = vec![];
552    let mut contact_groups = self.persistent.contact_groups.take().unwrap();
553    for (group_index, group) in contact_groups.iter_mut() {
554      log::debug!(group_index, contacts_count=group.contacts.len();
555        "group constrain positions");
556      log::trace!(group_index, contacts:?=group.contacts;
557        "group constrain positions contacts");
558      let mut iter = 0;
559      let mut remove_contacts = vec![];
560      loop {
561        remove_contacts.clear();
562        let mut satisfied = true;
563        for (contact_index, (object_pair, contact)) in
564          group.contacts.iter_mut().enumerate()
565        {
566          let (object_id_a, object_id_b) = (*object_pair).into();
567          let index_a = object_id_a.key().index();
568          let index_b = object_id_b.key().index();
569          let mut object_a = match object_id_a.kind() {
570            object::Kind::Static  =>
571              Either::Left  (objects_static[index_a].clone()),
572            object::Kind::Dynamic =>
573              Either::Right (objects_dynamic[index_a].clone()),
574            _ => unreachable!()
575          };
576          debug_assert_eq!(object_id_b.kind(), object::Kind::Dynamic);
577          let mut object_b = objects_dynamic[index_b].clone();
578          let mut pseudovelocity_a = match object_a {
579            Either::Left  (_) => None,
580            Either::Right (_) => Some (self.pseudo_velocities[index_a].clone())
581          };
582          let mut pseudovelocity_b = self.pseudo_velocities[index_b].clone();
583          let (maybe_impulses, proximity) = contact_constrain_position (
584            -1.0,
585            object_a.as_mut().map_left (|object_static| &*object_static),
586            &mut object_b, pseudovelocity_a.as_mut(), &mut pseudovelocity_b
587          );
588          if maybe_impulses.is_some() {
589            satisfied = false;
590            match object_a {
591              Either::Right (object_a) => {
592                objects_dynamic[index_a]        = object_a;
593                self.pseudo_velocities[index_a] = pseudovelocity_a.unwrap();
594              }
595              Either::Left  (_) => {}
596            }
597            objects_dynamic[index_b]        = object_b;
598            self.pseudo_velocities[index_b] = pseudovelocity_b;
599          }
600          // NOTE: checking the contact distance here instead of the try_into
601          // result so we don't have to clone to use the proximity object in the
602          // debug log statement
603          if proximity.distance >= CONTACT_DISTANCE {
604            log::debug!(object_pair:?, contact:?, proximity:?;
605              "removing contact @ T==1.0");
606            remove_contacts.push (contact_index as u32);
607          } else {
608            // update contact
609            *contact = proximity.try_into().unwrap();
610          }
611        }
612        iter += 1;
613        if satisfied {
614          // group satisfied
615          log::debug!(group_index, iters=iter;
616            "group position constraint satisfied");
617          if !remove_contacts.is_empty() {
618            self.persistent.remove_contacts (group, remove_contacts.as_slice());
619            if group.contacts.is_empty() {
620              remove_groups.push (group_index as contact::group::KeyType);
621            } else {
622              let partitions = group.partition();
623              if !partitions.is_empty() {
624                remove_groups.push (group_index as contact::group::KeyType);
625                new_groups.extend (partitions);
626              }
627            }
628          }
629          break
630        }
631      }
632    }
633    for group_key in remove_groups {
634      let _ = contact_groups.take (group_key as usize).unwrap();
635    }
636    for new_group in new_groups {
637      let dynamic_ids = new_group.dynamic_ids();
638      let group_key = contact_groups.put (new_group) as contact::group::KeyType;
639      for dynamic_id in dynamic_ids.into_vec() {
640        self.persistent.change_dynamic_group_key (dynamic_id, group_key);
641      }
642    }
643    self.persistent.contact_groups = Some (contact_groups);
644  }
645
646  /// Solve velocity constraints for persistent contact groups
647  pub fn constrain_contact_velocities (&self,
648    objects_static  : &VecMap <object::Static>,
649    objects_dynamic : &mut VecMap <object::Dynamic>
650  ) {
651    for (group_index, group) in self.contact_groups().iter() {
652      log::debug!(group_index, contacts_count=group.contacts.len();
653        "group constrain velocities");
654      log::trace!(group_index, contacts:?=group.contacts;
655        "group constrain velocities contacts");
656      let mut iter = 0;
657      loop {
658        let mut satisfied = true;
659        for (object_pair, contact) in group.contacts.iter() {
660          let (object_id_a, object_id_b) = (*object_pair).into();
661          let index_a = object_id_a.key().index();
662          let index_b = object_id_b.key().index();
663          let mut object_a = match object_id_a.kind() {
664            object::Kind::Static  =>
665              Either::Left  (objects_static[index_a].clone()),
666            object::Kind::Dynamic =>
667              Either::Right (objects_dynamic[index_a].clone()),
668            _ => unreachable!()
669          };
670          debug_assert_eq!(object_id_b.kind(), object::Kind::Dynamic);
671          let mut object_b = objects_dynamic[index_b].clone();
672          // NOTE: at this stage in the pipeline, pseudovelocities are zero
673          if contact_constrain_velocity (
674            contact.clone(), 0.0,
675            object_a.as_mut().map_left (|object_static| &*object_static),
676            &mut object_b, None, Vector3::zero()
677          ).is_some() {
678            match object_a {
679              Either::Right (object_a) => objects_dynamic[index_a] = object_a,
680              Either::Left  (_) => {}
681            }
682            objects_dynamic[index_b] = object_b;
683            satisfied = false;
684          }
685        }
686        iter += 1;
687        if satisfied {
688          // group satisfied
689          log::debug!(group_index, iters=iter;
690            "group velocity constraint satisfied");
691          break
692        }
693      }
694    }
695  }
696
697  pub (crate) fn contact_groups (&self) -> &Stash <contact::Group> {
698    self.persistent.contact_groups.as_ref().unwrap()
699  }
700
701  //
702  //  private methods
703  //
704
705  /// Detect TOI (time-of-impact) contacts, i.e. *collisions*.
706  fn detect_continuous (&mut self,
707    objects_static  : &VecMap <object::Static>,
708    objects_dynamic : &VecMap <object::Dynamic>
709  ) {
710    // collect broad overlap pairs
711    self.broad_overlap_pairs_continuous();
712    // mid-phase TOI pairs
713    self.mid_toi_pairs (&objects_dynamic);
714    // narrow-phase TOI pairs
715    self.narrow_toi_contacts (&objects_static, &objects_dynamic);
716  }
717
718  /// Resolve the first collision (TOI contact pair) in the pipeline, if any,
719  /// and returns true, otherwise returns false.
720  ///
721  /// If impulses do not prevent intersection at t==1.0, post-impulse position
722  /// correction will move objects to a distance of `0.5 * CONTACT_DISTANCE` by
723  /// applying a pseudo-velocity impulse at the collision TOI.
724  ///
725  /// This function will be called multiple times in the
726  /// `detect_resolve_loop()`.
727  fn resolve_collision (&mut self,
728    objects_static  : &VecMap <object::Static>,
729    objects_dynamic : &mut VecMap <object::Dynamic>
730  ) -> bool {
731    if let Some (Reverse ((toi, object_pair, contact))) =
732      self.pipeline.narrow_toi_contacts.pop()
733    {
734      // process the first narrow toi contact (collision): the container is
735      // reverse-sorted so we use pop
736      let (object_id_a, object_id_b) = object_pair.into();
737      let kind_a  = object_id_a.kind();
738      let kind_b  = object_id_b.kind();
739      let index_a = object_id_a.key().index();
740      let index_b = object_id_b.key().index();
741      // if one or both objects are not collidable, we report overlap and return
742      let collidable = {
743        let collidable_a = match kind_a {
744          object::Kind::Static  => objects_static[index_a].collidable,
745          object::Kind::Dynamic => objects_dynamic[index_a].collidable,
746          _ => unreachable!()
747        };
748        debug_assert_eq!(kind_b, object::Kind::Dynamic);
749        let collidable_b = objects_dynamic[index_b].collidable;
750        collidable_a && collidable_b
751      };
752      if !collidable {
753        let overlap = event::Overlap {
754          object_id_a: object_id_a.into(),
755          object_id_b: object_id_b.into()
756        };
757        log::debug!(toi=*toi, overlap:?; "nocollide overlap");
758        self.pipeline.overlaps.push (overlap);
759        return true
760      }
761      log::debug!(toi=*toi, object_pair:?=(object_id_a, object_id_b);
762        "resolve collision");
763      // get persistant contact groups
764      let maybe_group_a = self.persistent.get_group_key (object_id_a);
765      let maybe_group_b = {
766        let maybe_group_b = self.persistent.get_group_key (object_id_b);
767        if maybe_group_a == maybe_group_b {
768          // if the groups are the same, then just use group a
769          None
770        } else {
771          maybe_group_b
772        }
773      };
774      // resolve collision
775      let collision_resolve = self.resolve (objects_static, objects_dynamic,
776        toi, object_id_a, object_id_b, contact.clone(), maybe_group_a,
777        maybe_group_b);
778      log::trace!(collision_resolve:?; "resolved collision");
779      self.pipeline.resolved_collisions.push (collision_resolve);
780      true
781    } else {
782      // no narrow toi contact to process, return false
783      debug_assert!(self.pipeline.mid_toi_pairs.is_empty());
784      false
785    }
786  }
787
788  fn resolve (
789    &mut self,
790    objects_static  : &VecMap <object::Static>,
791    objects_dynamic : &mut VecMap <object::Dynamic>,
792    toi             : Normalized <f64>,
793    object_id_a     : InternalId,
794    object_id_b     : InternalId,
795    contact         : contact::Colliding,
796    group_key_a     : Option <contact::group::KeyType>,
797    group_key_b     : Option <contact::group::KeyType>
798  ) -> event::CollisionResolve {
799    // 1. lerp all involved dynamic objects to the TOI
800    // 2. constrain velocities
801    // 3. lerp to T==1.0
802    // 4. constrain positions
803    // 5. create or destroy persistent contacts from final proximity check
804    // 6. update AABBs in broad pipeline
805    use math::num_traits::Zero;
806    let mut dynamic_ids = SortedSet::new();
807    if object_id_a.kind() == object::Kind::Dynamic {
808      dynamic_ids.push (object_id_a);
809    };
810    dynamic_ids.push (object_id_b);
811    let group_a = group_key_a
812      .map (|group_key| self.persistent.get_group (group_key).unwrap());
813    let group_b = group_key_b
814      .map (|group_key| self.persistent.get_group (group_key).unwrap());
815    group_a.map (|group| dynamic_ids.extend (group.dynamic_ids().into_vec()));
816    group_b.map (|group| dynamic_ids.extend (group.dynamic_ids().into_vec()));
817    // track whether a dynamic object was modified; if so it will need to be
818    // removed from intermediate results in the pipeline and checked for new
819    // collisions
820    let mut dynamic_modified = SortedSet::new();
821    // 1. lerp to TOI
822    let lerp_objects =
823      |objects_dynamic : &mut VecMap <object::Dynamic>, time_delta|
824      for id in dynamic_ids.iter() {
825        let index          = id.key().index();
826        let object         = &mut objects_dynamic[index];
827        let pseudovelocity = self.pseudo_velocities[index].clone();
828        lerp_object (object, pseudovelocity, time_delta);
829      };
830    let (t_reverse, t_forward) = {
831      let toi_f64 : f64 = *toi;   // [0.0, 1.0]
832      (-1.0 + toi_f64, 1.0 - toi_f64)
833    };
834    lerp_objects (objects_dynamic, t_reverse);
835    let toi_aabbs = dynamic_ids.iter().map (|id|{
836      let index = id.key().index();
837      objects_dynamic[index].aabb_dilated()
838    }).collect::<Vec <_>>();
839    // 2. constrain velocities
840    let constrain_velocity = |
841      objects_dynamic  : &mut VecMap <object::Dynamic>,
842      pseudovelocities : &VecMap <Vector3 <f64>>,
843      contact          : Contact,
844      restitution      : f64,
845      object_id_a      : InternalId,
846      object_id_b      : InternalId
847    | {
848      let key_a    = object_id_a.key();
849      let key_b    = object_id_b.key();
850      let index_a  = key_a.index();
851      let index_b  = key_b.index();
852      let mut object_a = match object_id_a.kind() {
853        object::Kind::Static  => Either::Left  (objects_static[index_a].clone()),
854        object::Kind::Dynamic => Either::Right (objects_dynamic[index_a].clone()),
855        _ => unreachable!()
856      };
857      debug_assert_eq!(object_id_b.kind(), object::Kind::Dynamic);
858      let mut object_b     = objects_dynamic[index_b].clone();
859      let pseudovelocity_a = if object_id_a.kind() == object::Kind::Dynamic {
860        Some (pseudovelocities[index_a].clone())
861      } else {
862        None
863      };
864      let pseudovelocity_b = pseudovelocities[index_b].clone();
865      let maybe_impulses   = contact_constrain_velocity (
866        contact, restitution,
867        object_a.as_mut().map_left (|object_static| &*object_static),
868        &mut object_b, pseudovelocity_a, pseudovelocity_b
869      );
870      if maybe_impulses.is_some() {
871        match object_a {
872          Either::Right (object_a) => objects_dynamic[index_a] = object_a,
873          Either::Left  (_) => {}
874        }
875        objects_dynamic[index_b] = object_b;
876      }
877      maybe_impulses
878    };
879    let mut impulse_normal_a  = Vector3::zero();
880    let mut impulse_normal_b  = Vector3::zero();
881    let mut impulse_tangent_a = Vector3::zero();
882    let mut impulse_tangent_b = Vector3::zero();
883    let mut iter = 0;
884    loop {
885      let mut satisfied = true;
886      if let Some (impulses) = constrain_velocity (
887        objects_dynamic, &self.pseudo_velocities, contact.contact.clone(),
888        contact.restitution, object_id_a, object_id_b
889      ) {
890        // if there are no groups, then the collision is isolated and satisfied
891        satisfied = group_a.is_none() && group_b.is_none();
892        impulse_normal_a  += impulses.impulse_normal_a;
893        impulse_normal_b  += impulses.impulse_normal_b;
894        impulse_tangent_a += impulses.impulse_tangent_a;
895        impulse_tangent_b += impulses.impulse_tangent_b;
896      }
897      let mut constrain_group = |group : &contact::Group|
898        for (object_pair, contact) in group.contacts.iter().cloned() {
899          let (object_id_a, object_id_b) = object_pair.into();
900          if constrain_velocity (
901            objects_dynamic, &self.pseudo_velocities, contact, 0.0,
902            object_id_a, object_id_b
903          ).is_some() {
904            if object_id_a.kind() == object::Kind::Dynamic {
905              dynamic_modified.push (object_id_a);
906            }
907            dynamic_modified.push (object_id_b);
908            satisfied = false;
909          }
910        };
911      if let Some (group) = group_a {
912        constrain_group (group);
913      }
914      if let Some (group) = group_b {
915        constrain_group (group);
916      }
917      iter += 1;
918      if satisfied {
919        log::debug!(iters=iter; "collision velocity constraint satisfied");
920        break
921      }
922    }
923    // 3. lerp to T==1.0
924    lerp_objects (objects_dynamic, t_forward);
925    // 4. constrain positions
926    let constrain_position = |
927      objects_dynamic  : &mut VecMap <object::Dynamic>,
928      pseudovelocities : &mut VecMap <Vector3 <f64>>,
929      object_id_a      : InternalId,
930      object_id_b      : InternalId
931    | {
932      let key_a    = object_id_a.key();
933      let key_b    = object_id_b.key();
934      let index_a  = key_a.index();
935      let index_b  = key_b.index();
936      let mut object_a = match object_id_a.kind() {
937        object::Kind::Static  => Either::Left  (objects_static[index_a].clone()),
938        object::Kind::Dynamic => Either::Right (objects_dynamic[index_a].clone()),
939        _ => unreachable!()
940      };
941      debug_assert_eq!(object_id_b.kind(), object::Kind::Dynamic);
942      let mut object_b = objects_dynamic[index_b].clone();
943      let mut pseudovelocity_a = if object_id_a.kind() == object::Kind::Dynamic {
944        Some (pseudovelocities[index_a].clone())
945      } else {
946        None
947      };
948      let mut pseudovelocity_b = pseudovelocities[index_b].clone();
949      let (maybe_impulses, proximity) = contact_constrain_position (
950        t_reverse,
951        object_a.as_mut().map_left (|object_static| &*object_static),
952        &mut object_b, pseudovelocity_a.as_mut(), &mut pseudovelocity_b
953      );
954      if maybe_impulses.is_some() {
955        match object_a {
956          Either::Right (object_a) => {
957            objects_dynamic[index_a]  = object_a;
958            pseudovelocities[index_a] = pseudovelocity_a.unwrap();
959          }
960          Either::Left  (_) => {}
961        }
962        objects_dynamic[index_b]  = object_b;
963        pseudovelocities[index_b] = pseudovelocity_b;
964      }
965      (maybe_impulses, proximity)
966    };
967    let mut final_proximities = vec![
968      Proximity {
969        distance:  0.0,
970        half_axis: Vector3::zero(),
971        midpoint:  Point::origin(),
972        normal:    Unit3::axis_z()
973      };
974      1 + group_a.map (|group| group.contacts.len()).unwrap_or (0) +
975      group_b.map (|group| group.contacts.len()).unwrap_or (0)
976    ];
977    let mut pseudo_impulse_a = Vector3::zero();
978    let mut pseudo_impulse_b = Vector3::zero();
979    let mut iter = 0;
980    loop {
981      let mut satisfied     = true;
982      let mut contact_index = 0;
983      let (maybe_impulses, proximity) = constrain_position (
984        objects_dynamic, &mut self.pseudo_velocities, object_id_a, object_id_b
985      );
986      final_proximities[contact_index] = proximity;
987      contact_index += 1;
988      if let Some (impulses) = maybe_impulses {
989        // if there are no groups, then the collision is isolated and satisfied
990        satisfied = group_a.is_none() && group_b.is_none();
991        pseudo_impulse_a += impulses.pseudo_impulse_a;
992        pseudo_impulse_b += impulses.pseudo_impulse_b;
993      }
994      let mut constrain_group = |group : &contact::Group|
995        for (object_pair, _) in group.contacts.iter() {
996          let (object_id_a, object_id_b)  = object_pair.clone().into();
997          let (maybe_impulses, proximity) = constrain_position (
998            objects_dynamic, &mut self.pseudo_velocities,
999            object_id_a, object_id_b
1000          );
1001          final_proximities[contact_index] = proximity;
1002          contact_index += 1;
1003          if maybe_impulses.is_some() {
1004            if object_id_a.kind() == object::Kind::Dynamic {
1005              dynamic_modified.push (object_id_a);
1006            }
1007            dynamic_modified.push (object_id_b);
1008            satisfied = false;
1009          }
1010        };
1011      if let Some (group) = group_a {
1012        constrain_group (group);
1013      }
1014      if let Some (group) = group_b {
1015        constrain_group (group);
1016      }
1017      iter += 1;
1018      if satisfied {
1019        log::debug!(iters=iter; "collision position constraint satisfied");
1020        break
1021      }
1022    }
1023    // 5. create or destroy persistent contacts from final proximity check;
1024    // also add resolved pairs to the broad phase here since we are iterating
1025    // over contact groups one last time
1026    let object_pair = (object_id_a, object_id_b).into();
1027    if !impulse_normal_b.is_zero() || !impulse_tangent_b.is_zero() ||
1028      !pseudo_impulse_b.is_zero()
1029    {
1030      if object_id_a.kind() == object::Kind::Dynamic {
1031        debug_assert!(!impulse_normal_a.is_zero() ||
1032          !impulse_tangent_a.is_zero() || !pseudo_impulse_a.is_zero());
1033        dynamic_modified.push (object_id_a);
1034      }
1035      dynamic_modified.push (object_id_b);
1036    }
1037    self.broad.add_resolved (object_pair);
1038    let mut final_proximities     = final_proximities.into_iter();
1039    let collision_final_proximity = final_proximities.next().unwrap();
1040    // wait until after updating groups to create any new contacts in case the
1041    // groups are merged which would invalidate one of the old group keys
1042    let mut update_group = |
1043      final_proximities : &mut std::vec::IntoIter <Proximity>,
1044      group_key         : contact::group::KeyType
1045    | {
1046      let mut contact_groups = self.persistent.contact_groups.take().unwrap();
1047      let group = &mut contact_groups[group_key as usize];
1048      let mut remove_contacts = vec![];
1049      for (i, proximity) in
1050        final_proximities.take (group.contacts.len()).enumerate()
1051      {
1052        let contact = &mut group.contacts[i];
1053        self.broad.add_resolved (contact.0);
1054        if let Ok (update_contact) = Contact::try_from (proximity) {
1055          contact.1 = update_contact;
1056        } else {
1057          remove_contacts.push (i as u32);
1058        }
1059      }
1060      if !remove_contacts.is_empty() {
1061        self.persistent.remove_contacts (group, remove_contacts.as_slice());
1062        if group.contacts.is_empty() {
1063          let _ = contact_groups.take (group_key as usize).unwrap();
1064        } else {
1065          let partitions = group.partition();
1066          if !partitions.is_empty() {
1067            let _ = contact_groups.take (group_key as usize).unwrap();
1068            for new_group in partitions {
1069              let dynamic_ids = new_group.dynamic_ids();
1070              let group_key   = contact_groups.put (new_group)
1071                as contact::group::KeyType;
1072              for dynamic_id in dynamic_ids.into_vec() {
1073                self.persistent.change_dynamic_group_key (dynamic_id, group_key);
1074              }
1075            }
1076          }
1077        }
1078      }
1079      self.persistent.contact_groups = Some (contact_groups);
1080    };
1081    if let Some (group_key) = group_key_a {
1082      update_group (&mut final_proximities, group_key);
1083    }
1084    if let Some (group_key) = group_key_b {
1085      update_group (&mut final_proximities, group_key);
1086    }
1087    if let Ok (contact) = collision_final_proximity.try_into() {
1088      log::trace!(object_pair:?, contact:?; "creating contact @ T==1.0");
1089      self.persistent.add_contact (object_pair, contact);
1090    }
1091    // 6. update AABBs in broad pipeline;
1092    // also remove resolved dynamic objects from intermediate pipeline results
1093    for id in dynamic_modified.iter() {
1094      let i = dynamic_ids.binary_search_by_key (&id, |x| x).unwrap();
1095      let key    = id.key();
1096      let index  = key.index();
1097      let object = &objects_dynamic[index];
1098      let aabb_discrete   = object.aabb_dilated();
1099      let aabb_continuous = Aabb3::union (&toi_aabbs[i], &aabb_discrete);
1100      self.broad.update_aabb_dynamic_discrete (aabb_discrete, key);
1101      self.broad.update_aabb_dynamic_continuous (aabb_continuous, key);
1102    }
1103    self.pipeline.remove_resolved_dynamic (&dynamic_modified);
1104    // TODO: update the modified set in-place ?
1105    self.broad.set_modified (dynamic_modified);
1106    // result
1107    let object_id_a = object_id_a.into();
1108    let object_id_b = object_id_b.into();
1109    event::CollisionResolve {
1110      toi, contact,
1111      object_id_a,       object_id_b,
1112      impulse_normal_a,  impulse_normal_b,
1113      impulse_tangent_a, impulse_tangent_b,
1114      pseudo_impulse_a,  pseudo_impulse_b
1115    }
1116  }
1117
1118  /// Detects AABB overlap pairs of static/dynamic and dynamic/dynamic objects.
1119  ///
1120  /// Excludes Nocollide pairs that were already detected but does not filter
1121  /// object pairs that are already in persistent contacts.
1122  #[inline]
1123  fn broad_overlap_pairs_continuous (&mut self) {
1124    self.broad.overlap_pairs_continuous (
1125      &mut self.pipeline.broad_overlap_pairs, self.pipeline.detect_resolve_iter);
1126    // exclude nocollide pairs that were already detected
1127    self.pipeline.broad_overlap_pairs.retain (
1128      |pair|{
1129        let (id_a, id_b) = (*pair).into();
1130        let id_a = object::Id::from (id_a);
1131        let id_b = object::Id::from (id_b);
1132        for event::Overlap { object_id_a, object_id_b } in
1133          self.pipeline.overlaps.iter()
1134        {
1135          if object_id_a == &id_a && object_id_b == &id_b {
1136            return false
1137          }
1138        }
1139        true
1140      }
1141    );
1142    log::trace!(overlap_pairs:?=self.pipeline.broad_overlap_pairs;
1143      "broad overlap pairs");
1144  }
1145
1146  /// Collects swept AABB TOIs from broad overlap pairs.
1147  ///
1148  /// Filters pairs that are already in persistent contact.
1149  ///
1150  /// Note that the algorithm will aim for a distance of `0.5 *
1151  /// CONTACT_DISTANCE` at each TOI.
1152  fn mid_toi_pairs (&mut self, objects_dynamic : &VecMap <object::Dynamic>) {
1153    use sorted_vec::FindOrInsert;
1154    let last_toi = self.pipeline.resolved_collisions.last()
1155      .map_or (0.0, |collision_resolve| *collision_resolve.toi);
1156    'outer: for object_pair in self.pipeline.broad_overlap_pairs.drain (..) {
1157      let (object_id_a, object_id_b) = object_pair.into();
1158      debug_assert_eq!(object_id_b.kind(), object::Kind::Dynamic);
1159      match object_id_a.kind() {
1160        //
1161        //  static v. dynamic
1162        //
1163        object::Kind::Static => {
1164          let id_static     = object::Id::from (object_id_a);
1165          let id_dynamic    = object::Id::from (object_id_b);
1166          let key_static    = id_static.key;
1167          let key_dynamic   = id_dynamic.key;
1168          let _index_static = key_static.index();
1169          let index_dynamic = key_dynamic.index();
1170          match (
1171            self.persistent.get_contact_count (object_id_a),
1172            self.persistent.get_group_key (object_id_b)
1173          ) {
1174            (Some (static_contact_count), Some (dynamic_group_key)) => {
1175              debug_assert!(static_contact_count > 0);
1176              let group = self.persistent.get_group (dynamic_group_key)
1177                .unwrap();
1178              for (pair, _) in group.contacts.iter() {
1179                if *pair == object_pair {
1180                  continue 'outer
1181                }
1182              }
1183            }
1184            _ => {}
1185          }
1186          // should be safe to unwrap
1187          let dynamic_object         = &objects_dynamic[index_dynamic];
1188          let dynamic_velocity       = dynamic_object.derivatives.velocity;
1189          let dynamic_pseudovelocity = self.pseudo_velocities[index_dynamic];
1190          let dynamic_velocity_effective = dynamic_velocity + dynamic_pseudovelocity;
1191          let static_aabb            = self.broad.get_aabb_static (key_static);
1192          let dynamic_aabb_current   = self.broad
1193            .get_aabb_dynamic_discrete (key_dynamic);
1194          let _dynamic_aabb_swept    = self.broad
1195            .get_aabb_dynamic_continuous (key_dynamic);
1196          let dynamic_aabb_previous  = Aabb3::with_minmax (
1197            dynamic_aabb_current.min() - dynamic_velocity_effective,
1198            dynamic_aabb_current.max() - dynamic_velocity_effective);
1199          // NB: div by zero will result in `inf` values
1200          let dynamic_velocity_effective_reciprocal =
1201            dynamic_velocity_effective.recip();
1202          // these values adjust start/end times to avoid getting too close
1203          let t_margin_x =
1204            (0.5 * CONTACT_DISTANCE * dynamic_velocity_effective_reciprocal.x)
1205              .abs();
1206          let t_margin_y =
1207            (0.5 * CONTACT_DISTANCE * dynamic_velocity_effective_reciprocal.y)
1208              .abs();
1209          let t_margin_z =
1210            (0.5 * CONTACT_DISTANCE * dynamic_velocity_effective_reciprocal.z)
1211              .abs();
1212
1213          let (interval_start_x, interval_end_x) = if
1214            dynamic_velocity_effective.x == 0.0
1215          {
1216            debug_assert!(
1217              dynamic_aabb_current.max().0.x > static_aabb.min().0.x &&
1218              static_aabb.max().0.x > dynamic_aabb_current.min().0.x);
1219            debug_assert!(
1220              dynamic_aabb_previous.max().0.x > static_aabb.min().0.x &&
1221              static_aabb.max().0.x > dynamic_aabb_previous.min().0.x);
1222            (std::f64::NEG_INFINITY, std::f64::INFINITY)
1223          } else if dynamic_velocity_effective.x > 0.0 {
1224            ( (static_aabb.min().0.x - dynamic_aabb_previous.max().0.x) *
1225                dynamic_velocity_effective_reciprocal.x - t_margin_x,
1226              (static_aabb.max().0.x - dynamic_aabb_previous.min().0.x) *
1227                dynamic_velocity_effective_reciprocal.x + t_margin_x
1228            )
1229          } else {
1230            debug_assert!(dynamic_velocity_effective.x < 0.0);
1231            ( (static_aabb.max().0.x - dynamic_aabb_previous.min().0.x) *
1232                dynamic_velocity_effective_reciprocal.x - t_margin_x,
1233              (static_aabb.min().0.x - dynamic_aabb_previous.max().0.x) *
1234                dynamic_velocity_effective_reciprocal.x + t_margin_x
1235            )
1236          };
1237
1238          let (interval_start_y, interval_end_y) = if
1239            dynamic_velocity_effective.y == 0.0
1240          {
1241            debug_assert!(
1242              dynamic_aabb_current.max().0.y > static_aabb.min().0.y &&
1243              static_aabb.max().0.y > dynamic_aabb_current.min().0.y);
1244            debug_assert!(
1245              dynamic_aabb_previous.max().0.y > static_aabb.min().0.y &&
1246              static_aabb.max().0.y > dynamic_aabb_previous.min().0.y);
1247            (std::f64::NEG_INFINITY, std::f64::INFINITY)
1248          } else if dynamic_velocity_effective.y > 0.0 {
1249            ( dynamic_velocity_effective_reciprocal.y *
1250                (static_aabb.min().0.y - dynamic_aabb_previous.max().0.y)
1251                - t_margin_y,
1252              dynamic_velocity_effective_reciprocal.y *
1253                (static_aabb.max().0.y - dynamic_aabb_previous.min().0.y)
1254                + t_margin_y
1255            )
1256          } else {
1257            debug_assert!(dynamic_velocity_effective.y < 0.0);
1258            ( dynamic_velocity_effective_reciprocal.y *
1259                (static_aabb.max().0.y - dynamic_aabb_previous.min().0.y)
1260                - t_margin_y,
1261              dynamic_velocity_effective_reciprocal.y *
1262                (static_aabb.min().0.y - dynamic_aabb_previous.max().0.y)
1263                + t_margin_y
1264            )
1265          };
1266
1267          let (interval_start_z, interval_end_z) = if
1268            dynamic_velocity_effective.z == 0.0
1269          {
1270            debug_assert!(
1271              dynamic_aabb_current.max().0.z > static_aabb.min().0.z &&
1272              static_aabb.max().0.z > dynamic_aabb_current.min().0.z);
1273            debug_assert!(
1274              dynamic_aabb_previous.max().0.z > static_aabb.min().0.z &&
1275              static_aabb.max().0.z > dynamic_aabb_previous.min().0.z);
1276            (std::f64::NEG_INFINITY, std::f64::INFINITY)
1277          } else if dynamic_velocity_effective.z > 0.0 {
1278            ( dynamic_velocity_effective_reciprocal.z *
1279                (static_aabb.min().0.z - dynamic_aabb_previous.max().0.z)
1280                - t_margin_z,
1281              dynamic_velocity_effective_reciprocal.z *
1282                (static_aabb.max().0.z - dynamic_aabb_previous.min().0.z)
1283                + t_margin_z
1284            )
1285          } else {
1286            debug_assert!(dynamic_velocity_effective.z < 0.0);
1287            ( dynamic_velocity_effective_reciprocal.z *
1288                (static_aabb.max().0.z - dynamic_aabb_previous.min().0.z)
1289                - t_margin_z,
1290              dynamic_velocity_effective_reciprocal.z *
1291                (static_aabb.min().0.z - dynamic_aabb_previous.max().0.z)
1292                + t_margin_z
1293            )
1294          };
1295
1296          if let Some ((interval_start, interval_end)) =
1297            if let Some ((interval_start_xy, interval_end_xy)) =
1298              if interval_start_x < interval_end_y &&
1299                 interval_start_y < interval_end_x
1300              {
1301                Some ((
1302                  f64::max (interval_start_x, interval_start_y),
1303                  f64::min (interval_end_x, interval_end_y)
1304                ))
1305              } else {
1306                None
1307              }
1308            {
1309              if interval_start_xy < interval_end_z &&
1310                 interval_start_z  < interval_end_xy
1311              {
1312                Some ((
1313                  f64::max (interval_start_xy, interval_start_z),
1314                  f64::min (interval_end_xy, interval_end_z)
1315                ))
1316              } else {
1317                None
1318              }
1319            } else {
1320              None
1321            }
1322          {
1323            if interval_start < 1.0 && 0.0 < interval_end {
1324              let mid_toi_pair = (
1325                Normalized::clamp (f64::max (interval_start, last_toi)),
1326                Normalized::clamp (interval_end),
1327                object_pair
1328              );
1329              match self.pipeline.mid_toi_pairs
1330                .find_or_insert (Reverse (mid_toi_pair))
1331              {
1332                FindOrInsert::Inserted (_) => {}
1333                FindOrInsert::Found    (_) => unreachable!()
1334              }
1335            }
1336          }
1337        }
1338        //
1339        //  dynamic v. dynamic
1340        //
1341        object::Kind::Dynamic => {
1342          let key_a   = object_id_a.key();
1343          let key_b   = object_id_b.key();
1344          let index_a = key_a.index();
1345          let index_b = key_b.index();
1346          match (
1347            self.persistent.get_group_key (object_id_a),
1348            self.persistent.get_group_key (object_id_b)
1349          ) {
1350            (Some (group_key_a), Some (group_key_b)) =>
1351              if group_key_a == group_key_b {
1352                let group = self.persistent.get_group (group_key_a).unwrap();
1353                for (pair, _) in group.contacts.iter() {
1354                  if *pair == object_pair {
1355                    continue 'outer
1356                  }
1357                }
1358              }
1359            _ => {}
1360          }
1361          // should be safe to unwrap: objects should not be destroyed during
1362          // collision detection
1363          let object_a          = &objects_dynamic[index_a];
1364          let object_b          = &objects_dynamic[index_b];
1365          let velocity_a        = object_a.derivatives.velocity;
1366          let velocity_b        = object_b.derivatives.velocity;
1367          let pseudovelocity_a  = self.pseudo_velocities[key_a.index()];
1368          let pseudovelocity_b  = self.pseudo_velocities[key_b.index()];
1369          let velocity_effective_a = velocity_a + pseudovelocity_a;
1370          let velocity_effective_b = velocity_b + pseudovelocity_b;
1371          let velocity_effective_relative =
1372            velocity_effective_a - velocity_effective_b;
1373          let velocity_effective_relative_reciprocal =
1374            velocity_effective_relative.recip();
1375          let aabb_current_a    = self.broad.get_aabb_dynamic_discrete   (key_a);
1376          let aabb_current_b    = self.broad.get_aabb_dynamic_discrete   (key_b);
1377          let _aabb_swept_a     = self.broad.get_aabb_dynamic_continuous (key_a);
1378          let _aabb_swept_b     = self.broad.get_aabb_dynamic_continuous (key_b);
1379          let aabb_previous_a   = Aabb3::with_minmax (
1380            aabb_current_a.min() - velocity_effective_a,
1381            aabb_current_a.max() - velocity_effective_a);
1382          let aabb_previous_b   = Aabb3::with_minmax (
1383            aabb_current_b.min() - velocity_effective_b,
1384            aabb_current_b.max() - velocity_effective_b);
1385
1386          // compute overlap time start/end interval for the given axis
1387          let overlap_interval_axis = |axis : Axis3| {
1388            let axis_index = axis as usize;
1389            // div by zero will result in `inf` values; t_margin adjusts
1390            // start/end times to avoid getting too close
1391            let t_margin = (0.5 * CONTACT_DISTANCE *
1392              velocity_effective_relative_reciprocal[axis_index]).abs();
1393            let velocity_relative = velocity_effective_relative[axis_index];
1394            let velocity_relative_reciprocal =
1395              velocity_effective_relative_reciprocal[axis_index];
1396            let prev_a_min = aabb_previous_a.min().0[axis_index];
1397            let prev_a_max = aabb_previous_a.max().0[axis_index];
1398            let prev_b_min = aabb_previous_b.min().0[axis_index];
1399            let prev_b_max = aabb_previous_b.max().0[axis_index];
1400            if velocity_relative == 0.0 {
1401              // NOTE: even though the relative velocity is zero, the previous
1402              // AABBs may not be overlapping due to rounding errors; we will
1403              // still consider the begin/end time to be infinite
1404              geometry::Interval::with_minmax (
1405                std::f64::NEG_INFINITY, std::f64::INFINITY)
1406            } else if velocity_relative > 0.0 {
1407              geometry::Interval::with_minmax (
1408                (prev_b_min - prev_a_max) * velocity_relative_reciprocal -
1409                  t_margin,
1410                (prev_b_max - prev_a_min) * velocity_relative_reciprocal +
1411                  t_margin
1412              )
1413            } else {
1414              debug_assert!(velocity_relative < 0.0);
1415              geometry::Interval::with_minmax (
1416                (prev_b_max - prev_a_min) * velocity_relative_reciprocal -
1417                  t_margin,
1418                (prev_b_min - prev_a_max) * velocity_relative_reciprocal +
1419                  t_margin
1420              )
1421            }
1422          };
1423          let overlap_interval_x = overlap_interval_axis (Axis3::X);
1424          let overlap_interval_y = overlap_interval_axis (Axis3::Y);
1425          let overlap_interval_z = overlap_interval_axis (Axis3::Z);
1426
1427          if let Some (interval) = overlap_interval_x
1428            .intersection (overlap_interval_y).and_then (|overlap_interval_xy|
1429              overlap_interval_xy.intersection (overlap_interval_z))
1430          {
1431            if interval.min() < 1.0 && 0.0 < interval.max() {
1432              let mid_toi_pair = (
1433                Normalized::clamp (f64::max (interval.min(), last_toi)),
1434                Normalized::clamp (interval.max()),
1435                object_pair
1436              );
1437              match self.pipeline.mid_toi_pairs
1438                .find_or_insert (Reverse (mid_toi_pair))
1439              {
1440                FindOrInsert::Inserted (_) => {}
1441                FindOrInsert::Found    (_) => unreachable!()
1442              }
1443            }
1444          }
1445        }
1446        _ => unreachable!()
1447      }
1448    }
1449
1450    log::trace!(mid_toi_pairs:?=self.pipeline.mid_toi_pairs; "mid TOI pairs");
1451
1452    self.pipeline.broad_overlap_pairs.clear();
1453  }
1454
1455  /// Returns narrow TOI pairs.
1456  ///
1457  /// Note that the algorithm will aim for a distance of `0.5 *
1458  /// CONTACT_DISTANCE` at each TOI.
1459  fn narrow_toi_contacts (&mut self,
1460    objects_static  : &VecMap <object::Static>,
1461    objects_dynamic : &VecMap <object::Dynamic>
1462  ) {
1463    use sorted_vec::FindOrInsert;
1464
1465    let narrow_toi_contacts = &mut self.pipeline.narrow_toi_contacts;
1466    while let Some (mid_toi_pair) = self.pipeline.mid_toi_pairs.last().cloned() {
1467      let earliest_toi_contact = if !narrow_toi_contacts.is_empty() {
1468        narrow_toi_contacts[0].0.0
1469      } else {
1470        Normalized::clamp (1.0)
1471      };
1472
1473      if mid_toi_pair.0.0 < earliest_toi_contact {
1474        let (mid_toi_start, _mid_toi_end, object_pair) =
1475          mid_toi_pair.0.clone();
1476        let (object_id_a, object_id_b) = object_pair.into();
1477        let kind_a  = object_id_a.kind();
1478        let kind_b  = object_id_b.kind();
1479        match (kind_a, kind_b) {
1480          (object::Kind::Static, object::Kind::Dynamic)  => {
1481            let key_static    = object_id_a.key();
1482            let key_dynamic   = object_id_b.key();
1483            let index_static  = key_static.index();
1484            let index_dynamic = key_dynamic.index();
1485            // safe to unwrap
1486            let mut dynamic_object = objects_dynamic[index_dynamic].clone();
1487            let dynamic_velocity   = dynamic_object.derivatives.velocity;
1488            let dynamic_pseudovelocity = self.pseudo_velocities[index_dynamic];
1489            let dynamic_velocity_effective =
1490              dynamic_velocity - dynamic_pseudovelocity;
1491            let static_object      = objects_static[index_static].clone();
1492            let mut narrow_toi     = *mid_toi_start;
1493            // rewind object to the narrow_toi: the object is currently at
1494            // t==1.0, so we are lerping a negative value
1495            lerp_object (&mut dynamic_object, dynamic_pseudovelocity,
1496              -1.0 + narrow_toi);
1497            let mut t_remaining    = 1.0 - narrow_toi;
1498            #[allow(unused_variables)]
1499            let mut iter           = 0;
1500            loop {
1501              #[cfg(debug_assertions)]
1502              if self.narrow_toi_max_iter_count < iter {
1503                self.narrow_toi_max_iter_count = iter;
1504              }
1505              let proximity = Proximity::query (&static_object, &dynamic_object);
1506              if cfg!(debug_assertions) {
1507                if dynamic_object.collidable && static_object.collidable {
1508                  debug_assert!(proximity.distance >= 0.0);
1509                }
1510              }
1511              let dynamic_velocity_effective_normal =
1512                dynamic_velocity_effective.dot (-*proximity.normal);
1513              if dynamic_velocity_effective_normal > 0.0 &&
1514                dynamic_object.collidable && static_object.collidable
1515              {
1516                // no collision: separating velocity zero or positive
1517                log::trace!(
1518                  t=narrow_toi, velocity=dynamic_velocity_effective_normal;
1519                  "collision rejected, separating velocity");
1520                break
1521              } else if proximity.distance < CONTACT_DISTANCE {
1522                debug_assert!(dynamic_velocity_effective_normal <= 0.0 ||
1523                  !dynamic_object.collidable || !static_object.collidable);
1524                // collision: toi contact
1525                log::trace!(t=narrow_toi, proximity:?; "collision detected");
1526                let contact     = Contact { constraint: proximity.into() };
1527                let restitution = dynamic_object.material.restitution *
1528                  static_object.material.restitution;
1529                let collision   = contact::Colliding { contact, restitution };
1530                let narrow_toi_contact = Reverse ((
1531                  Normalized::noisy (narrow_toi), object_pair, collision
1532                ));
1533                match narrow_toi_contacts.find_or_insert (narrow_toi_contact) {
1534                  FindOrInsert::Inserted (_) => {}
1535                  FindOrInsert::Found    (_) => unreachable!()
1536                }
1537                break
1538              } else if dynamic_velocity_effective_normal == 0.0 {
1539                log::trace!(
1540                  t=narrow_toi, velocity=dynamic_velocity_effective_normal;
1541                  "collision rejected, zero relative velocity and objects not \
1542                  in contact");
1543                break
1544              }
1545              let dynamic_velocity_effective_normal_reciprocal =
1546                1.0 / dynamic_velocity_effective_normal;
1547              let toi_axis = narrow_toi +
1548                (proximity.distance - 0.5 * CONTACT_DISTANCE) *
1549                (-dynamic_velocity_effective_normal_reciprocal);
1550              debug_assert!(toi_axis > narrow_toi ||
1551                !dynamic_object.collidable || !static_object.collidable);
1552              if toi_axis > 1.0 || toi_axis < 0.0 {
1553                // no collision this step
1554                log::trace!(t=narrow_toi, proximity:?;
1555                  "collision rejected, final proximity");
1556                break
1557              }
1558              let t_advance = toi_axis - narrow_toi;
1559              lerp_object (&mut dynamic_object, dynamic_pseudovelocity, t_advance);
1560              narrow_toi    = toi_axis;
1561              t_remaining   -= t_advance;
1562              debug_assert!(t_remaining > 0.0);
1563              iter += 1;
1564            }
1565          }
1566
1567          (object::Kind::Dynamic, object::Kind::Dynamic) => {
1568            let key_a   = object_id_a.key();
1569            let key_b   = object_id_b.key();
1570            let index_a = key_a.index();
1571            let index_b = key_b.index();
1572             // safe to unwrap: objects should not be destroyed during collision
1573             // detection
1574            let mut object_a     = objects_dynamic[index_a].clone();
1575            let mut object_b     = objects_dynamic[index_b].clone();
1576            let pseudovelocity_a = self.pseudo_velocities[index_a];
1577            let pseudovelocity_b = self.pseudo_velocities[index_b];
1578            let velocity_a       = object_a.derivatives.velocity;
1579            let velocity_b       = object_b.derivatives.velocity;
1580            let velocity_effective_a = velocity_a + pseudovelocity_a;
1581            let velocity_effective_b = velocity_b + pseudovelocity_b;
1582            let velocity_effective_relative =
1583              velocity_effective_a - velocity_effective_b;
1584            let mut narrow_toi   = *mid_toi_start;
1585            let mut t_remaining  = 1.0 - narrow_toi;
1586            lerp_object (&mut object_a, pseudovelocity_a, -1.0 + narrow_toi);
1587            lerp_object (&mut object_b, pseudovelocity_b, -1.0 + narrow_toi);
1588            loop {
1589              debug_assert!(narrow_toi <= 1.0);
1590              debug_assert!(t_remaining >= 0.0);
1591              let proximity = Proximity::query (&object_a, &object_b);
1592              if cfg!(debug_assertions) {
1593                if object_a.collidable && object_b.collidable {
1594                  debug_assert!(proximity.distance >= 0.0);
1595                }
1596              }
1597              let velocity_effective_relative_normal =
1598                velocity_effective_relative.dot (*proximity.normal);
1599              if velocity_effective_relative_normal > 0.0 &&
1600                object_a.collidable && object_b.collidable
1601              {
1602                // no collision: separating velocity zero or positive
1603                log::trace!(
1604                  t=narrow_toi, velocity=velocity_effective_relative_normal;
1605                  "collision rejected, separating velocity");
1606                break
1607              } else if proximity.distance < CONTACT_DISTANCE {
1608                debug_assert!(velocity_effective_relative_normal <= 0.0 ||
1609                  !object_a.collidable || !object_b.collidable);
1610                // collision: toi contact
1611                log::trace!(t=narrow_toi, proximity:?; "collision detected");
1612                let contact     = Contact { constraint: proximity.into() };
1613                let restitution =
1614                  object_a.material.restitution * object_b.material.restitution;
1615                let collision   = contact::Colliding { contact, restitution };
1616                let narrow_toi_contact = Reverse ((
1617                  Normalized::noisy (narrow_toi), object_pair, collision
1618                ));
1619                match narrow_toi_contacts.find_or_insert (narrow_toi_contact) {
1620                  FindOrInsert::Inserted (_) => {}
1621                  FindOrInsert::Found    (_) => unreachable!()
1622                }
1623                break
1624              } else if velocity_effective_relative_normal == 0.0 {
1625                log::trace!(
1626                  t=narrow_toi, velocity=velocity_effective_relative_normal;
1627                  "collision rejected, zero relative velocity and objects not \
1628                  in contact");
1629                break
1630              }
1631              let velocity_effective_relative_normal_reciprocal =
1632                1.0 / velocity_effective_relative_normal;
1633              let toi_axis = narrow_toi +
1634                (proximity.distance - 0.5 * CONTACT_DISTANCE) *
1635                (-velocity_effective_relative_normal_reciprocal);
1636              debug_assert!(toi_axis > narrow_toi ||
1637                !object_a.collidable || !object_b.collidable);
1638              if toi_axis > 1.0 || toi_axis < 0.0 {
1639                // no collision this step
1640                log::trace!(t=narrow_toi, proximity:?;
1641                  "collision rejected, final proximity");
1642                break
1643              }
1644              let t_advance = toi_axis - narrow_toi;
1645              lerp_object (&mut object_a, pseudovelocity_a, t_advance);
1646              lerp_object (&mut object_b, pseudovelocity_b, t_advance);
1647              narrow_toi    = toi_axis;
1648              t_remaining   -= t_advance;
1649            }
1650          }
1651          _ => unreachable!()
1652        }
1653        self.pipeline.mid_toi_pairs.pop();
1654      } else { break }
1655    }
1656    #[cfg(debug_assertions)]
1657    log::debug!(max_iter_count=self.narrow_toi_max_iter_count;
1658      "narrow TOI max iters");
1659    log::trace!(contacts:?=narrow_toi_contacts; "narrow TOI contacts");
1660  }
1661
1662  #[inline]
1663  fn add_object_static (&mut self,
1664    object     : &object::Static,
1665    object_key : object::Key,
1666  ) {
1667    self.broad.add_object_static (object.aabb_dilated(), object_key);
1668  }
1669
1670  #[inline]
1671  fn add_object_dynamic (&mut self,
1672    object     : &object::Dynamic,
1673    object_key : object::Key
1674  ) {
1675    let aabb_discrete   = object.aabb_dilated();
1676    let aabb_continuous = Aabb3::with_minmax (
1677      aabb_discrete.min() - object.derivatives.velocity,
1678      aabb_discrete.max() - object.derivatives.velocity);
1679    // NB: this swept AABB should not be seen by the next continuous collision
1680    // detection/resolution loop since `begin_step()` will re-calculate for the
1681    // next frame
1682    self.broad.add_object_dynamic (aabb_discrete, aabb_continuous, object_key);
1683    assert!(self.pseudo_velocities
1684      .insert (object_key.index(), Vector3::zero()).is_none());
1685  }
1686
1687}
1688
1689impl Pipeline {
1690  #[inline]
1691  pub fn is_empty (&self) -> bool {
1692    self.broad_overlap_pairs.is_empty() &&
1693    self.mid_toi_pairs.is_empty() &&
1694    self.narrow_toi_contacts.is_empty() &&
1695    self.resolved_collisions.is_empty() &&
1696    self.overlaps.is_empty()
1697  }
1698
1699  /// After a collision is resolved, all modified (dynamic) objects should be
1700  /// removed from intermediate results in the pipeline:
1701  ///
1702  /// - `mid_toi_pairs`
1703  /// - `narrow_toi_contacts`
1704  ///
1705  /// These objects will be checked for overlaps in the broad phase of the next
1706  /// detect/resolve loop iteration.
1707  // TODO: because we are only removing resolved dynamic objects and the
1708  // resolved list is sorted, we could be slicing the resolved set to only
1709  // contain the dynamic ids and exclude the static ids from binary search
1710  #[inline]
1711  pub fn remove_resolved_dynamic (&mut self, resolved : &SortedSet <InternalId>) {
1712    debug_assert!(self.broad_overlap_pairs.is_empty());
1713
1714    let (mut i, mut len);
1715
1716    // remove intermediate mid TOI pairs
1717    i   = 0;
1718    len = self.mid_toi_pairs.len();
1719    while i < len {
1720      let (_, _, object_pair) = self.mid_toi_pairs[i].clone().0;
1721      let (id_a, id_b) = object_pair.into();
1722      if id_a.kind() == object::Kind::Dynamic {
1723        if resolved.binary_search (&id_a).is_ok() {
1724          self.mid_toi_pairs.remove_index (i);
1725          len -= 1;
1726          continue
1727        }
1728      }
1729      debug_assert_eq!(id_b.kind(), object::Kind::Dynamic);
1730      if resolved.binary_search (&id_b).is_ok() {
1731        self.mid_toi_pairs.remove_index (i);
1732        len -= 1;
1733        continue
1734      }
1735      i += 1;
1736    }
1737
1738    // remove intermediate narrow TOI contacts
1739    i   = 0;
1740    len = self.narrow_toi_contacts.len();
1741    while i < len {
1742      let (_, object_pair, _) = self.narrow_toi_contacts[i].clone().0;
1743      let (id_a, id_b) = object_pair.into();
1744      if id_a.kind() == object::Kind::Dynamic {
1745        if resolved.binary_search (&id_a).is_ok() {
1746          self.narrow_toi_contacts.remove_index (i);
1747          len -= 1;
1748          continue
1749        }
1750      }
1751      debug_assert_eq!(id_b.kind(), object::Kind::Dynamic);
1752      if resolved.binary_search (&id_b).is_ok() {
1753        self.narrow_toi_contacts.remove_index (i);
1754        len -= 1;
1755        continue
1756      }
1757      i += 1;
1758    }
1759  }
1760}
1761
1762impl ObjectPair {
1763  /// Create a new object pair.
1764  ///
1765  /// One object must be dynamic. Internally the object IDs will be stored in
1766  /// sorted order, and a static object will always be first.
1767  pub fn new (id_a : InternalId, id_b : InternalId) -> Self {
1768    debug_assert!(id_a.kind() == object::Kind::Dynamic ||
1769      id_b.kind() == object::Kind::Dynamic);
1770    if id_a < id_b {
1771      ObjectPair (id_a, id_b)
1772    } else {
1773      debug_assert!(id_a > id_b, "overlap ids should not be identical");
1774      ObjectPair (id_b, id_a)
1775    }
1776  }
1777}
1778
1779impl From <(InternalId, InternalId)> for ObjectPair {
1780  fn from ((id_a, id_b) : (InternalId, InternalId)) -> Self {
1781    ObjectPair::new (id_a, id_b)
1782  }
1783}
1784
1785impl InternalId {
1786  #[inline]
1787  pub fn new_static (key : object::Key) -> Self {
1788    debug_assert!(key.value() < OBJECT_KEY_MAX);
1789    InternalId (key.value())
1790  }
1791  #[inline]
1792  pub fn new_dynamic (key : object::Key) -> Self {
1793    debug_assert!(key.value() < OBJECT_KEY_MAX);
1794    InternalId (key.value() | INTERNAL_ID_DYNAMIC_BIT)
1795  }
1796  #[inline]
1797  pub fn kind (&self) -> object::Kind {
1798    match self.0 & INTERNAL_ID_DYNAMIC_BIT > 0 {
1799      true  => object::Kind::Dynamic,
1800      false => object::Kind::Static
1801    }
1802  }
1803  #[inline]
1804  pub fn key (&self) -> object::Key {
1805    object::Key::from (self.0 & !INTERNAL_ID_DYNAMIC_BIT)
1806  }
1807}
1808impl From <InternalId> for object::Id {
1809  fn from (id : InternalId) -> Self {
1810    object::Id {
1811      kind: id.kind(),
1812      key:  id.key()
1813    }
1814  }
1815}