avian3d/collision/contact_types/contact_graph.rs
1use crate::data_structures::{
2 graph::{EdgeWeightsMut, NodeIndex, UnGraph},
3 pair_key::PairKey,
4 sparse_secondary_map::SparseSecondaryEntityMap,
5};
6#[expect(unused_imports)]
7use crate::prelude::*;
8use bevy::{platform::collections::HashSet, prelude::*};
9
10use super::{ContactPair, ContactPairFlags};
11
12/// A resource that stores all contact pairs in the physics world
13/// in an [undirected graph](UnGraph).
14///
15/// Contact pairs exist between [colliders](Collider) that have intersecting [AABBs](ColliderAabb),
16/// even if the shapes themselves are not yet touching.
17///
18/// For a simpler API that only provides touching contacts, consider using the [`Collisions`]
19/// system parameter.
20///
21/// # Usage
22///
23/// The following methods can be used for querying collisions:
24///
25/// - [`get`](Self::get) and [`get_mut`](Self::get_mut)
26/// - [`iter`](Self::iter) and [`iter_mut`](Self::iter_mut)
27/// - [`contains`](Self::contains)
28/// - [`collisions_with`](Self::collisions_with) and
29/// [`collisions_with_mut`](Self::collisions_with_mut)
30/// - [`entities_colliding_with`](Self::entities_colliding_with)
31///
32/// For example, to iterate over all collisions with a given entity:
33///
34/// ```
35#[cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
36#[cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
37/// use bevy::prelude::*;
38///
39/// #[derive(Component)]
40/// struct PressurePlate;
41///
42/// fn activate_pressure_plates(mut query: Query<Entity, With<PressurePlate>>, contact_graph: Res<ContactGraph>) {
43/// for pressure_plate in &query {
44/// // Compute the total impulse applied to the pressure plate.
45/// let mut total_impulse = 0.0;
46///
47/// for contact_pair in contact_graph.collisions_with(pressure_plate) {
48/// total_impulse += contact_pair.total_normal_impulse_magnitude();
49/// }
50///
51/// if total_impulse > 5.0 {
52/// println!("Pressure plate activated!");
53/// }
54/// }
55/// }
56/// ```
57///
58/// While mutable access is allowed, contact modification and filtering should typically
59/// be done using [`CollisionHooks`]. See the documentation for more information.
60///
61/// # Warning
62///
63/// For users, this resource is primarily for querying and reading collision data.
64///
65/// Directly adding, modifying, or removing contact pairs using this resource will *not* trigger any collision events,
66/// wake up the entities involved, or perform any other cleanup. Only make structural modifications if you know what you are doing.
67///
68/// For filtering and modifying collisions, consider using [`CollisionHooks`] instead.
69#[derive(Resource, Clone, Debug, Default)]
70pub struct ContactGraph {
71 // TODO: We could have a separate intersection graph for sensors.
72 /// The internal undirected graph where nodes are entities and edges are contact pairs.
73 pub internal: UnGraph<Entity, ContactPair>,
74
75 /// A set of all contact pairs for fast lookup.
76 ///
77 /// The [`PairKey`] is a unique pair of entity indices, sorted in ascending order,
78 /// concatenated into a single `u64` key.
79 ///
80 /// Two entities have a contact pair if they have intersecting AABBs.
81 pub(crate) pair_set: HashSet<PairKey>,
82
83 /// A map from entities to their corresponding node indices in the contact graph.
84 entity_to_node: SparseSecondaryEntityMap<NodeIndex>,
85}
86
87impl ContactGraph {
88 /// Returns the contact pair between two entities.
89 /// If the pair does not exist, `None` is returned.
90 ///
91 /// A contact pair exists between two entities if their [`ColliderAabb`]s intersect.
92 /// Use [`ContactPair::is_touching`] to determine if the actual collider shapes are touching.
93 #[inline]
94 pub fn get(&self, entity1: Entity, entity2: Entity) -> Option<&ContactPair> {
95 let (Some(&index1), Some(&index2)) = (
96 self.entity_to_node.get(entity1),
97 self.entity_to_node.get(entity2),
98 ) else {
99 return None;
100 };
101
102 self.internal
103 .find_edge(index1, index2)
104 .and_then(|edge| self.internal.edge_weight(edge))
105 }
106
107 /// Returns a mutable reference to the contact pair between two entities.
108 /// If the pair does not exist, `None` is returned.
109 ///
110 /// A contact pair exists between two entities if their [`ColliderAabb`]s intersect.
111 /// Use [`ContactPair::is_touching`] to determine if the actual collider shapes are touching.
112 #[inline]
113 pub fn get_mut(&mut self, entity1: Entity, entity2: Entity) -> Option<&mut ContactPair> {
114 let (Some(&index1), Some(&index2)) = (
115 self.entity_to_node.get(entity1),
116 self.entity_to_node.get(entity2),
117 ) else {
118 return None;
119 };
120
121 self.internal
122 .find_edge(index1, index2)
123 .and_then(|edge| self.internal.edge_weight_mut(edge))
124 }
125
126 /// Returns `true` if the given entities have a contact pair.
127 ///
128 /// A contact pair exists between two entities if their [`ColliderAabb`]s intersect,
129 /// even if the shapes themselves are not yet touching.
130 #[inline]
131 pub fn contains(&self, entity1: Entity, entity2: Entity) -> bool {
132 self.contains_key(&PairKey::new(entity1.index(), entity2.index()))
133 }
134
135 /// Returns `true` if the given pair key is in the contact graph.
136 ///
137 /// The pair key should be equivalent to `PairKey::new(entity1.index(), entity2.index())`.
138 ///
139 /// This method can be useful to avoid constructing a new `PairKey` when the key is already known.
140 /// If the key is not available, consider using [`contains`](Self::contains) instead.
141 #[inline]
142 pub fn contains_key(&self, pair_key: &PairKey) -> bool {
143 self.pair_set.contains(pair_key)
144 }
145
146 /// Returns an iterator yielding immutable access to all contact pairs.
147 ///
148 /// A contact pair exists between two entities if their [`ColliderAabb`]s intersect,
149 /// even if the shapes themselves are not yet touching.
150 ///
151 /// If you only want touching contacts, use [`iter_touching`](Self::iter_touching) instead.
152 #[inline]
153 pub fn iter(&self) -> impl Iterator<Item = &ContactPair> {
154 self.internal.all_edge_weights()
155 }
156
157 /// Returns an iterator yielding immutable access to all contact pairs that are currently touching.
158 ///
159 /// This is a subset of [`iter`](Self::iter) that only includes pairs where the colliders are touching.
160 #[inline]
161 pub fn iter_touching(&self) -> impl Iterator<Item = &ContactPair> {
162 self.internal
163 .all_edge_weights()
164 .filter(|contacts| contacts.flags.contains(ContactPairFlags::TOUCHING))
165 }
166
167 /// Returns a iterator yielding mutable access to all contact pairs.
168 ///
169 /// A contact pair exists between two entities if their [`ColliderAabb`]s intersect,
170 /// even if the shapes themselves are not yet touching.
171 ///
172 /// If you only want touching contacts, use [`iter_touching_mut`](Self::iter_touching_mut) instead.
173 #[inline]
174 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut ContactPair> {
175 self.internal.all_edge_weights_mut()
176 }
177
178 /// Returns a iterator yielding mutable access to all contact pairs that are currently touching.
179 ///
180 /// This is a subset of [`iter_mut`](Self::iter_mut) that only includes pairs where the colliders are touching.
181 #[inline]
182 pub fn iter_touching_mut(&mut self) -> impl Iterator<Item = &mut ContactPair> {
183 self.internal
184 .all_edge_weights_mut()
185 .filter(|contacts| contacts.flags.contains(ContactPairFlags::TOUCHING))
186 }
187
188 /// Returns an iterator yielding immutable access to all contact pairs involving the given entity.
189 ///
190 /// A contact pair exists between two entities if their [`ColliderAabb`]s intersect,
191 /// even if the shapes themselves are not yet touching.
192 ///
193 /// Use [`ContactPair::is_touching`](ContactPair::is_touching) to determine if the actual collider shapes are touching.
194 #[inline]
195 pub fn collisions_with(&self, entity: Entity) -> impl Iterator<Item = &ContactPair> {
196 self.entity_to_node
197 .get(entity)
198 .into_iter()
199 .flat_map(move |&index| self.internal.edge_weights(index))
200 }
201
202 /// Returns an iterator yielding mutable access to all contact pairs involving the given entity.
203 ///
204 /// A contact pair exists between two entities if their [`ColliderAabb`]s intersect,
205 /// even if the shapes themselves are not yet touching.
206 ///
207 /// Use [`ContactPair::is_touching`](ContactPair::is_touching) to determine if the actual collider shapes are touching.
208 #[inline]
209 pub fn collisions_with_mut(
210 &mut self,
211 entity: Entity,
212 ) -> impl Iterator<Item = &mut ContactPair> {
213 if let Some(&index) = self.entity_to_node.get(entity) {
214 self.internal.edge_weights_mut(index)
215 } else {
216 EdgeWeightsMut {
217 graph: &mut self.internal,
218 incoming_edge: None,
219 outgoing_edge: None,
220 }
221 }
222 }
223
224 /// Returns an iterator yielding immutable access to all entities that have a contact pair with the given entity.
225 ///
226 /// A contact pair exists between two entities if their [`ColliderAabb`]s intersect,
227 /// even if the shapes themselves are not yet touching.
228 #[inline]
229 pub fn entities_colliding_with(&self, entity: Entity) -> impl Iterator<Item = Entity> + '_ {
230 self.entity_to_node
231 .get(entity)
232 .into_iter()
233 .flat_map(move |&index| {
234 self.internal
235 .neighbors(index)
236 .map(|index| *self.internal.node_weight(index).unwrap())
237 })
238 }
239
240 /// Creates a contact pair between two entities.
241 ///
242 /// If a pair with the same entities already exists, this will do nothing.
243 ///
244 /// # Warning
245 ///
246 /// Creating a collision pair with this method will *not* trigger any collision events
247 /// or wake up the entities involved. Only use this method if you know what you are doing.
248 #[inline]
249 pub fn add_pair(&mut self, contacts: ContactPair) {
250 let pair_key = PairKey::new(contacts.collider1.index(), contacts.collider2.index());
251 self.add_pair_with_key(contacts, pair_key);
252 }
253
254 /// Creates a contact pair between two entities with the given pair key.
255 ///
256 /// The key must be equivalent to `PairKey::new(contacts.entity1.index(), contacts.entity2.index())`.
257 ///
258 /// If a pair with the same entities already exists, this will do nothing.
259 ///
260 /// This method can be useful to avoid constructing a new `PairKey` when the key is already known.
261 /// If the key is not available, consider using [`add_pair`](Self::add_pair) instead.
262 ///
263 /// # Warning
264 ///
265 /// Creating a collision pair with this method will *not* trigger any collision events
266 /// or wake up the entities involved. Only use this method if you know what you are doing.
267 #[inline]
268 pub fn add_pair_with_key(&mut self, contacts: ContactPair, pair_key: PairKey) {
269 // Add the pair to the pair set for fast lookup.
270 if !self.pair_set.insert(pair_key) {
271 // The pair already exists.
272 return;
273 }
274
275 // Get the indices of the entities in the graph.
276 let index1 = self
277 .entity_to_node
278 .get_or_insert_with(contacts.collider1, || {
279 self.internal.add_node(contacts.collider1)
280 });
281 let index2 = self
282 .entity_to_node
283 .get_or_insert_with(contacts.collider2, || {
284 self.internal.add_node(contacts.collider2)
285 });
286
287 // Add the edge to the graph.
288 self.internal.add_edge(index1, index2, contacts);
289 }
290
291 /// Inserts a contact pair between two entities.
292 ///
293 /// If a pair with the same entities already exists, it will be overwritten.
294 ///
295 /// # Warning
296 ///
297 /// Inserting a collision pair with this method will *not* trigger any collision events
298 /// or wake up the entities involved. Only use this method if you know what you are doing.
299 #[inline]
300 pub fn insert_pair(&mut self, contacts: ContactPair) {
301 let pair_key = PairKey::new(contacts.collider1.index(), contacts.collider2.index());
302 self.insert_pair_with_key(contacts, pair_key);
303 }
304
305 /// Inserts a contact pair between two entities with the given pair key.
306 ///
307 /// The key must be equivalent to `PairKey::new(contacts.entity1.index(), contacts.entity2.index())`.
308 ///
309 /// If a pair with the same entities already exists, it will be overwritten.
310 ///
311 /// This method can be useful to avoid constructing a new `PairKey` when the key is already known.
312 /// If the key is not available, consider using [`insert_pair`](Self::insert_pair) instead.
313 ///
314 /// # Warning
315 ///
316 /// Inserting a collision pair with this method will *not* trigger any collision events
317 /// or wake up the entities involved. Only use this method if you know what you are doing.
318 #[inline]
319 pub fn insert_pair_with_key(&mut self, contacts: ContactPair, pair_key: PairKey) {
320 // Add the pair to the pair set for fast lookup.
321 self.pair_set.insert(pair_key);
322
323 // Get the indices of the entities in the graph.
324 let index1 = self
325 .entity_to_node
326 .get_or_insert_with(contacts.collider1, || {
327 self.internal.add_node(contacts.collider1)
328 });
329 let index2 = self
330 .entity_to_node
331 .get_or_insert_with(contacts.collider2, || {
332 self.internal.add_node(contacts.collider2)
333 });
334
335 // Update the edge in the graph.
336 self.internal.update_edge(index1, index2, contacts);
337 }
338
339 /// Removes a contact pair between two entites and returns its value.
340 ///
341 /// # Warning
342 ///
343 /// Removing a collision pair with this method will *not* trigger any collision events
344 /// or wake up the entities involved. Only use this method if you know what you are doing.
345 ///
346 /// For filtering and modifying collisions, consider using [`CollisionHooks`] instead.
347 #[inline]
348 pub fn remove_pair(&mut self, entity1: Entity, entity2: Entity) -> Option<ContactPair> {
349 let (Some(&index1), Some(&index2)) = (
350 self.entity_to_node.get(entity1),
351 self.entity_to_node.get(entity2),
352 ) else {
353 return None;
354 };
355
356 // Remove the pair from the pair set.
357 self.pair_set
358 .remove(&PairKey::new(entity1.index(), entity2.index()));
359
360 // Remove the edge from the graph.
361 self.internal
362 .find_edge(index1, index2)
363 .and_then(|edge| self.internal.remove_edge(edge))
364 }
365
366 /// Removes the collider of the given entity from the contact graph,
367 /// calling the given callback for each contact pair that is removed in the process.
368 ///
369 /// # Warning
370 ///
371 /// Removing a collider with this method will *not* trigger any collision events
372 /// or wake up the entities involved. Only use this method if you know what you are doing.
373 #[inline]
374 pub fn remove_collider_with<F>(&mut self, entity: Entity, mut pair_callback: F)
375 where
376 F: FnMut(ContactPair),
377 {
378 // Remove the entity from the entity-to-node mapping,
379 // and get the index of the node in the graph.
380 let Some(index) = self.entity_to_node.remove(entity) else {
381 return;
382 };
383
384 // Remove the entity from the graph.
385 self.internal.remove_node_with(index, |contacts| {
386 let pair_key = PairKey::new(contacts.collider1.index(), contacts.collider2.index());
387
388 pair_callback(contacts);
389
390 // Remove the pair from the pair set.
391 self.pair_set.remove(&pair_key);
392 });
393
394 // Removing the node swapped the last node to its place,
395 // so we need to remap the entity-to-node mapping of the swapped node.
396 if let Some(swapped) = self.internal.node_weight(index).copied() {
397 let swapped_index = self
398 .entity_to_node
399 .get_mut(swapped)
400 // This should never panic.
401 .expect("swapped entity has no entity-to-node mapping");
402 *swapped_index = index;
403 }
404 }
405}