ncollide3d/query/contact/
contact_manifold.rs

1use crate::math::Point;
2use crate::query::ContactPreprocessor;
3use crate::query::{Contact, ContactKinematic, TrackedContact};
4use crate::shape::FeatureId;
5use na::{self, RealField};
6use slab::Slab;
7use std::collections::{hash_map::Entry, HashMap};
8
9/// The technique used for contact tracking.
10#[derive(Copy, Clone, Debug, PartialEq, Eq)]
11pub enum ContactTrackingMode<N: RealField + Copy> {
12    /// Contact tracking using features.
13    /// Two contacts are considered the same if they are on the same features.
14    FeatureBased,
15    /// Contact tracking using distances.
16    /// Two contacts are considered the same if they are closer than the given threshold.
17    DistanceBased(N),
18}
19
20#[derive(Clone, Debug)]
21enum ContactCache<N: RealField + Copy> {
22    FeatureBased(HashMap<(FeatureId, FeatureId), usize>),
23    DistanceBased(Vec<(Point<N>, usize)>, N),
24}
25
26/// A contact manifold.
27///
28/// A contact manifold is a set of contacts between two shapes.
29/// If the shapes are convex, then the convex hull of those contacts are often interpreted as surface.
30/// This structure is responsible for matching new contacts with old ones in order to perform an
31/// approximate tracking of the contact points.
32#[derive(Clone, Debug)]
33pub struct ContactManifold<N: RealField + Copy> {
34    ncontacts: usize,
35    persistence: usize,
36    deepest: usize,
37    contacts: Slab<(TrackedContact<N>, usize)>,
38    cache: ContactCache<N>,
39}
40
41impl<N: RealField + Copy> ContactManifold<N> {
42    /// Initializes a contact manifold without any contact.
43    ///
44    /// The default contact tracking mode is set to `ContactTrackingMode::DistanceBased(0.02)`.
45    pub fn new() -> Self {
46        ContactManifold {
47            ncontacts: 0,
48            deepest: 0,
49            persistence: 1,
50            contacts: Slab::new(),
51            cache: ContactCache::DistanceBased(Vec::new(), na::convert(0.02)),
52        }
53    }
54
55    /// The number of contacts contained by this manifold.
56    pub fn len(&self) -> usize {
57        self.ncontacts
58    }
59
60    /// All the contact tracked by this manifold.
61    pub fn contacts(&self) -> impl Iterator<Item = &TrackedContact<N>> {
62        let persistence = self.persistence;
63        self.contacts
64            .iter()
65            .filter_map(move |(_, c)| if c.1 == persistence { Some(&c.0) } else { None })
66    }
67
68    /// Mutable reference to all the contact tracked by this manifold.
69    pub fn contacts_mut(&mut self) -> impl Iterator<Item = &mut TrackedContact<N>> {
70        let persistence = self.persistence;
71        self.contacts.iter_mut().filter_map(move |(_, c)| {
72            if c.1 == persistence {
73                Some(&mut c.0)
74            } else {
75                None
76            }
77        })
78    }
79
80    /// The contact of this manifold with the deepest penetration depth.
81    pub fn deepest_contact(&self) -> Option<&TrackedContact<N>> {
82        if self.len() != 0 {
83            Some(&self.contacts[self.deepest].0)
84        } else {
85            None
86        }
87    }
88
89    /// Empty the manifold as well as its cache.
90    pub fn clear(&mut self) {
91        match &mut self.cache {
92            ContactCache::FeatureBased(h) => h.clear(),
93            ContactCache::DistanceBased(v, _) => v.clear(),
94        }
95        self.contacts.clear();
96        self.ncontacts = 0;
97    }
98
99    /// Gets the technique currently used for tracking contacts.
100    pub fn tracking_mode(&self) -> ContactTrackingMode<N> {
101        match self.cache {
102            ContactCache::FeatureBased(_) => ContactTrackingMode::FeatureBased,
103            ContactCache::DistanceBased(_, threshold) => {
104                ContactTrackingMode::DistanceBased(threshold)
105            }
106        }
107    }
108
109    /// Sets the technique used for tracking contacts.
110    ///
111    /// If the selected method is different from the current one,
112    /// the current contact cache is cleared.
113    pub fn set_tracking_mode(&mut self, mode: ContactTrackingMode<N>) {
114        match mode {
115            ContactTrackingMode::FeatureBased => {
116                if let ContactCache::FeatureBased(_) = self.cache {
117                    // Nothing to do.
118                } else {
119                    self.cache = ContactCache::FeatureBased(HashMap::new())
120                }
121            }
122            ContactTrackingMode::DistanceBased(new_threshold) => {
123                if let ContactCache::DistanceBased(_, threshold) = &mut self.cache {
124                    *threshold = new_threshold;
125                    return;
126                }
127
128                self.cache = ContactCache::DistanceBased(Vec::new(), new_threshold)
129            }
130        }
131    }
132
133    /// Save the contacts to a cache and empty the manifold.
134    pub fn save_cache_and_clear(&mut self) {
135        match &mut self.cache {
136            ContactCache::DistanceBased(cache, _) => {
137                let ctcts = &self.contacts;
138                cache.retain(|c| ctcts[c.1].1 != 0);
139            }
140            ContactCache::FeatureBased(cache) => {
141                let ctcts = &self.contacts;
142                cache.retain(|_k, v| ctcts[*v].1 != 0);
143            }
144        }
145
146        self.deepest = 0;
147        self.ncontacts = 0;
148        self.contacts.retain(|_i, c| {
149            if c.1 == 0 {
150                false
151            } else {
152                c.1 -= 1;
153                true
154            }
155        });
156    }
157
158    // FIXME: the method taking a preprocessor should be different?
159    /// Add a new contact to the manifold.
160    ///
161    /// The manifold will attempt to match this contact with another one
162    /// previously added and added to the cache by the last call to
163    /// `save_cache_and_clear`. The matching is done by spacial proximity, i.e.,
164    /// two contacts that are sufficiently close will be given the same identifier.
165    pub fn push(
166        &mut self,
167        mut contact: Contact<N>,
168        mut kinematic: ContactKinematic<N>,
169        tracking_pt: Point<N>,
170        preprocessor1: Option<&dyn ContactPreprocessor<N>>,
171        preprocessor2: Option<&dyn ContactPreprocessor<N>>,
172    ) -> bool {
173        if let Some(pp) = preprocessor1 {
174            if !pp.process_contact(&mut contact, &mut kinematic, true) {
175                return false;
176            }
177        }
178
179        if let Some(pp) = preprocessor2 {
180            if !pp.process_contact(&mut contact, &mut kinematic, false) {
181                return false;
182            }
183        }
184
185        let is_deepest =
186            self.ncontacts == 0 || contact.depth > self.contacts[self.deepest].0.contact.depth;
187
188        match &mut self.cache {
189            ContactCache::DistanceBased(cache, threshold) => {
190                let mut closest = cache.len();
191                let mut closest_dist: N = *threshold * *threshold;
192
193                for (i, cached) in cache.iter().enumerate() {
194                    let dist = na::distance_squared(&tracking_pt, &cached.0);
195                    if dist < closest_dist {
196                        closest_dist = dist;
197                        closest = i;
198                    }
199                }
200
201                if closest == cache.len() {
202                    let tracked = TrackedContact::new(contact, kinematic);
203                    let i = self.contacts.insert((tracked, self.persistence));
204                    cache.push((tracking_pt, i));
205                    self.ncontacts += 1;
206
207                    if is_deepest {
208                        self.deepest = i;
209                    }
210
211                    false
212                } else {
213                    let contact_i = cache[closest].1;
214                    if is_deepest {
215                        self.deepest = contact_i;
216                    }
217
218                    let c = &mut self.contacts[contact_i];
219
220                    if c.1 == self.persistence {
221                        if contact.depth <= c.0.contact.depth {
222                            // Keep the contact already in cache because it is deeper.
223                            return true;
224                        }
225                    } else {
226                        self.ncontacts += 1;
227                        c.1 = self.persistence;
228                    }
229
230                    c.0.contact = contact;
231                    c.0.kinematic = kinematic;
232                    cache[closest].0 = tracking_pt;
233
234                    true
235                }
236            }
237            ContactCache::FeatureBased(cache) => {
238                match cache.entry((kinematic.feature1(), kinematic.feature2())) {
239                    Entry::Vacant(e) => {
240                        let tracked = TrackedContact::new(contact, kinematic);
241                        let i = self.contacts.insert((tracked, self.persistence));
242                        let _ = e.insert(i);
243                        self.ncontacts += 1;
244
245                        if is_deepest {
246                            self.deepest = i;
247                        }
248
249                        false
250                    }
251                    Entry::Occupied(e) => {
252                        if is_deepest {
253                            self.deepest = *e.get();
254                        }
255
256                        let c = &mut self.contacts[*e.get()];
257
258                        if c.1 == self.persistence {
259                            if contact.depth <= c.0.contact.depth {
260                                // Keep the contact already in cache because it is deeper.
261                                return true;
262                            }
263                        } else {
264                            self.ncontacts += 1;
265                            c.1 = self.persistence;
266                        }
267
268                        c.0.contact = contact;
269                        c.0.kinematic = kinematic;
270
271                        true
272                    }
273                }
274            }
275        }
276    }
277}