rapier2d/dynamics/joint/impulse_joint/impulse_joint_set.rs
1use parry::utils::hashset::HashSet;
2
3use super::ImpulseJoint;
4use crate::geometry::{InteractionGraph, RigidBodyGraphIndex, TemporaryInteractionIndex};
5
6use crate::data::Coarena;
7use crate::data::arena::Arena;
8use crate::dynamics::{GenericJoint, IslandManager, RigidBodyHandle, RigidBodySet};
9
10/// The unique identifier of a joint added to the joint set.
11/// The unique identifier of a collider added to a collider set.
12#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
13#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
14#[repr(transparent)]
15pub struct ImpulseJointHandle(pub crate::data::arena::Index);
16
17impl ImpulseJointHandle {
18 /// Converts this handle into its (index, generation) components.
19 pub fn into_raw_parts(self) -> (u32, u32) {
20 self.0.into_raw_parts()
21 }
22
23 /// Reconstructs an handle from its (index, generation) components.
24 pub fn from_raw_parts(id: u32, generation: u32) -> Self {
25 Self(crate::data::arena::Index::from_raw_parts(id, generation))
26 }
27
28 /// An always-invalid joint handle.
29 pub fn invalid() -> Self {
30 Self(crate::data::arena::Index::from_raw_parts(
31 crate::INVALID_U32,
32 crate::INVALID_U32,
33 ))
34 }
35}
36
37pub(crate) type JointIndex = usize;
38pub(crate) type JointGraphEdge = crate::data::graph::Edge<ImpulseJoint>;
39
40#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
41#[derive(Clone, Default, Debug)]
42/// The collection that stores all joints connecting rigid bodies in your physics world.
43///
44/// Joints constrain how two bodies can move relative to each other. This set manages
45/// all joint instances (hinges, sliders, springs, etc.) using handles for safe access.
46///
47/// # Common joint types
48/// - [`FixedJoint`](crate::dynamics::FixedJoint): Weld two bodies together
49/// - [`RevoluteJoint`](crate::dynamics::RevoluteJoint): Hinge (rotation around axis)
50/// - [`PrismaticJoint`](crate::dynamics::PrismaticJoint): Slider (translation along axis)
51/// - [`SpringJoint`](crate::dynamics::SpringJoint): Elastic connection
52/// - [`RopeJoint`](crate::dynamics::RopeJoint): Maximum distance limit
53///
54/// # Example
55/// ```
56/// # use rapier3d::prelude::*;
57/// # let mut bodies = RigidBodySet::new();
58/// # let body1 = bodies.insert(RigidBodyBuilder::dynamic());
59/// # let body2 = bodies.insert(RigidBodyBuilder::dynamic());
60/// let mut joints = ImpulseJointSet::new();
61///
62/// // Create a hinge connecting two bodies
63/// let joint = RevoluteJointBuilder::new(Vector::Y)
64/// .local_anchor1(Vector::new(1.0, 0.0, 0.0))
65/// .local_anchor2(Vector::new(-1.0, 0.0, 0.0))
66/// .build();
67/// let handle = joints.insert(body1, body2, joint, true);
68/// ```
69pub struct ImpulseJointSet {
70 rb_graph_ids: Coarena<RigidBodyGraphIndex>,
71 /// Map joint handles to edge ids on the graph.
72 joint_ids: Arena<TemporaryInteractionIndex>,
73 joint_graph: InteractionGraph<RigidBodyHandle, ImpulseJoint>,
74 /// A set of rigid-body handles to wake-up during the next timestep.
75 pub(crate) to_wake_up: HashSet<RigidBodyHandle>,
76 /// A set of rigid-body pairs to join in the island manager during the next timestep.
77 pub(crate) to_join: HashSet<(RigidBodyHandle, RigidBodyHandle)>,
78}
79
80impl ImpulseJointSet {
81 /// Creates a new empty set of impulse_joints.
82 pub fn new() -> Self {
83 Self {
84 rb_graph_ids: Coarena::new(),
85 joint_ids: Arena::new(),
86 joint_graph: InteractionGraph::new(),
87 to_wake_up: HashSet::default(),
88 to_join: HashSet::default(),
89 }
90 }
91
92 /// Returns how many joints are currently in this collection.
93 pub fn len(&self) -> usize {
94 self.joint_graph.graph.edges.len()
95 }
96
97 /// Returns `true` if there are no joints in this collection.
98 pub fn is_empty(&self) -> bool {
99 self.joint_graph.graph.edges.is_empty()
100 }
101
102 /// Returns the internal graph structure (nodes=bodies, edges=joints).
103 ///
104 /// Advanced usage - most users should use `attached_joints()` instead.
105 pub fn joint_graph(&self) -> &InteractionGraph<RigidBodyHandle, ImpulseJoint> {
106 &self.joint_graph
107 }
108
109 /// Returns all joints connecting two specific bodies.
110 ///
111 /// Usually returns 0 or 1 joint, but multiple joints can connect the same pair.
112 ///
113 /// # Example
114 /// ```
115 /// # use rapier3d::prelude::*;
116 /// # let mut bodies = RigidBodySet::new();
117 /// # let mut joints = ImpulseJointSet::new();
118 /// # let body1 = bodies.insert(RigidBodyBuilder::dynamic());
119 /// # let body2 = bodies.insert(RigidBodyBuilder::dynamic());
120 /// # let joint = RevoluteJointBuilder::new(Vector::Y);
121 /// # joints.insert(body1, body2, joint, true);
122 /// for (handle, joint) in joints.joints_between(body1, body2) {
123 /// println!("Found joint {:?}", handle);
124 /// }
125 /// ```
126 pub fn joints_between(
127 &self,
128 body1: RigidBodyHandle,
129 body2: RigidBodyHandle,
130 ) -> impl Iterator<Item = (ImpulseJointHandle, &ImpulseJoint)> {
131 self.rb_graph_ids
132 .get(body1.0)
133 .zip(self.rb_graph_ids.get(body2.0))
134 .into_iter()
135 .flat_map(move |(id1, id2)| self.joint_graph.interaction_pair(*id1, *id2).into_iter())
136 .map(|inter| (inter.2.handle, inter.2))
137 }
138
139 /// Returns all joints attached to a specific body.
140 ///
141 /// Each result is `(body1, body2, joint_handle, joint)` where one of the bodies
142 /// matches the queried body.
143 ///
144 /// # Example
145 /// ```
146 /// # use rapier3d::prelude::*;
147 /// # let mut bodies = RigidBodySet::new();
148 /// # let mut joints = ImpulseJointSet::new();
149 /// # let body_handle = bodies.insert(RigidBodyBuilder::dynamic());
150 /// # let other_body = bodies.insert(RigidBodyBuilder::dynamic());
151 /// # let joint = RevoluteJointBuilder::new(Vector::Y);
152 /// # joints.insert(body_handle, other_body, joint, true);
153 /// for (b1, b2, j_handle, joint) in joints.attached_joints(body_handle) {
154 /// println!("Body connected to {:?} via {:?}", b2, j_handle);
155 /// }
156 /// ```
157 pub fn attached_joints(
158 &self,
159 body: RigidBodyHandle,
160 ) -> impl Iterator<
161 Item = (
162 RigidBodyHandle,
163 RigidBodyHandle,
164 ImpulseJointHandle,
165 &ImpulseJoint,
166 ),
167 > {
168 self.rb_graph_ids
169 .get(body.0)
170 .into_iter()
171 .flat_map(move |id| self.joint_graph.interactions_with(*id))
172 .map(|inter| (inter.0, inter.1, inter.2.handle, inter.2))
173 }
174
175 /// Iterates through all the impulse joints attached to the given rigid-body.
176 pub fn map_attached_joints_mut(
177 &mut self,
178 body: RigidBodyHandle,
179 mut f: impl FnMut(RigidBodyHandle, RigidBodyHandle, ImpulseJointHandle, &mut ImpulseJoint),
180 ) {
181 self.rb_graph_ids.get(body.0).into_iter().for_each(|id| {
182 for inter in self.joint_graph.interactions_with_mut(*id) {
183 (f)(inter.0, inter.1, inter.3.handle, inter.3)
184 }
185 })
186 }
187
188 /// Returns only the enabled joints attached to a body.
189 ///
190 /// Same as `attached_joints()` but filters out disabled joints.
191 pub fn attached_enabled_joints(
192 &self,
193 body: RigidBodyHandle,
194 ) -> impl Iterator<
195 Item = (
196 RigidBodyHandle,
197 RigidBodyHandle,
198 ImpulseJointHandle,
199 &ImpulseJoint,
200 ),
201 > {
202 self.attached_joints(body)
203 .filter(|inter| inter.3.data.is_enabled())
204 }
205
206 /// Checks if the given joint handle is valid (joint still exists).
207 pub fn contains(&self, handle: ImpulseJointHandle) -> bool {
208 self.joint_ids.contains(handle.0)
209 }
210
211 /// Returns a read-only reference to the joint with the given handle.
212 pub fn get(&self, handle: ImpulseJointHandle) -> Option<&ImpulseJoint> {
213 let id = self.joint_ids.get(handle.0)?;
214 self.joint_graph.graph.edge_weight(*id)
215 }
216
217 /// Returns a mutable reference to the joint with the given handle.
218 ///
219 /// # Parameters
220 /// * `wake_up_connected_bodies` - If `true`, wakes up both bodies connected by this joint
221 pub fn get_mut(
222 &mut self,
223 handle: ImpulseJointHandle,
224 wake_up_connected_bodies: bool,
225 ) -> Option<&mut ImpulseJoint> {
226 let id = self.joint_ids.get(handle.0)?;
227 let joint = self.joint_graph.graph.edge_weight_mut(*id);
228 if wake_up_connected_bodies {
229 if let Some(joint) = &joint {
230 self.to_wake_up.insert(joint.body1);
231 self.to_wake_up.insert(joint.body2);
232 }
233 }
234 joint
235 }
236
237 /// Gets a joint by index without knowing the generation (advanced/unsafe).
238 ///
239 /// ⚠️ **Prefer `get()` instead!** This bypasses generation checks.
240 /// See [`RigidBodySet::get_unknown_gen`] for details on the ABA problem.
241 pub fn get_unknown_gen(&self, i: u32) -> Option<(&ImpulseJoint, ImpulseJointHandle)> {
242 let (id, handle) = self.joint_ids.get_unknown_gen(i)?;
243 Some((
244 self.joint_graph.graph.edge_weight(*id)?,
245 ImpulseJointHandle(handle),
246 ))
247 }
248
249 /// Gets a mutable joint by index without knowing the generation (advanced/unsafe).
250 ///
251 /// ⚠️ **Prefer `get_mut()` instead!** This bypasses generation checks.
252 pub fn get_unknown_gen_mut(
253 &mut self,
254 i: u32,
255 ) -> Option<(&mut ImpulseJoint, ImpulseJointHandle)> {
256 let (id, handle) = self.joint_ids.get_unknown_gen(i)?;
257 Some((
258 self.joint_graph.graph.edge_weight_mut(*id)?,
259 ImpulseJointHandle(handle),
260 ))
261 }
262
263 /// Iterates over all joints in this collection.
264 ///
265 /// Each iteration yields `(joint_handle, &joint)`.
266 pub fn iter(&self) -> impl Iterator<Item = (ImpulseJointHandle, &ImpulseJoint)> {
267 self.joint_graph
268 .graph
269 .edges
270 .iter()
271 .map(|e| (e.weight.handle, &e.weight))
272 }
273
274 /// Iterates over all joints with mutable access.
275 ///
276 /// Each iteration yields `(joint_handle, &mut joint)`.
277 pub fn iter_mut(&mut self) -> impl Iterator<Item = (ImpulseJointHandle, &mut ImpulseJoint)> {
278 self.joint_graph
279 .graph
280 .edges
281 .iter_mut()
282 .map(|e| (e.weight.handle, &mut e.weight))
283 }
284
285 // /// The set of impulse_joints as an array.
286 // pub(crate) fn impulse_joints(&self) -> &[JointGraphEdge] {
287 // // self.joint_graph
288 // // .graph
289 // // .edges
290 // // .iter_mut()
291 // // .map(|e| &mut e.weight)
292 // }
293
294 // #[cfg(not(feature = "parallel"))]
295 #[allow(dead_code)] // That will likely be useful when we re-introduce intra-island parallelism.
296 pub(crate) fn joints_mut(&mut self) -> &mut [JointGraphEdge] {
297 &mut self.joint_graph.graph.edges[..]
298 }
299
300 #[cfg(feature = "parallel")]
301 pub(crate) fn joints_vec_mut(&mut self) -> &mut Vec<JointGraphEdge> {
302 &mut self.joint_graph.graph.edges
303 }
304
305 /// Adds a joint connecting two bodies and returns its handle.
306 ///
307 /// The joint constrains how the two bodies can move relative to each other.
308 ///
309 /// # Parameters
310 /// * `body1`, `body2` - The two bodies to connect
311 /// * `data` - The joint configuration (FixedJoint, RevoluteJoint, etc.)
312 /// * `wake_up` - If `true`, wakes up both bodies
313 ///
314 /// # Example
315 /// ```
316 /// # use rapier3d::prelude::*;
317 /// # let mut bodies = RigidBodySet::new();
318 /// # let mut joints = ImpulseJointSet::new();
319 /// # let body1 = bodies.insert(RigidBodyBuilder::dynamic());
320 /// # let body2 = bodies.insert(RigidBodyBuilder::dynamic());
321 /// let joint = RevoluteJointBuilder::new(Vector::Y)
322 /// .local_anchor1(Vector::new(1.0, 0.0, 0.0))
323 /// .local_anchor2(Vector::new(-1.0, 0.0, 0.0))
324 /// .build();
325 /// let handle = joints.insert(body1, body2, joint, true);
326 /// ```
327 #[profiling::function]
328 pub fn insert(
329 &mut self,
330 body1: RigidBodyHandle,
331 body2: RigidBodyHandle,
332 data: impl Into<GenericJoint>,
333 wake_up: bool,
334 ) -> ImpulseJointHandle {
335 let data = data.into();
336 let handle = self.joint_ids.insert(0.into());
337 let joint = ImpulseJoint {
338 body1,
339 body2,
340 data,
341 impulses: Default::default(),
342 handle: ImpulseJointHandle(handle),
343 };
344
345 let default_id = InteractionGraph::<(), ()>::invalid_graph_index();
346 let mut graph_index1 = *self
347 .rb_graph_ids
348 .ensure_element_exist(joint.body1.0, default_id);
349 let mut graph_index2 = *self
350 .rb_graph_ids
351 .ensure_element_exist(joint.body2.0, default_id);
352
353 // NOTE: the body won't have a graph index if it does not
354 // have any joint attached.
355 if !InteractionGraph::<RigidBodyHandle, ImpulseJoint>::is_graph_index_valid(graph_index1) {
356 graph_index1 = self.joint_graph.graph.add_node(joint.body1);
357 self.rb_graph_ids.insert(joint.body1.0, graph_index1);
358 }
359
360 if !InteractionGraph::<RigidBodyHandle, ImpulseJoint>::is_graph_index_valid(graph_index2) {
361 graph_index2 = self.joint_graph.graph.add_node(joint.body2);
362 self.rb_graph_ids.insert(joint.body2.0, graph_index2);
363 }
364
365 self.joint_ids[handle] = self.joint_graph.add_edge(graph_index1, graph_index2, joint);
366
367 if wake_up {
368 self.to_wake_up.insert(body1);
369 self.to_wake_up.insert(body2);
370 }
371
372 self.to_join.insert((body1, body2));
373
374 ImpulseJointHandle(handle)
375 }
376
377 /// Retrieve all the enabled impulse joints happening between two active bodies.
378 // NOTE: this is very similar to the code from NarrowPhase::select_active_interactions.
379 pub(crate) fn select_active_interactions(
380 &self,
381 islands: &IslandManager,
382 bodies: &RigidBodySet,
383 out: &mut [Vec<JointIndex>],
384 ) {
385 for out_island in &mut out[..islands.active_islands().len()] {
386 out_island.clear();
387 }
388
389 // FIXME: don't iterate through all the interactions.
390 for (i, edge) in self.joint_graph.graph.edges.iter().enumerate() {
391 let joint = &edge.weight;
392 let rb1 = &bodies[joint.body1];
393 let rb2 = &bodies[joint.body2];
394
395 if joint.data.is_enabled()
396 && (rb1.is_dynamic_or_kinematic() || rb2.is_dynamic_or_kinematic())
397 && (!rb1.is_dynamic_or_kinematic() || !rb1.is_sleeping())
398 && (!rb2.is_dynamic_or_kinematic() || !rb2.is_sleeping())
399 {
400 let island_awake_index = if !rb1.is_dynamic_or_kinematic() {
401 islands.islands[rb2.ids.active_island_id]
402 .id_in_awake_list()
403 .expect("Internal error: island should be awake.")
404 } else {
405 islands.islands[rb1.ids.active_island_id]
406 .id_in_awake_list()
407 .expect("Internal error: island should be awake.")
408 };
409
410 out[island_awake_index].push(i);
411 }
412 }
413 }
414
415 /// Removes a joint from the world.
416 ///
417 /// Returns the removed joint if it existed, or `None` if the handle was invalid.
418 ///
419 /// # Parameters
420 /// * `wake_up` - If `true`, wakes up both bodies that were connected by this joint
421 ///
422 /// # Example
423 /// ```
424 /// # use rapier3d::prelude::*;
425 /// # let mut bodies = RigidBodySet::new();
426 /// # let mut joints = ImpulseJointSet::new();
427 /// # let body1 = bodies.insert(RigidBodyBuilder::dynamic());
428 /// # let body2 = bodies.insert(RigidBodyBuilder::dynamic());
429 /// # let joint = RevoluteJointBuilder::new(Vector::Y).build();
430 /// # let joint_handle = joints.insert(body1, body2, joint, true);
431 /// if let Some(joint) = joints.remove(joint_handle, true) {
432 /// println!("Removed joint between {:?} and {:?}", joint.body1, joint.body2);
433 /// }
434 /// ```
435 #[profiling::function]
436 pub fn remove(&mut self, handle: ImpulseJointHandle, wake_up: bool) -> Option<ImpulseJoint> {
437 let id = self.joint_ids.remove(handle.0)?;
438 let endpoints = self.joint_graph.graph.edge_endpoints(id)?;
439
440 if wake_up {
441 if let Some(rb_handle) = self.joint_graph.graph.node_weight(endpoints.0) {
442 self.to_wake_up.insert(*rb_handle);
443 }
444 if let Some(rb_handle) = self.joint_graph.graph.node_weight(endpoints.1) {
445 self.to_wake_up.insert(*rb_handle);
446 }
447 }
448
449 let removed_joint = self.joint_graph.graph.remove_edge(id);
450
451 if let Some(edge) = self.joint_graph.graph.edge_weight(id) {
452 self.joint_ids[edge.handle.0] = id;
453 }
454
455 removed_joint
456 }
457
458 /// Deletes all the impulse_joints attached to the given rigid-body.
459 ///
460 /// The provided rigid-body handle is not required to identify a rigid-body that
461 /// is still contained by the `bodies` component set.
462 /// Returns the (now invalid) handles of the removed impulse_joints.
463 #[profiling::function]
464 pub fn remove_joints_attached_to_rigid_body(
465 &mut self,
466 handle: RigidBodyHandle,
467 ) -> Vec<ImpulseJointHandle> {
468 let mut deleted = vec![];
469
470 if let Some(deleted_id) = self
471 .rb_graph_ids
472 .remove(handle.0, InteractionGraph::<(), ()>::invalid_graph_index())
473 {
474 if InteractionGraph::<(), ()>::is_graph_index_valid(deleted_id) {
475 // We have to delete each joint one by one in order to:
476 // - Wake-up the attached bodies.
477 // - Update our Handle -> graph edge mapping.
478 // Delete the node.
479 let to_delete: Vec<_> = self
480 .joint_graph
481 .interactions_with(deleted_id)
482 .map(|e| (e.0, e.1, e.2.handle))
483 .collect();
484 for (h1, h2, to_delete_handle) in to_delete {
485 deleted.push(to_delete_handle);
486 let to_delete_edge_id = self.joint_ids.remove(to_delete_handle.0).unwrap();
487 self.joint_graph.graph.remove_edge(to_delete_edge_id);
488
489 // Update the id of the edge which took the place of the deleted one.
490 if let Some(j) = self.joint_graph.graph.edge_weight_mut(to_delete_edge_id) {
491 self.joint_ids[j.handle.0] = to_delete_edge_id;
492 }
493
494 // Wake up the attached bodies.
495 self.to_wake_up.insert(h1);
496 self.to_wake_up.insert(h2);
497 }
498
499 if let Some(other) = self.joint_graph.remove_node(deleted_id) {
500 // One rigid-body joint graph index may have been invalidated
501 // so we need to update it.
502 self.rb_graph_ids.insert(other.0, deleted_id);
503 }
504 }
505 }
506
507 deleted
508 }
509}