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}