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])
583          };
584          let mut pseudovelocity_b = self.pseudo_velocities[index_b];
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  #[allow(clippy::too_many_arguments)]
791  fn resolve (
792    &mut self,
793    objects_static  : &VecMap <object::Static>,
794    objects_dynamic : &mut VecMap <object::Dynamic>,
795    toi             : Normalized <f64>,
796    object_id_a     : InternalId,
797    object_id_b     : InternalId,
798    contact         : contact::Colliding,
799    group_key_a     : Option <contact::group::KeyType>,
800    group_key_b     : Option <contact::group::KeyType>
801  ) -> event::CollisionResolve {
802    // 1. lerp all involved dynamic objects to the TOI
803    // 2. constrain velocities
804    // 3. lerp to T==1.0
805    // 4. constrain positions
806    // 5. create or destroy persistent contacts from final proximity check
807    // 6. update AABBs in broad pipeline
808    use math::num_traits::Zero;
809    let mut dynamic_ids = SortedSet::new();
810    if object_id_a.kind() == object::Kind::Dynamic {
811      dynamic_ids.push (object_id_a);
812    };
813    dynamic_ids.push (object_id_b);
814    let group_a = group_key_a
815      .map (|group_key| self.persistent.get_group (group_key).unwrap());
816    let group_b = group_key_b
817      .map (|group_key| self.persistent.get_group (group_key).unwrap());
818    group_a.map (|group| dynamic_ids.extend (group.dynamic_ids().into_vec()));
819    group_b.map (|group| dynamic_ids.extend (group.dynamic_ids().into_vec()));
820    // track whether a dynamic object was modified; if so it will need to be
821    // removed from intermediate results in the pipeline and checked for new
822    // collisions
823    let mut dynamic_modified = SortedSet::new();
824    // 1. lerp to TOI
825    let lerp_objects =
826      |objects_dynamic : &mut VecMap <object::Dynamic>, time_delta|
827      for id in dynamic_ids.iter() {
828        let index          = id.key().index();
829        let object         = &mut objects_dynamic[index];
830        let pseudovelocity = self.pseudo_velocities[index];
831        lerp_object (object, pseudovelocity, time_delta);
832      };
833    let (t_reverse, t_forward) = {
834      let toi_f64 : f64 = *toi;   // [0.0, 1.0]
835      (-1.0 + toi_f64, 1.0 - toi_f64)
836    };
837    lerp_objects (objects_dynamic, t_reverse);
838    let toi_aabbs = dynamic_ids.iter().map (|id|{
839      let index = id.key().index();
840      objects_dynamic[index].aabb_dilated()
841    }).collect::<Vec <_>>();
842    // 2. constrain velocities
843    let constrain_velocity = |
844      objects_dynamic  : &mut VecMap <object::Dynamic>,
845      pseudovelocities : &VecMap <Vector3 <f64>>,
846      contact          : Contact,
847      restitution      : f64,
848      object_id_a      : InternalId,
849      object_id_b      : InternalId
850    | {
851      let key_a    = object_id_a.key();
852      let key_b    = object_id_b.key();
853      let index_a  = key_a.index();
854      let index_b  = key_b.index();
855      let mut object_a = match object_id_a.kind() {
856        object::Kind::Static  => Either::Left  (objects_static[index_a].clone()),
857        object::Kind::Dynamic => Either::Right (objects_dynamic[index_a].clone()),
858        _ => unreachable!()
859      };
860      debug_assert_eq!(object_id_b.kind(), object::Kind::Dynamic);
861      let mut object_b     = objects_dynamic[index_b].clone();
862      let pseudovelocity_a = if object_id_a.kind() == object::Kind::Dynamic {
863        Some (pseudovelocities[index_a])
864      } else {
865        None
866      };
867      let pseudovelocity_b = pseudovelocities[index_b];
868      let maybe_impulses   = contact_constrain_velocity (
869        contact, restitution,
870        object_a.as_mut().map_left (|object_static| &*object_static),
871        &mut object_b, pseudovelocity_a, pseudovelocity_b
872      );
873      if maybe_impulses.is_some() {
874        match object_a {
875          Either::Right (object_a) => objects_dynamic[index_a] = object_a,
876          Either::Left  (_) => {}
877        }
878        objects_dynamic[index_b] = object_b;
879      }
880      maybe_impulses
881    };
882    let mut impulse_normal_a  = Vector3::zero();
883    let mut impulse_normal_b  = Vector3::zero();
884    let mut impulse_tangent_a = Vector3::zero();
885    let mut impulse_tangent_b = Vector3::zero();
886    let mut iter = 0;
887    loop {
888      let mut satisfied = true;
889      if let Some (impulses) = constrain_velocity (
890        objects_dynamic, &self.pseudo_velocities, contact.contact.clone(),
891        contact.restitution, object_id_a, object_id_b
892      ) {
893        // if there are no groups, then the collision is isolated and satisfied
894        satisfied = group_a.is_none() && group_b.is_none();
895        impulse_normal_a  += impulses.impulse_normal_a;
896        impulse_normal_b  += impulses.impulse_normal_b;
897        impulse_tangent_a += impulses.impulse_tangent_a;
898        impulse_tangent_b += impulses.impulse_tangent_b;
899      }
900      let mut constrain_group = |group : &contact::Group|
901        for (object_pair, contact) in group.contacts.iter().cloned() {
902          let (object_id_a, object_id_b) = object_pair.into();
903          if constrain_velocity (
904            objects_dynamic, &self.pseudo_velocities, contact, 0.0,
905            object_id_a, object_id_b
906          ).is_some() {
907            if object_id_a.kind() == object::Kind::Dynamic {
908              dynamic_modified.push (object_id_a);
909            }
910            dynamic_modified.push (object_id_b);
911            satisfied = false;
912          }
913        };
914      if let Some (group) = group_a {
915        constrain_group (group);
916      }
917      if let Some (group) = group_b {
918        constrain_group (group);
919      }
920      iter += 1;
921      if satisfied {
922        log::debug!(iters=iter; "collision velocity constraint satisfied");
923        break
924      }
925    }
926    // 3. lerp to T==1.0
927    lerp_objects (objects_dynamic, t_forward);
928    // 4. constrain positions
929    let constrain_position = |
930      objects_dynamic  : &mut VecMap <object::Dynamic>,
931      pseudovelocities : &mut VecMap <Vector3 <f64>>,
932      object_id_a      : InternalId,
933      object_id_b      : InternalId
934    | {
935      let key_a    = object_id_a.key();
936      let key_b    = object_id_b.key();
937      let index_a  = key_a.index();
938      let index_b  = key_b.index();
939      let mut object_a = match object_id_a.kind() {
940        object::Kind::Static  => Either::Left  (objects_static[index_a].clone()),
941        object::Kind::Dynamic => Either::Right (objects_dynamic[index_a].clone()),
942        _ => unreachable!()
943      };
944      debug_assert_eq!(object_id_b.kind(), object::Kind::Dynamic);
945      let mut object_b = objects_dynamic[index_b].clone();
946      let mut pseudovelocity_a = if object_id_a.kind() == object::Kind::Dynamic {
947        Some (pseudovelocities[index_a])
948      } else {
949        None
950      };
951      let mut pseudovelocity_b = pseudovelocities[index_b];
952      let (maybe_impulses, proximity) = contact_constrain_position (
953        t_reverse,
954        object_a.as_mut().map_left (|object_static| &*object_static),
955        &mut object_b, pseudovelocity_a.as_mut(), &mut pseudovelocity_b
956      );
957      if maybe_impulses.is_some() {
958        match object_a {
959          Either::Right (object_a) => {
960            objects_dynamic[index_a]  = object_a;
961            pseudovelocities[index_a] = pseudovelocity_a.unwrap();
962          }
963          Either::Left  (_) => {}
964        }
965        objects_dynamic[index_b]  = object_b;
966        pseudovelocities[index_b] = pseudovelocity_b;
967      }
968      (maybe_impulses, proximity)
969    };
970    let mut final_proximities = vec![
971      Proximity {
972        distance:  0.0,
973        half_axis: Vector3::zero(),
974        midpoint:  Point::origin(),
975        normal:    Unit3::axis_z()
976      };
977      1 + group_a.map (|group| group.contacts.len()).unwrap_or (0) +
978      group_b.map (|group| group.contacts.len()).unwrap_or (0)
979    ];
980    let mut pseudo_impulse_a = Vector3::zero();
981    let mut pseudo_impulse_b = Vector3::zero();
982    let mut iter = 0;
983    loop {
984      let mut satisfied     = true;
985      let mut contact_index = 0;
986      let (maybe_impulses, proximity) = constrain_position (
987        objects_dynamic, &mut self.pseudo_velocities, object_id_a, object_id_b
988      );
989      final_proximities[contact_index] = proximity;
990      contact_index += 1;
991      if let Some (impulses) = maybe_impulses {
992        // if there are no groups, then the collision is isolated and satisfied
993        satisfied = group_a.is_none() && group_b.is_none();
994        pseudo_impulse_a += impulses.pseudo_impulse_a;
995        pseudo_impulse_b += impulses.pseudo_impulse_b;
996      }
997      let mut constrain_group = |group : &contact::Group|
998        for (object_pair, _) in group.contacts.iter() {
999          let (object_id_a, object_id_b)  = (*object_pair).into();
1000          let (maybe_impulses, proximity) = constrain_position (
1001            objects_dynamic, &mut self.pseudo_velocities,
1002            object_id_a, object_id_b
1003          );
1004          final_proximities[contact_index] = proximity;
1005          contact_index += 1;
1006          if maybe_impulses.is_some() {
1007            if object_id_a.kind() == object::Kind::Dynamic {
1008              dynamic_modified.push (object_id_a);
1009            }
1010            dynamic_modified.push (object_id_b);
1011            satisfied = false;
1012          }
1013        };
1014      if let Some (group) = group_a {
1015        constrain_group (group);
1016      }
1017      if let Some (group) = group_b {
1018        constrain_group (group);
1019      }
1020      iter += 1;
1021      if satisfied {
1022        log::debug!(iters=iter; "collision position constraint satisfied");
1023        break
1024      }
1025    }
1026    // 5. create or destroy persistent contacts from final proximity check;
1027    // also add resolved pairs to the broad phase here since we are iterating
1028    // over contact groups one last time
1029    let object_pair = (object_id_a, object_id_b).into();
1030    if !impulse_normal_b.is_zero() || !impulse_tangent_b.is_zero() ||
1031      !pseudo_impulse_b.is_zero()
1032    {
1033      if object_id_a.kind() == object::Kind::Dynamic {
1034        debug_assert!(!impulse_normal_a.is_zero() ||
1035          !impulse_tangent_a.is_zero() || !pseudo_impulse_a.is_zero());
1036        dynamic_modified.push (object_id_a);
1037      }
1038      dynamic_modified.push (object_id_b);
1039    }
1040    self.broad.add_resolved (object_pair);
1041    let mut final_proximities     = final_proximities.into_iter();
1042    let collision_final_proximity = final_proximities.next().unwrap();
1043    // wait until after updating groups to create any new contacts in case the
1044    // groups are merged which would invalidate one of the old group keys
1045    let mut update_group = |
1046      final_proximities : &mut std::vec::IntoIter <Proximity>,
1047      group_key         : contact::group::KeyType
1048    | {
1049      let mut contact_groups = self.persistent.contact_groups.take().unwrap();
1050      let group = &mut contact_groups[group_key as usize];
1051      let mut remove_contacts = vec![];
1052      for (i, proximity) in
1053        final_proximities.take (group.contacts.len()).enumerate()
1054      {
1055        let contact = &mut group.contacts[i];
1056        self.broad.add_resolved (contact.0);
1057        if let Ok (update_contact) = Contact::try_from (proximity) {
1058          contact.1 = update_contact;
1059        } else {
1060          remove_contacts.push (i as u32);
1061        }
1062      }
1063      if !remove_contacts.is_empty() {
1064        self.persistent.remove_contacts (group, remove_contacts.as_slice());
1065        if group.contacts.is_empty() {
1066          let _ = contact_groups.take (group_key as usize).unwrap();
1067        } else {
1068          let partitions = group.partition();
1069          if !partitions.is_empty() {
1070            let _ = contact_groups.take (group_key as usize).unwrap();
1071            for new_group in partitions {
1072              let dynamic_ids = new_group.dynamic_ids();
1073              let group_key   = contact_groups.put (new_group)
1074                as contact::group::KeyType;
1075              for dynamic_id in dynamic_ids.into_vec() {
1076                self.persistent.change_dynamic_group_key (dynamic_id, group_key);
1077              }
1078            }
1079          }
1080        }
1081      }
1082      self.persistent.contact_groups = Some (contact_groups);
1083    };
1084    if let Some (group_key) = group_key_a {
1085      update_group (&mut final_proximities, group_key);
1086    }
1087    if let Some (group_key) = group_key_b {
1088      update_group (&mut final_proximities, group_key);
1089    }
1090    if let Ok (contact) = collision_final_proximity.try_into() {
1091      log::trace!(object_pair:?, contact:?; "creating contact @ T==1.0");
1092      self.persistent.add_contact (object_pair, contact);
1093    }
1094    // 6. update AABBs in broad pipeline;
1095    // also remove resolved dynamic objects from intermediate pipeline results
1096    for id in dynamic_modified.iter() {
1097      let i = dynamic_ids.binary_search_by_key (&id, |x| x).unwrap();
1098      let key    = id.key();
1099      let index  = key.index();
1100      let object = &objects_dynamic[index];
1101      let aabb_discrete   = object.aabb_dilated();
1102      let aabb_continuous = Aabb3::union (&toi_aabbs[i], &aabb_discrete);
1103      self.broad.update_aabb_dynamic_discrete (aabb_discrete, key);
1104      self.broad.update_aabb_dynamic_continuous (aabb_continuous, key);
1105    }
1106    self.pipeline.remove_resolved_dynamic (&dynamic_modified);
1107    // TODO: update the modified set in-place ?
1108    self.broad.set_modified (dynamic_modified);
1109    // result
1110    let object_id_a = object_id_a.into();
1111    let object_id_b = object_id_b.into();
1112    event::CollisionResolve {
1113      toi, contact,
1114      object_id_a,       object_id_b,
1115      impulse_normal_a,  impulse_normal_b,
1116      impulse_tangent_a, impulse_tangent_b,
1117      pseudo_impulse_a,  pseudo_impulse_b
1118    }
1119  }
1120
1121  /// Detects AABB overlap pairs of static/dynamic and dynamic/dynamic objects.
1122  ///
1123  /// Excludes Nocollide pairs that were already detected but does not filter
1124  /// object pairs that are already in persistent contacts.
1125  #[inline]
1126  fn broad_overlap_pairs_continuous (&mut self) {
1127    self.broad.overlap_pairs_continuous (
1128      &mut self.pipeline.broad_overlap_pairs, self.pipeline.detect_resolve_iter);
1129    // exclude nocollide pairs that were already detected
1130    self.pipeline.broad_overlap_pairs.retain (
1131      |pair|{
1132        let (id_a, id_b) = (*pair).into();
1133        let id_a = object::Id::from (id_a);
1134        let id_b = object::Id::from (id_b);
1135        for event::Overlap { object_id_a, object_id_b } in
1136          self.pipeline.overlaps.iter()
1137        {
1138          if object_id_a == &id_a && object_id_b == &id_b {
1139            return false
1140          }
1141        }
1142        true
1143      }
1144    );
1145    log::trace!(overlap_pairs:?=self.pipeline.broad_overlap_pairs;
1146      "broad overlap pairs");
1147  }
1148
1149  /// Collects swept AABB TOIs from broad overlap pairs.
1150  ///
1151  /// Filters pairs that are already in persistent contact.
1152  ///
1153  /// Note that the algorithm will aim for a distance of `0.5 *
1154  /// CONTACT_DISTANCE` at each TOI.
1155  fn mid_toi_pairs (&mut self, objects_dynamic : &VecMap <object::Dynamic>) {
1156    use sorted_vec::FindOrInsert;
1157    let last_toi = self.pipeline.resolved_collisions.last()
1158      .map_or (0.0, |collision_resolve| *collision_resolve.toi);
1159    'outer: for object_pair in self.pipeline.broad_overlap_pairs.drain (..) {
1160      let (object_id_a, object_id_b) = object_pair.into();
1161      debug_assert_eq!(object_id_b.kind(), object::Kind::Dynamic);
1162      match object_id_a.kind() {
1163        //
1164        //  static v. dynamic
1165        //
1166        object::Kind::Static => {
1167          let id_static     = object::Id::from (object_id_a);
1168          let id_dynamic    = object::Id::from (object_id_b);
1169          let key_static    = id_static.key;
1170          let key_dynamic   = id_dynamic.key;
1171          let _index_static = key_static.index();
1172          let index_dynamic = key_dynamic.index();
1173          if let (Some (static_contact_count), Some (dynamic_group_key)) = (
1174            self.persistent.get_contact_count (object_id_a),
1175            self.persistent.get_group_key (object_id_b)
1176          ) {
1177            debug_assert!(static_contact_count > 0);
1178            let group = self.persistent.get_group (dynamic_group_key).unwrap();
1179            for (pair, _) in group.contacts.iter() {
1180              if *pair == object_pair {
1181                continue 'outer
1182              }
1183            }
1184          }
1185          // should be safe to unwrap
1186          let dynamic_object         = &objects_dynamic[index_dynamic];
1187          let dynamic_velocity       = dynamic_object.derivatives.velocity;
1188          let dynamic_pseudovelocity = self.pseudo_velocities[index_dynamic];
1189          let dynamic_velocity_effective = dynamic_velocity + dynamic_pseudovelocity;
1190          let static_aabb            = self.broad.get_aabb_static (key_static);
1191          let dynamic_aabb_current   = self.broad
1192            .get_aabb_dynamic_discrete (key_dynamic);
1193          let _dynamic_aabb_swept    = self.broad
1194            .get_aabb_dynamic_continuous (key_dynamic);
1195          let dynamic_aabb_previous  = Aabb3::with_minmax (
1196            dynamic_aabb_current.min() - dynamic_velocity_effective,
1197            dynamic_aabb_current.max() - dynamic_velocity_effective);
1198          // NB: div by zero will result in `inf` values
1199          let dynamic_velocity_effective_reciprocal =
1200            dynamic_velocity_effective.recip();
1201          // these values adjust start/end times to avoid getting too close
1202          let t_margin_x =
1203            (0.5 * CONTACT_DISTANCE * dynamic_velocity_effective_reciprocal.x)
1204              .abs();
1205          let t_margin_y =
1206            (0.5 * CONTACT_DISTANCE * dynamic_velocity_effective_reciprocal.y)
1207              .abs();
1208          let t_margin_z =
1209            (0.5 * CONTACT_DISTANCE * dynamic_velocity_effective_reciprocal.z)
1210              .abs();
1211
1212          let (interval_start_x, interval_end_x) = if
1213            dynamic_velocity_effective.x == 0.0
1214          {
1215            debug_assert!(
1216              dynamic_aabb_current.max().0.x > static_aabb.min().0.x &&
1217              static_aabb.max().0.x > dynamic_aabb_current.min().0.x);
1218            debug_assert!(
1219              dynamic_aabb_previous.max().0.x > static_aabb.min().0.x &&
1220              static_aabb.max().0.x > dynamic_aabb_previous.min().0.x);
1221            (f64::NEG_INFINITY, f64::INFINITY)
1222          } else if dynamic_velocity_effective.x > 0.0 {
1223            ( (static_aabb.min().0.x - dynamic_aabb_previous.max().0.x) *
1224                dynamic_velocity_effective_reciprocal.x - t_margin_x,
1225              (static_aabb.max().0.x - dynamic_aabb_previous.min().0.x) *
1226                dynamic_velocity_effective_reciprocal.x + t_margin_x
1227            )
1228          } else {
1229            debug_assert!(dynamic_velocity_effective.x < 0.0);
1230            ( (static_aabb.max().0.x - dynamic_aabb_previous.min().0.x) *
1231                dynamic_velocity_effective_reciprocal.x - t_margin_x,
1232              (static_aabb.min().0.x - dynamic_aabb_previous.max().0.x) *
1233                dynamic_velocity_effective_reciprocal.x + t_margin_x
1234            )
1235          };
1236
1237          let (interval_start_y, interval_end_y) = if
1238            dynamic_velocity_effective.y == 0.0
1239          {
1240            debug_assert!(
1241              dynamic_aabb_current.max().0.y > static_aabb.min().0.y &&
1242              static_aabb.max().0.y > dynamic_aabb_current.min().0.y);
1243            debug_assert!(
1244              dynamic_aabb_previous.max().0.y > static_aabb.min().0.y &&
1245              static_aabb.max().0.y > dynamic_aabb_previous.min().0.y);
1246            (f64::NEG_INFINITY, f64::INFINITY)
1247          } else if dynamic_velocity_effective.y > 0.0 {
1248            ( dynamic_velocity_effective_reciprocal.y *
1249                (static_aabb.min().0.y - dynamic_aabb_previous.max().0.y)
1250                - t_margin_y,
1251              dynamic_velocity_effective_reciprocal.y *
1252                (static_aabb.max().0.y - dynamic_aabb_previous.min().0.y)
1253                + t_margin_y
1254            )
1255          } else {
1256            debug_assert!(dynamic_velocity_effective.y < 0.0);
1257            ( dynamic_velocity_effective_reciprocal.y *
1258                (static_aabb.max().0.y - dynamic_aabb_previous.min().0.y)
1259                - t_margin_y,
1260              dynamic_velocity_effective_reciprocal.y *
1261                (static_aabb.min().0.y - dynamic_aabb_previous.max().0.y)
1262                + t_margin_y
1263            )
1264          };
1265
1266          let (interval_start_z, interval_end_z) = if
1267            dynamic_velocity_effective.z == 0.0
1268          {
1269            debug_assert!(
1270              dynamic_aabb_current.max().0.z > static_aabb.min().0.z &&
1271              static_aabb.max().0.z > dynamic_aabb_current.min().0.z);
1272            debug_assert!(
1273              dynamic_aabb_previous.max().0.z > static_aabb.min().0.z &&
1274              static_aabb.max().0.z > dynamic_aabb_previous.min().0.z);
1275            (f64::NEG_INFINITY, f64::INFINITY)
1276          } else if dynamic_velocity_effective.z > 0.0 {
1277            ( dynamic_velocity_effective_reciprocal.z *
1278                (static_aabb.min().0.z - dynamic_aabb_previous.max().0.z)
1279                - t_margin_z,
1280              dynamic_velocity_effective_reciprocal.z *
1281                (static_aabb.max().0.z - dynamic_aabb_previous.min().0.z)
1282                + t_margin_z
1283            )
1284          } else {
1285            debug_assert!(dynamic_velocity_effective.z < 0.0);
1286            ( dynamic_velocity_effective_reciprocal.z *
1287                (static_aabb.max().0.z - dynamic_aabb_previous.min().0.z)
1288                - t_margin_z,
1289              dynamic_velocity_effective_reciprocal.z *
1290                (static_aabb.min().0.z - dynamic_aabb_previous.max().0.z)
1291                + t_margin_z
1292            )
1293          };
1294
1295          // TODO: can we write this more clearly?
1296          #[allow(clippy::collapsible_if)]
1297          if let Some ((interval_start, interval_end)) =
1298            if let Some ((interval_start_xy, interval_end_xy)) =
1299              if interval_start_x < interval_end_y &&
1300                 interval_start_y < interval_end_x
1301              {
1302                Some ((
1303                  f64::max (interval_start_x, interval_start_y),
1304                  f64::min (interval_end_x, interval_end_y)
1305                ))
1306              } else {
1307                None
1308              }
1309            {
1310              if interval_start_xy < interval_end_z &&
1311                 interval_start_z  < interval_end_xy
1312              {
1313                Some ((
1314                  f64::max (interval_start_xy, interval_start_z),
1315                  f64::min (interval_end_xy, interval_end_z)
1316                ))
1317              } else {
1318                None
1319              }
1320            } else {
1321              None
1322            }
1323          {
1324            if interval_start < 1.0 && 0.0 < interval_end {
1325              let mid_toi_pair = (
1326                Normalized::clamp (f64::max (interval_start, last_toi)),
1327                Normalized::clamp (interval_end),
1328                object_pair
1329              );
1330              match self.pipeline.mid_toi_pairs
1331                .find_or_insert (Reverse (mid_toi_pair))
1332              {
1333                FindOrInsert::Inserted (_) => {}
1334                FindOrInsert::Found    (_) => unreachable!()
1335              }
1336            }
1337          }
1338        }
1339        //
1340        //  dynamic v. dynamic
1341        //
1342        object::Kind::Dynamic => {
1343          let key_a   = object_id_a.key();
1344          let key_b   = object_id_b.key();
1345          let index_a = key_a.index();
1346          let index_b = key_b.index();
1347          if let (Some (group_key_a), Some (group_key_b)) = (
1348            self.persistent.get_group_key (object_id_a),
1349            self.persistent.get_group_key (object_id_b)
1350          ) && group_key_a == group_key_b {
1351            let group = self.persistent.get_group (group_key_a).unwrap();
1352            for (pair, _) in group.contacts.iter() {
1353              if *pair == object_pair {
1354                continue 'outer
1355              }
1356            }
1357          }
1358          // should be safe to unwrap: objects should not be destroyed during
1359          // collision detection
1360          let object_a          = &objects_dynamic[index_a];
1361          let object_b          = &objects_dynamic[index_b];
1362          let velocity_a        = object_a.derivatives.velocity;
1363          let velocity_b        = object_b.derivatives.velocity;
1364          let pseudovelocity_a  = self.pseudo_velocities[key_a.index()];
1365          let pseudovelocity_b  = self.pseudo_velocities[key_b.index()];
1366          let velocity_effective_a = velocity_a + pseudovelocity_a;
1367          let velocity_effective_b = velocity_b + pseudovelocity_b;
1368          let velocity_effective_relative =
1369            velocity_effective_a - velocity_effective_b;
1370          let velocity_effective_relative_reciprocal =
1371            velocity_effective_relative.recip();
1372          let aabb_current_a    = self.broad.get_aabb_dynamic_discrete   (key_a);
1373          let aabb_current_b    = self.broad.get_aabb_dynamic_discrete   (key_b);
1374          let _aabb_swept_a     = self.broad.get_aabb_dynamic_continuous (key_a);
1375          let _aabb_swept_b     = self.broad.get_aabb_dynamic_continuous (key_b);
1376          let aabb_previous_a   = Aabb3::with_minmax (
1377            aabb_current_a.min() - velocity_effective_a,
1378            aabb_current_a.max() - velocity_effective_a);
1379          let aabb_previous_b   = Aabb3::with_minmax (
1380            aabb_current_b.min() - velocity_effective_b,
1381            aabb_current_b.max() - velocity_effective_b);
1382
1383          // compute overlap time start/end interval for the given axis
1384          let overlap_interval_axis = |axis : Axis3| {
1385            let axis_index = axis as usize;
1386            // div by zero will result in `inf` values; t_margin adjusts
1387            // start/end times to avoid getting too close
1388            let t_margin = (0.5 * CONTACT_DISTANCE *
1389              velocity_effective_relative_reciprocal[axis_index]).abs();
1390            let velocity_relative = velocity_effective_relative[axis_index];
1391            let velocity_relative_reciprocal =
1392              velocity_effective_relative_reciprocal[axis_index];
1393            let prev_a_min = aabb_previous_a.min().0[axis_index];
1394            let prev_a_max = aabb_previous_a.max().0[axis_index];
1395            let prev_b_min = aabb_previous_b.min().0[axis_index];
1396            let prev_b_max = aabb_previous_b.max().0[axis_index];
1397            if velocity_relative == 0.0 {
1398              // NOTE: even though the relative velocity is zero, the previous
1399              // AABBs may not be overlapping due to rounding errors; we will
1400              // still consider the begin/end time to be infinite
1401              geometry::Interval::with_minmax (f64::NEG_INFINITY, f64::INFINITY)
1402            } else if velocity_relative > 0.0 {
1403              geometry::Interval::with_minmax (
1404                (prev_b_min - prev_a_max) * velocity_relative_reciprocal -
1405                  t_margin,
1406                (prev_b_max - prev_a_min) * velocity_relative_reciprocal +
1407                  t_margin
1408              )
1409            } else {
1410              debug_assert!(velocity_relative < 0.0);
1411              geometry::Interval::with_minmax (
1412                (prev_b_max - prev_a_min) * velocity_relative_reciprocal -
1413                  t_margin,
1414                (prev_b_min - prev_a_max) * velocity_relative_reciprocal +
1415                  t_margin
1416              )
1417            }
1418          };
1419          let overlap_interval_x = overlap_interval_axis (Axis3::X);
1420          let overlap_interval_y = overlap_interval_axis (Axis3::Y);
1421          let overlap_interval_z = overlap_interval_axis (Axis3::Z);
1422
1423          if let Some (interval) = overlap_interval_x
1424            .intersection (overlap_interval_y).and_then (|overlap_interval_xy|
1425              overlap_interval_xy.intersection (overlap_interval_z))
1426            && interval.min() < 1.0 && 0.0 < interval.max()
1427          {
1428            let mid_toi_pair = (
1429              Normalized::clamp (f64::max (interval.min(), last_toi)),
1430              Normalized::clamp (interval.max()),
1431              object_pair
1432            );
1433            match self.pipeline.mid_toi_pairs .find_or_insert (Reverse (mid_toi_pair)) {
1434              FindOrInsert::Inserted (_) => {}
1435              FindOrInsert::Found    (_) => unreachable!()
1436            }
1437          }
1438        }
1439        _ => unreachable!()
1440      }
1441    }
1442
1443    log::trace!(mid_toi_pairs:?=self.pipeline.mid_toi_pairs; "mid TOI pairs");
1444
1445    self.pipeline.broad_overlap_pairs.clear();
1446  }
1447
1448  /// Returns narrow TOI pairs.
1449  ///
1450  /// Note that the algorithm will aim for a distance of `0.5 *
1451  /// CONTACT_DISTANCE` at each TOI.
1452  fn narrow_toi_contacts (&mut self,
1453    objects_static  : &VecMap <object::Static>,
1454    objects_dynamic : &VecMap <object::Dynamic>
1455  ) {
1456    use sorted_vec::FindOrInsert;
1457
1458    let narrow_toi_contacts = &mut self.pipeline.narrow_toi_contacts;
1459    while let Some (mid_toi_pair) = self.pipeline.mid_toi_pairs.last().cloned() {
1460      let earliest_toi_contact = if !narrow_toi_contacts.is_empty() {
1461        narrow_toi_contacts[0].0.0
1462      } else {
1463        Normalized::clamp (1.0)
1464      };
1465
1466      if mid_toi_pair.0.0 < earliest_toi_contact {
1467        let (mid_toi_start, _mid_toi_end, object_pair) =
1468          mid_toi_pair.0;
1469        let (object_id_a, object_id_b) = object_pair.into();
1470        let kind_a  = object_id_a.kind();
1471        let kind_b  = object_id_b.kind();
1472        match (kind_a, kind_b) {
1473          (object::Kind::Static, object::Kind::Dynamic)  => {
1474            let key_static    = object_id_a.key();
1475            let key_dynamic   = object_id_b.key();
1476            let index_static  = key_static.index();
1477            let index_dynamic = key_dynamic.index();
1478            // safe to unwrap
1479            let mut dynamic_object = objects_dynamic[index_dynamic].clone();
1480            let dynamic_velocity   = dynamic_object.derivatives.velocity;
1481            let dynamic_pseudovelocity = self.pseudo_velocities[index_dynamic];
1482            let dynamic_velocity_effective =
1483              dynamic_velocity - dynamic_pseudovelocity;
1484            let static_object      = objects_static[index_static].clone();
1485            let mut narrow_toi     = *mid_toi_start;
1486            // rewind object to the narrow_toi: the object is currently at
1487            // t==1.0, so we are lerping a negative value
1488            lerp_object (&mut dynamic_object, dynamic_pseudovelocity,
1489              -1.0 + narrow_toi);
1490            let mut t_remaining    = 1.0 - narrow_toi;
1491            #[allow(unused_variables)]
1492            let mut iter           = 0;
1493            loop {
1494              #[cfg(debug_assertions)]
1495              if self.narrow_toi_max_iter_count < iter {
1496                self.narrow_toi_max_iter_count = iter;
1497              }
1498              let proximity = Proximity::query (&static_object, &dynamic_object);
1499              if cfg!(debug_assertions) &&
1500                dynamic_object.collidable && static_object.collidable
1501              {
1502                debug_assert!(proximity.distance >= 0.0);
1503              }
1504              let dynamic_velocity_effective_normal =
1505                dynamic_velocity_effective.dot (-*proximity.normal);
1506              if dynamic_velocity_effective_normal > 0.0 &&
1507                dynamic_object.collidable && static_object.collidable
1508              {
1509                // no collision: separating velocity zero or positive
1510                log::trace!(
1511                  t=narrow_toi, velocity=dynamic_velocity_effective_normal;
1512                  "collision rejected, separating velocity");
1513                break
1514              } else if proximity.distance < CONTACT_DISTANCE {
1515                debug_assert!(dynamic_velocity_effective_normal <= 0.0 ||
1516                  !dynamic_object.collidable || !static_object.collidable);
1517                // collision: toi contact
1518                log::trace!(t=narrow_toi, proximity:?; "collision detected");
1519                let contact     = Contact { constraint: proximity.into() };
1520                let restitution = dynamic_object.material.restitution *
1521                  static_object.material.restitution;
1522                let collision   = contact::Colliding { contact, restitution };
1523                let narrow_toi_contact = Reverse ((
1524                  Normalized::noisy (narrow_toi), object_pair, collision
1525                ));
1526                match narrow_toi_contacts.find_or_insert (narrow_toi_contact) {
1527                  FindOrInsert::Inserted (_) => {}
1528                  FindOrInsert::Found    (_) => unreachable!()
1529                }
1530                break
1531              } else if dynamic_velocity_effective_normal == 0.0 {
1532                log::trace!(
1533                  t=narrow_toi, velocity=dynamic_velocity_effective_normal;
1534                  "collision rejected, zero relative velocity and objects not \
1535                  in contact");
1536                break
1537              }
1538              let dynamic_velocity_effective_normal_reciprocal =
1539                1.0 / dynamic_velocity_effective_normal;
1540              let toi_axis = narrow_toi +
1541                (proximity.distance - 0.5 * CONTACT_DISTANCE) *
1542                (-dynamic_velocity_effective_normal_reciprocal);
1543              debug_assert!(toi_axis > narrow_toi ||
1544                !dynamic_object.collidable || !static_object.collidable);
1545              if toi_axis > 1.0 || toi_axis < 0.0 {
1546                // no collision this step
1547                log::trace!(t=narrow_toi, proximity:?;
1548                  "collision rejected, final proximity");
1549                break
1550              }
1551              let t_advance = toi_axis - narrow_toi;
1552              lerp_object (&mut dynamic_object, dynamic_pseudovelocity, t_advance);
1553              narrow_toi    = toi_axis;
1554              t_remaining   -= t_advance;
1555              debug_assert!(t_remaining > 0.0);
1556              iter += 1;
1557            }
1558          }
1559
1560          (object::Kind::Dynamic, object::Kind::Dynamic) => {
1561            let key_a   = object_id_a.key();
1562            let key_b   = object_id_b.key();
1563            let index_a = key_a.index();
1564            let index_b = key_b.index();
1565             // safe to unwrap: objects should not be destroyed during collision
1566             // detection
1567            let mut object_a     = objects_dynamic[index_a].clone();
1568            let mut object_b     = objects_dynamic[index_b].clone();
1569            let pseudovelocity_a = self.pseudo_velocities[index_a];
1570            let pseudovelocity_b = self.pseudo_velocities[index_b];
1571            let velocity_a       = object_a.derivatives.velocity;
1572            let velocity_b       = object_b.derivatives.velocity;
1573            let velocity_effective_a = velocity_a + pseudovelocity_a;
1574            let velocity_effective_b = velocity_b + pseudovelocity_b;
1575            let velocity_effective_relative =
1576              velocity_effective_a - velocity_effective_b;
1577            let mut narrow_toi   = *mid_toi_start;
1578            let mut t_remaining  = 1.0 - narrow_toi;
1579            lerp_object (&mut object_a, pseudovelocity_a, -1.0 + narrow_toi);
1580            lerp_object (&mut object_b, pseudovelocity_b, -1.0 + narrow_toi);
1581            loop {
1582              debug_assert!(narrow_toi <= 1.0);
1583              debug_assert!(t_remaining >= 0.0);
1584              let proximity = Proximity::query (&object_a, &object_b);
1585              if cfg!(debug_assertions) && object_a.collidable && object_b.collidable {
1586                debug_assert!(proximity.distance >= 0.0);
1587              }
1588              let velocity_effective_relative_normal =
1589                velocity_effective_relative.dot (*proximity.normal);
1590              if velocity_effective_relative_normal > 0.0 &&
1591                object_a.collidable && object_b.collidable
1592              {
1593                // no collision: separating velocity zero or positive
1594                log::trace!(
1595                  t=narrow_toi, velocity=velocity_effective_relative_normal;
1596                  "collision rejected, separating velocity");
1597                break
1598              } else if proximity.distance < CONTACT_DISTANCE {
1599                debug_assert!(velocity_effective_relative_normal <= 0.0 ||
1600                  !object_a.collidable || !object_b.collidable);
1601                // collision: toi contact
1602                log::trace!(t=narrow_toi, proximity:?; "collision detected");
1603                let contact     = Contact { constraint: proximity.into() };
1604                let restitution =
1605                  object_a.material.restitution * object_b.material.restitution;
1606                let collision   = contact::Colliding { contact, restitution };
1607                let narrow_toi_contact = Reverse ((
1608                  Normalized::noisy (narrow_toi), object_pair, collision
1609                ));
1610                match narrow_toi_contacts.find_or_insert (narrow_toi_contact) {
1611                  FindOrInsert::Inserted (_) => {}
1612                  FindOrInsert::Found    (_) => unreachable!()
1613                }
1614                break
1615              } else if velocity_effective_relative_normal == 0.0 {
1616                log::trace!(
1617                  t=narrow_toi, velocity=velocity_effective_relative_normal;
1618                  "collision rejected, zero relative velocity and objects not \
1619                  in contact");
1620                break
1621              }
1622              let velocity_effective_relative_normal_reciprocal =
1623                1.0 / velocity_effective_relative_normal;
1624              let toi_axis = narrow_toi +
1625                (proximity.distance - 0.5 * CONTACT_DISTANCE) *
1626                (-velocity_effective_relative_normal_reciprocal);
1627              debug_assert!(toi_axis > narrow_toi ||
1628                !object_a.collidable || !object_b.collidable);
1629              if toi_axis > 1.0 || toi_axis < 0.0 {
1630                // no collision this step
1631                log::trace!(t=narrow_toi, proximity:?;
1632                  "collision rejected, final proximity");
1633                break
1634              }
1635              let t_advance = toi_axis - narrow_toi;
1636              lerp_object (&mut object_a, pseudovelocity_a, t_advance);
1637              lerp_object (&mut object_b, pseudovelocity_b, t_advance);
1638              narrow_toi    = toi_axis;
1639              t_remaining   -= t_advance;
1640            }
1641          }
1642          _ => unreachable!()
1643        }
1644        self.pipeline.mid_toi_pairs.pop();
1645      } else { break }
1646    }
1647    #[cfg(debug_assertions)]
1648    log::debug!(max_iter_count=self.narrow_toi_max_iter_count;
1649      "narrow TOI max iters");
1650    log::trace!(contacts:?=narrow_toi_contacts; "narrow TOI contacts");
1651  }
1652
1653  #[inline]
1654  fn add_object_static (&mut self,
1655    object     : &object::Static,
1656    object_key : object::Key,
1657  ) {
1658    self.broad.add_object_static (object.aabb_dilated(), object_key);
1659  }
1660
1661  #[inline]
1662  fn add_object_dynamic (&mut self,
1663    object     : &object::Dynamic,
1664    object_key : object::Key
1665  ) {
1666    let aabb_discrete   = object.aabb_dilated();
1667    let aabb_continuous = Aabb3::with_minmax (
1668      aabb_discrete.min() - object.derivatives.velocity,
1669      aabb_discrete.max() - object.derivatives.velocity);
1670    // NB: this swept AABB should not be seen by the next continuous collision
1671    // detection/resolution loop since `begin_step()` will re-calculate for the
1672    // next frame
1673    self.broad.add_object_dynamic (aabb_discrete, aabb_continuous, object_key);
1674    assert!(self.pseudo_velocities
1675      .insert (object_key.index(), Vector3::zero()).is_none());
1676  }
1677
1678}
1679
1680impl Pipeline {
1681  #[inline]
1682  pub fn is_empty (&self) -> bool {
1683    self.broad_overlap_pairs.is_empty() &&
1684    self.mid_toi_pairs.is_empty() &&
1685    self.narrow_toi_contacts.is_empty() &&
1686    self.resolved_collisions.is_empty() &&
1687    self.overlaps.is_empty()
1688  }
1689
1690  /// After a collision is resolved, all modified (dynamic) objects should be
1691  /// removed from intermediate results in the pipeline:
1692  ///
1693  /// - `mid_toi_pairs`
1694  /// - `narrow_toi_contacts`
1695  ///
1696  /// These objects will be checked for overlaps in the broad phase of the next
1697  /// detect/resolve loop iteration.
1698  // TODO: because we are only removing resolved dynamic objects and the
1699  // resolved list is sorted, we could be slicing the resolved set to only
1700  // contain the dynamic ids and exclude the static ids from binary search
1701  #[inline]
1702  pub fn remove_resolved_dynamic (&mut self, resolved : &SortedSet <InternalId>) {
1703    debug_assert!(self.broad_overlap_pairs.is_empty());
1704
1705    let (mut i, mut len);
1706
1707    // remove intermediate mid TOI pairs
1708    i   = 0;
1709    len = self.mid_toi_pairs.len();
1710    while i < len {
1711      let (_, _, object_pair) = self.mid_toi_pairs[i].0;
1712      let (id_a, id_b) = object_pair.into();
1713      if id_a.kind() == object::Kind::Dynamic && resolved.binary_search (&id_a).is_ok() {
1714        self.mid_toi_pairs.remove_index (i);
1715        len -= 1;
1716        continue
1717      }
1718      debug_assert_eq!(id_b.kind(), object::Kind::Dynamic);
1719      if resolved.binary_search (&id_b).is_ok() {
1720        self.mid_toi_pairs.remove_index (i);
1721        len -= 1;
1722        continue
1723      }
1724      i += 1;
1725    }
1726
1727    // remove intermediate narrow TOI contacts
1728    i   = 0;
1729    len = self.narrow_toi_contacts.len();
1730    while i < len {
1731      let (_, object_pair, _) = self.narrow_toi_contacts[i].clone().0;
1732      let (id_a, id_b) = object_pair.into();
1733      if id_a.kind() == object::Kind::Dynamic && resolved.binary_search (&id_a).is_ok() {
1734        self.narrow_toi_contacts.remove_index (i);
1735        len -= 1;
1736        continue
1737      }
1738      debug_assert_eq!(id_b.kind(), object::Kind::Dynamic);
1739      if resolved.binary_search (&id_b).is_ok() {
1740        self.narrow_toi_contacts.remove_index (i);
1741        len -= 1;
1742        continue
1743      }
1744      i += 1;
1745    }
1746  }
1747}
1748
1749impl ObjectPair {
1750  /// Create a new object pair.
1751  ///
1752  /// One object must be dynamic. Internally the object IDs will be stored in
1753  /// sorted order, and a static object will always be first.
1754  pub fn new (id_a : InternalId, id_b : InternalId) -> Self {
1755    debug_assert!(id_a.kind() == object::Kind::Dynamic ||
1756      id_b.kind() == object::Kind::Dynamic);
1757    if id_a < id_b {
1758      ObjectPair (id_a, id_b)
1759    } else {
1760      debug_assert!(id_a > id_b, "overlap ids should not be identical");
1761      ObjectPair (id_b, id_a)
1762    }
1763  }
1764}
1765
1766impl From <(InternalId, InternalId)> for ObjectPair {
1767  fn from ((id_a, id_b) : (InternalId, InternalId)) -> Self {
1768    ObjectPair::new (id_a, id_b)
1769  }
1770}
1771
1772impl InternalId {
1773  #[inline]
1774  pub fn new_static (key : object::Key) -> Self {
1775    debug_assert!(key.value() < OBJECT_KEY_MAX);
1776    InternalId (key.value())
1777  }
1778  #[inline]
1779  pub fn new_dynamic (key : object::Key) -> Self {
1780    debug_assert!(key.value() < OBJECT_KEY_MAX);
1781    InternalId (key.value() | INTERNAL_ID_DYNAMIC_BIT)
1782  }
1783  #[inline]
1784  pub fn kind (&self) -> object::Kind {
1785    match self.0 & INTERNAL_ID_DYNAMIC_BIT > 0 {
1786      true  => object::Kind::Dynamic,
1787      false => object::Kind::Static
1788    }
1789  }
1790  #[inline]
1791  pub fn key (&self) -> object::Key {
1792    object::Key::from (self.0 & !INTERNAL_ID_DYNAMIC_BIT)
1793  }
1794}
1795impl From <InternalId> for object::Id {
1796  fn from (id : InternalId) -> Self {
1797    object::Id {
1798      kind: id.kind(),
1799      key:  id.key()
1800    }
1801  }
1802}