Skip to main content

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}