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