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