Skip to main content

rapier2d/dynamics/joint/impulse_joint/
impulse_joint_set.rs

1use crate::alloc_prelude::*;
2use parry::utils::hashset::HashSet;
3
4use super::ImpulseJoint;
5use crate::geometry::{InteractionGraph, RigidBodyGraphIndex, TemporaryInteractionIndex};
6
7use crate::data::Coarena;
8use crate::data::arena::Arena;
9use crate::dynamics::{
10    GenericJoint, ImpulseJointHandle, IslandManager, RigidBodyHandle, RigidBodySet,
11};
12
13pub(crate) type JointIndex = usize;
14pub(crate) type JointGraphEdge = crate::data::graph::Edge<ImpulseJoint>;
15
16#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
17#[derive(Clone, Default, Debug)]
18/// The collection that stores all joints connecting rigid bodies in your physics world.
19///
20/// Joints constrain how two bodies can move relative to each other. This set manages
21/// all joint instances (hinges, sliders, springs, etc.) using handles for safe access.
22///
23/// # Common joint types
24/// - [`FixedJoint`](crate::dynamics::FixedJoint): Weld two bodies together
25/// - [`RevoluteJoint`](crate::dynamics::RevoluteJoint): Hinge (rotation around axis)
26/// - [`PrismaticJoint`](crate::dynamics::PrismaticJoint): Slider (translation along axis)
27/// - [`SpringJoint`](crate::dynamics::SpringJoint): Elastic connection
28/// - [`RopeJoint`](crate::dynamics::RopeJoint): Maximum distance limit
29///
30/// # Example
31/// ```
32/// # use rapier3d::prelude::*;
33/// # let mut bodies = RigidBodySet::new();
34/// # let body1 = bodies.insert(RigidBodyBuilder::dynamic());
35/// # let body2 = bodies.insert(RigidBodyBuilder::dynamic());
36/// let mut joints = ImpulseJointSet::new();
37///
38/// // Create a hinge connecting two bodies
39/// let joint = RevoluteJointBuilder::new(Vector::Y)
40///     .local_anchor1(Vector::new(1.0, 0.0, 0.0))
41///     .local_anchor2(Vector::new(-1.0, 0.0, 0.0))
42///     .build();
43/// let handle = joints.insert(body1, body2, joint, true);
44/// ```
45pub struct ImpulseJointSet {
46    rb_graph_ids: Coarena<RigidBodyGraphIndex>,
47    /// Map joint handles to edge ids on the graph.
48    joint_ids: Arena<TemporaryInteractionIndex>,
49    joint_graph: InteractionGraph<RigidBodyHandle, ImpulseJoint>,
50    /// A set of rigid-body handles to wake-up during the next timestep.
51    pub(crate) to_wake_up: HashSet<RigidBodyHandle>,
52    /// A set of rigid-body pairs to join in the island manager during the next timestep.
53    pub(crate) to_join: HashSet<(RigidBodyHandle, RigidBodyHandle)>,
54    /// Persistent-island connectivity events (joint created/removed/rewired),
55    /// drained at the start of the next timestep, in order.
56    #[cfg_attr(feature = "serde-serialize", serde(skip))]
57    pub(crate) island_events: Vec<crate::dynamics::ImpulseJointIslandEvent>,
58    /// Bumped by every mutation that can affect the solver's joint constraint assembly (joint
59    /// insertion/removal, mutable joint access, user-changes to a rigid-body with attached joints).
60    /// The solver reuses its joint assembly while this, the joint list, and the island epoch are unchanged.
61    #[cfg_attr(feature = "serde-serialize", serde(skip))]
62    pub(crate) assembly_epoch: u32,
63    /// The `(active_set_epoch, assembly_epoch)` the last [`Self::select_active_interactions`] ran
64    /// with: while both are unchanged the selection (and the solver-body ids it stamps) is
65    /// identical, so the caller's previous output is reused untouched.
66    #[cfg_attr(feature = "serde-serialize", serde(skip))]
67    selection_epochs: Option<(u32, u32)>,
68}
69
70impl ImpulseJointSet {
71    /// Creates a new empty set of impulse_joints.
72    pub fn new() -> Self {
73        Self {
74            rb_graph_ids: Coarena::new(),
75            joint_ids: Arena::new(),
76            joint_graph: InteractionGraph::new(),
77            to_wake_up: HashSet::default(),
78            to_join: HashSet::default(),
79            island_events: Vec::new(),
80            assembly_epoch: 0,
81            selection_epochs: None,
82        }
83    }
84
85    /// Drops the memo of the last [`Self::select_active_interactions`], forcing the next
86    /// call to recompute into the caller's buffer.
87    pub(crate) fn invalidate_selection_memo(&mut self) {
88        self.selection_epochs = None;
89    }
90
91    /// Marks the solver-facing joint assembly inputs as changed. See
92    /// [`Self::assembly_epoch`].
93    pub(crate) fn bump_assembly_epoch(&mut self) {
94        self.assembly_epoch = self.assembly_epoch.wrapping_add(1);
95        self.selection_epochs = None;
96    }
97
98    /// `true` if this body has (or recently had) impulse joints attached.
99    pub(crate) fn body_may_have_joints(&self, body: crate::dynamics::RigidBodyHandle) -> bool {
100        self.rb_graph_ids.get(body.0).is_some_and(|id| {
101            InteractionGraph::<RigidBodyHandle, ImpulseJoint>::is_graph_index_valid(*id)
102        })
103    }
104
105    /// Returns how many joints are currently in this collection.
106    pub fn len(&self) -> usize {
107        self.joint_graph.graph.edges.len()
108    }
109
110    /// Returns `true` if there are no joints in this collection.
111    pub fn is_empty(&self) -> bool {
112        self.joint_graph.graph.edges.is_empty()
113    }
114
115    /// Returns the internal graph structure (nodes=bodies, edges=joints).
116    ///
117    /// Advanced usage - most users should use `attached_joints()` instead.
118    pub fn joint_graph(&self) -> &InteractionGraph<RigidBodyHandle, ImpulseJoint> {
119        &self.joint_graph
120    }
121
122    /// Returns all joints connecting two specific bodies.
123    ///
124    /// Usually returns 0 or 1 joint, but multiple joints can connect the same pair.
125    ///
126    /// # Example
127    /// ```
128    /// # use rapier3d::prelude::*;
129    /// # let mut bodies = RigidBodySet::new();
130    /// # let mut joints = ImpulseJointSet::new();
131    /// # let body1 = bodies.insert(RigidBodyBuilder::dynamic());
132    /// # let body2 = bodies.insert(RigidBodyBuilder::dynamic());
133    /// # let joint = RevoluteJointBuilder::new(Vector::Y);
134    /// # joints.insert(body1, body2, joint, true);
135    /// for (handle, joint) in joints.joints_between(body1, body2) {
136    ///     println!("Found joint {:?}", handle);
137    /// }
138    /// ```
139    pub fn joints_between(
140        &self,
141        body1: RigidBodyHandle,
142        body2: RigidBodyHandle,
143    ) -> impl Iterator<Item = (ImpulseJointHandle, &ImpulseJoint)> {
144        self.rb_graph_ids
145            .get(body1.0)
146            .zip(self.rb_graph_ids.get(body2.0))
147            .into_iter()
148            .flat_map(move |(id1, id2)| self.joint_graph.interactions_between(*id1, *id2))
149            .map(|inter| (inter.2.handle, inter.2))
150    }
151
152    /// Returns all joints attached to a specific body.
153    ///
154    /// Each result is `(body1, body2, joint_handle, joint)` where one of the bodies
155    /// matches the queried body.
156    ///
157    /// # Example
158    /// ```
159    /// # use rapier3d::prelude::*;
160    /// # let mut bodies = RigidBodySet::new();
161    /// # let mut joints = ImpulseJointSet::new();
162    /// # let body_handle = bodies.insert(RigidBodyBuilder::dynamic());
163    /// # let other_body = bodies.insert(RigidBodyBuilder::dynamic());
164    /// # let joint = RevoluteJointBuilder::new(Vector::Y);
165    /// # joints.insert(body_handle, other_body, joint, true);
166    /// for (b1, b2, j_handle, joint) in joints.attached_joints(body_handle) {
167    ///     println!("Body connected to {:?} via {:?}", b2, j_handle);
168    /// }
169    /// ```
170    pub fn attached_joints(
171        &self,
172        body: RigidBodyHandle,
173    ) -> impl Iterator<
174        Item = (
175            RigidBodyHandle,
176            RigidBodyHandle,
177            ImpulseJointHandle,
178            &ImpulseJoint,
179        ),
180    > {
181        self.rb_graph_ids
182            .get(body.0)
183            .into_iter()
184            .flat_map(move |id| self.joint_graph.interactions_with(*id))
185            .map(|inter| (inter.0, inter.1, inter.2.handle, inter.2))
186    }
187
188    /// Iterates through all the impulse joints attached to the given rigid-body.
189    pub fn map_attached_joints_mut(
190        &mut self,
191        body: RigidBodyHandle,
192        mut f: impl FnMut(RigidBodyHandle, RigidBodyHandle, ImpulseJointHandle, &mut ImpulseJoint),
193    ) {
194        self.bump_assembly_epoch();
195        self.rb_graph_ids.get(body.0).into_iter().for_each(|id| {
196            for inter in self.joint_graph.interactions_with_mut(*id) {
197                (f)(inter.0, inter.1, inter.3.handle, inter.3)
198            }
199        })
200    }
201
202    /// Returns only the enabled joints attached to a body.
203    ///
204    /// Same as `attached_joints()` but filters out disabled joints.
205    pub fn attached_enabled_joints(
206        &self,
207        body: RigidBodyHandle,
208    ) -> impl Iterator<
209        Item = (
210            RigidBodyHandle,
211            RigidBodyHandle,
212            ImpulseJointHandle,
213            &ImpulseJoint,
214        ),
215    > {
216        self.attached_joints(body)
217            .filter(|inter| inter.3.data.is_enabled())
218    }
219
220    /// Checks if the given joint handle is valid (joint still exists).
221    pub fn contains(&self, handle: ImpulseJointHandle) -> bool {
222        self.joint_ids.contains(handle.0)
223    }
224
225    /// Returns a read-only reference to the joint with the given handle.
226    pub fn get(&self, handle: ImpulseJointHandle) -> Option<&ImpulseJoint> {
227        let id = self.joint_ids.get(handle.0)?;
228        self.joint_graph.graph.edge_weight(*id)
229    }
230
231    /// Returns a mutable reference to the joint with the given handle.
232    ///
233    /// # Parameters
234    /// * `wake_up_connected_bodies` - If `true`, wakes up both bodies connected by this joint
235    pub fn get_mut(
236        &mut self,
237        handle: ImpulseJointHandle,
238        wake_up_connected_bodies: bool,
239    ) -> Option<&mut ImpulseJoint> {
240        self.bump_assembly_epoch();
241        let id = self.joint_ids.get(handle.0)?;
242        let joint = self.joint_graph.graph.edge_weight_mut(*id);
243        if wake_up_connected_bodies {
244            if let Some(joint) = &joint {
245                self.to_wake_up.insert(joint.body1);
246                self.to_wake_up.insert(joint.body2);
247            }
248        }
249        joint
250    }
251
252    /// Gets a joint by index without knowing the generation (advanced/unsafe).
253    ///
254    /// ⚠️ **Prefer `get()` instead!** This bypasses generation checks.
255    /// See [`RigidBodySet::get_unknown_gen`] for details on the ABA problem.
256    pub fn get_unknown_gen(&self, i: u32) -> Option<(&ImpulseJoint, ImpulseJointHandle)> {
257        let (id, handle) = self.joint_ids.get_unknown_gen(i)?;
258        Some((
259            self.joint_graph.graph.edge_weight(*id)?,
260            ImpulseJointHandle(handle),
261        ))
262    }
263
264    /// Gets a mutable joint by index without knowing the generation (advanced/unsafe).
265    ///
266    /// ⚠️ **Prefer `get_mut()` instead!** This bypasses generation checks.
267    pub fn get_unknown_gen_mut(
268        &mut self,
269        i: u32,
270    ) -> Option<(&mut ImpulseJoint, ImpulseJointHandle)> {
271        self.bump_assembly_epoch();
272        let (id, handle) = self.joint_ids.get_unknown_gen(i)?;
273        Some((
274            self.joint_graph.graph.edge_weight_mut(*id)?,
275            ImpulseJointHandle(handle),
276        ))
277    }
278
279    /// Iterates over all joints in this collection.
280    ///
281    /// Each iteration yields `(joint_handle, &joint)`.
282    pub fn iter(&self) -> impl Iterator<Item = (ImpulseJointHandle, &ImpulseJoint)> {
283        self.joint_graph
284            .graph
285            .edges
286            .iter()
287            .map(|e| (e.weight.handle, &e.weight))
288    }
289
290    /// Iterates over all joints with mutable access.
291    ///
292    /// Each iteration yields `(joint_handle, &mut joint)`.
293    pub fn iter_mut(&mut self) -> impl Iterator<Item = (ImpulseJointHandle, &mut ImpulseJoint)> {
294        self.bump_assembly_epoch();
295        self.joint_graph
296            .graph
297            .edges
298            .iter_mut()
299            .map(|e| (e.weight.handle, &mut e.weight))
300    }
301
302    pub(crate) fn joints_mut(&mut self) -> &mut [JointGraphEdge] {
303        &mut self.joint_graph.graph.edges[..]
304    }
305
306    /// Adds a joint connecting two bodies and returns its handle.
307    ///
308    /// The joint constrains how the two bodies can move relative to each other.
309    ///
310    /// # Parameters
311    /// * `body1`, `body2` - The two bodies to connect
312    /// * `data` - The joint configuration (FixedJoint, RevoluteJoint, etc.)
313    /// * `wake_up` - If `true`, wakes up both bodies
314    ///
315    /// # Example
316    /// ```
317    /// # use rapier3d::prelude::*;
318    /// # let mut bodies = RigidBodySet::new();
319    /// # let mut joints = ImpulseJointSet::new();
320    /// # let body1 = bodies.insert(RigidBodyBuilder::dynamic());
321    /// # let body2 = bodies.insert(RigidBodyBuilder::dynamic());
322    /// let joint = RevoluteJointBuilder::new(Vector::Y)
323    ///     .local_anchor1(Vector::new(1.0, 0.0, 0.0))
324    ///     .local_anchor2(Vector::new(-1.0, 0.0, 0.0))
325    ///     .build();
326    /// let handle = joints.insert(body1, body2, joint, true);
327    /// ```
328    #[profiling::function]
329    pub fn insert(
330        &mut self,
331        body1: RigidBodyHandle,
332        body2: RigidBodyHandle,
333        data: impl Into<GenericJoint>,
334        wake_up: bool,
335    ) -> ImpulseJointHandle {
336        let data = data.into();
337        let joint_enabled = data.is_enabled();
338        self.bump_assembly_epoch();
339        let handle = self.joint_ids.insert(0.into());
340        let joint = ImpulseJoint {
341            body1,
342            body2,
343            data,
344            impulses: Default::default(),
345            handle: ImpulseJointHandle(handle),
346            solver_body_ids: [u32::MAX; 2],
347            solver_color: crate::geometry::contact_pair::SOLVER_COLOR_UNCOLORED,
348        };
349
350        let default_id = InteractionGraph::<(), ()>::invalid_graph_index();
351        let mut graph_index1 = *self
352            .rb_graph_ids
353            .ensure_element_exist(joint.body1.0, default_id);
354        let mut graph_index2 = *self
355            .rb_graph_ids
356            .ensure_element_exist(joint.body2.0, default_id);
357
358        // NOTE: the body won't have a graph index if it does not
359        // have any joint attached.
360        if !InteractionGraph::<RigidBodyHandle, ImpulseJoint>::is_graph_index_valid(graph_index1) {
361            graph_index1 = self.joint_graph.graph.add_node(joint.body1);
362            self.rb_graph_ids.insert(joint.body1.0, graph_index1);
363        }
364
365        if !InteractionGraph::<RigidBodyHandle, ImpulseJoint>::is_graph_index_valid(graph_index2) {
366            graph_index2 = self.joint_graph.graph.add_node(joint.body2);
367            self.rb_graph_ids.insert(joint.body2.0, graph_index2);
368        }
369
370        self.joint_ids[handle] = self.joint_graph.add_edge(graph_index1, graph_index2, joint);
371
372        if wake_up {
373            self.to_wake_up.insert(body1);
374            self.to_wake_up.insert(body2);
375        }
376
377        self.to_join.insert((body1, body2));
378        if joint_enabled {
379            self.island_events
380                .push(crate::dynamics::ImpulseJointIslandEvent::Link {
381                    handle: ImpulseJointHandle(handle),
382                    body1,
383                    body2,
384                });
385        }
386
387        ImpulseJointHandle(handle)
388    }
389
390    /// Rewires an existing joint to a new pair of bodies, preserving its
391    /// [`ImpulseJointHandle`], its `GenericJoint` configuration, and its
392    /// warm-started impulses.
393    ///
394    /// Unlike directly mutating `body1`/`body2` (which isn't possible anyway
395    /// since those fields are crate-private), this also updates the
396    /// interaction graph and schedules the new pair for island merging so
397    /// the solver, island manager, and joint constraint builder stay in
398    /// sync. Use this when retargeting a persistent joint — e.g. a mouse
399    /// "pick" joint that is pre-allocated at startup and reattached to
400    /// whatever body the user grabs.
401    ///
402    /// # Parameters
403    /// * `handle` - The joint to rewire. If the handle is invalid this
404    ///   returns `None`.
405    /// * `new_body1`, `new_body2` - The new endpoints.
406    /// * `wake_up` - If `true`, wakes up both the previous and new endpoints.
407    ///
408    /// Returns a mutable reference to the updated joint, or `None` if the
409    /// handle was stale. When `new_body1`/`new_body2` match the joint's
410    /// current endpoints, this is a no-op and returns the joint unchanged.
411    ///
412    /// # Example
413    /// ```
414    /// # use rapier3d::prelude::*;
415    /// # let mut bodies = RigidBodySet::new();
416    /// # let mut joints = ImpulseJointSet::new();
417    /// # let body1 = bodies.insert(RigidBodyBuilder::dynamic());
418    /// # let body2 = bodies.insert(RigidBodyBuilder::dynamic());
419    /// # let body3 = bodies.insert(RigidBodyBuilder::dynamic());
420    /// # let joint = RevoluteJointBuilder::new(Vector::Y).build();
421    /// let joint_handle = joints.insert(body1, body2, joint, true);
422    /// // Swap body2 for body3 without losing the handle or the joint data.
423    /// joints.set_bodies(joint_handle, body1, body3, true);
424    /// ```
425    #[profiling::function]
426    pub fn set_bodies(
427        &mut self,
428        handle: ImpulseJointHandle,
429        new_body1: RigidBodyHandle,
430        new_body2: RigidBodyHandle,
431        wake_up: bool,
432    ) -> Option<&mut ImpulseJoint> {
433        self.bump_assembly_epoch();
434        let edge_id = *self.joint_ids.get(handle.0)?;
435
436        // Early-out when the endpoints haven't actually changed.
437        let (old_body1, old_body2) = {
438            let joint = self.joint_graph.graph.edge_weight(edge_id)?;
439            (joint.body1, joint.body2)
440        };
441        if old_body1 == new_body1 && old_body2 == new_body2 {
442            return self.joint_graph.graph.edge_weight_mut(edge_id);
443        }
444
445        // Detach the edge from its current endpoints. `remove_edge` uses
446        // `swap_remove`, so another edge may have taken `edge_id`'s slot —
447        // patch its handle→edge mapping exactly like `ImpulseJointSet::remove`.
448        let mut joint = self.joint_graph.graph.remove_edge(edge_id)?;
449        if let Some(swapped) = self.joint_graph.graph.edge_weight(edge_id) {
450            self.joint_ids[swapped.handle.0] = edge_id;
451        }
452
453        // Ensure both new endpoints have graph nodes (same dance `insert`
454        // does for first-time endpoints).
455        let default_id = InteractionGraph::<(), ()>::invalid_graph_index();
456        let mut graph_index1 = *self
457            .rb_graph_ids
458            .ensure_element_exist(new_body1.0, default_id);
459        let mut graph_index2 = *self
460            .rb_graph_ids
461            .ensure_element_exist(new_body2.0, default_id);
462        if !InteractionGraph::<RigidBodyHandle, ImpulseJoint>::is_graph_index_valid(graph_index1) {
463            graph_index1 = self.joint_graph.graph.add_node(new_body1);
464            self.rb_graph_ids.insert(new_body1.0, graph_index1);
465        }
466        if !InteractionGraph::<RigidBodyHandle, ImpulseJoint>::is_graph_index_valid(graph_index2) {
467            graph_index2 = self.joint_graph.graph.add_node(new_body2);
468            self.rb_graph_ids.insert(new_body2.0, graph_index2);
469        }
470
471        joint.body1 = new_body1;
472        joint.body2 = new_body2;
473        let new_edge_id = self.joint_graph.add_edge(graph_index1, graph_index2, joint);
474        self.joint_ids[handle.0] = new_edge_id;
475
476        if wake_up {
477            self.to_wake_up.insert(old_body1);
478            self.to_wake_up.insert(old_body2);
479            self.to_wake_up.insert(new_body1);
480            self.to_wake_up.insert(new_body2);
481        }
482        self.to_join.insert((new_body1, new_body2));
483        self.island_events
484            .push(crate::dynamics::ImpulseJointIslandEvent::Unlink { handle });
485        if self
486            .joint_graph
487            .graph
488            .edge_weight(new_edge_id)
489            .is_some_and(|j| j.data.is_enabled())
490        {
491            self.island_events
492                .push(crate::dynamics::ImpulseJointIslandEvent::Link {
493                    handle,
494                    body1: new_body1,
495                    body2: new_body2,
496                });
497        }
498
499        self.joint_graph.graph.edge_weight_mut(new_edge_id)
500    }
501
502    /// Retrieve all the enabled impulse joints happening between two active bodies.
503    // NOTE: this is very similar to the code from NarrowPhase::select_active_interactions.
504    pub(crate) fn select_active_interactions(
505        &mut self,
506        islands: &IslandManager,
507        bodies: &RigidBodySet,
508        out: &mut Vec<JointIndex>,
509    ) {
510        // The selection depends only on the active-set epoch and the assembly epoch: while both
511        // are unchanged, `out` (assumed to be the previous call's output, which the physics
512        // pipeline keeps around) and the stamped solver-body ids are still exact.
513        let epochs = (islands.active_set_epoch, self.assembly_epoch);
514        if self.selection_epochs == Some(epochs) {
515            return;
516        }
517        self.selection_epochs = Some(epochs);
518
519        out.clear();
520
521        // Only iterate joints adjacent to an active body instead of the whole graph: any joint
522        // selected below has at least one awake dynamic/kinematic body, so the neighborhood walk
523        // is exhaustive — and much smaller when most of the scene is asleep.
524        let mut candidates: Vec<u32> = Vec::new();
525
526        // When most bodies are awake, walking the graph adjacency (pointer-chasing) and sorting
527        // costs more than the linear edge scan it replaces — just visit every joint and let the
528        // per-joint checks below skip inactive ones.
529        let num_active = islands.active_bodies().count();
530        if num_active * 2 >= bodies.len() {
531            candidates.extend(0..self.joint_graph.graph.edges.len() as u32);
532        } else {
533            for handle in islands.active_bodies() {
534                if let Some(gid) = self.rb_graph_ids.get(handle.0) {
535                    for edge in self.joint_graph.graph.edges(*gid) {
536                        candidates.push(edge.id().index() as u32);
537                    }
538                }
539            }
540
541            // Sorting + deduplicating guarantees each joint is visited exactly once, in
542            // the same deterministic edge-index order as a full graph scan.
543            candidates.sort_unstable();
544            candidates.dedup();
545        }
546
547        for i in candidates.iter().map(|id| *id as usize) {
548            let edge = &mut self.joint_graph.graph.edges[i];
549            let joint = &mut edge.weight;
550            let rb1 = &bodies[joint.body1];
551            let rb2 = &bodies[joint.body2];
552
553            if joint.data.is_enabled()
554                && (rb1.is_dynamic_or_kinematic() || rb2.is_dynamic_or_kinematic())
555                && (!rb1.is_dynamic_or_kinematic() || !rb1.is_sleeping())
556                && (!rb2.is_dynamic_or_kinematic() || !rb2.is_sleeping())
557            {
558                // Stamp the solver-body ids while both body cache lines are hot so the solver's
559                // coloring/grouping and jacobian generation don't re-read the rigid-body set. `u32::MAX`
560                // marks a world-attached side (fixed, or defensively sleeping — its `active_set_id` indexes another island).
561                let solver_id = |rb: &crate::dynamics::RigidBody| {
562                    if rb.is_dynamic_or_kinematic() && !rb.is_sleeping() {
563                        rb.ids.active_set_id
564                    } else {
565                        u32::MAX
566                    }
567                };
568                joint.solver_body_ids = [solver_id(rb1), solver_id(rb2)];
569                out.push(i);
570            }
571        }
572    }
573
574    /// Removes a joint from the world.
575    ///
576    /// Returns the removed joint if it existed, or `None` if the handle was invalid.
577    ///
578    /// # Parameters
579    /// * `wake_up` - If `true`, wakes up both bodies that were connected by this joint
580    ///
581    /// # Example
582    /// ```
583    /// # use rapier3d::prelude::*;
584    /// # let mut bodies = RigidBodySet::new();
585    /// # let mut joints = ImpulseJointSet::new();
586    /// # let body1 = bodies.insert(RigidBodyBuilder::dynamic());
587    /// # let body2 = bodies.insert(RigidBodyBuilder::dynamic());
588    /// # let joint = RevoluteJointBuilder::new(Vector::Y).build();
589    /// # let joint_handle = joints.insert(body1, body2, joint, true);
590    /// if let Some(joint) = joints.remove(joint_handle, true) {
591    ///     println!("Removed joint between {:?} and {:?}", joint.body1(), joint.body2());
592    /// }
593    /// ```
594    #[profiling::function]
595    pub fn remove(&mut self, handle: ImpulseJointHandle, wake_up: bool) -> Option<ImpulseJoint> {
596        self.bump_assembly_epoch();
597        let id = self.joint_ids.remove(handle.0)?;
598        let endpoints = self.joint_graph.graph.edge_endpoints(id)?;
599
600        if wake_up {
601            for endpoint in [endpoints.0, endpoints.1] {
602                if let Some(rb_handle) = self.joint_graph.graph.node_weight(endpoint) {
603                    self.to_wake_up.insert(*rb_handle);
604                }
605            }
606        }
607
608        let removed_joint = self.joint_graph.graph.remove_edge(id);
609
610        if let Some(edge) = self.joint_graph.graph.edge_weight(id) {
611            self.joint_ids[edge.handle.0] = id;
612        }
613
614        self.island_events
615            .push(crate::dynamics::ImpulseJointIslandEvent::Unlink { handle });
616
617        removed_joint
618    }
619
620    /// Deletes all the impulse_joints attached to the given rigid-body.
621    ///
622    /// The provided rigid-body handle is not required to identify a rigid-body that
623    /// is still contained by the `bodies` component set.
624    /// Returns the (now invalid) handles of the removed impulse_joints.
625    #[profiling::function]
626    pub fn remove_joints_attached_to_rigid_body(
627        &mut self,
628        handle: RigidBodyHandle,
629    ) -> Vec<ImpulseJointHandle> {
630        let mut deleted = vec![];
631
632        if let Some(deleted_id) = self
633            .rb_graph_ids
634            .remove(handle.0, InteractionGraph::<(), ()>::invalid_graph_index())
635        {
636            if InteractionGraph::<(), ()>::is_graph_index_valid(deleted_id) {
637                // We have to delete each joint one by one in order to:
638                // - Wake-up the attached bodies.
639                // - Update our Handle -> graph edge mapping.
640                // Delete the node.
641                let to_delete: Vec<_> = self
642                    .joint_graph
643                    .interactions_with(deleted_id)
644                    .map(|e| (e.0, e.1, e.2.handle))
645                    .collect();
646                for (h1, h2, to_delete_handle) in to_delete {
647                    deleted.push(to_delete_handle);
648                    let to_delete_edge_id = self.joint_ids.remove(to_delete_handle.0).unwrap();
649                    self.joint_graph.graph.remove_edge(to_delete_edge_id);
650
651                    // Update the id of the edge which took the place of the deleted one.
652                    if let Some(j) = self.joint_graph.graph.edge_weight_mut(to_delete_edge_id) {
653                        self.joint_ids[j.handle.0] = to_delete_edge_id;
654                    }
655
656                    // Wake up the attached bodies.
657                    self.to_wake_up.insert(h1);
658                    self.to_wake_up.insert(h2);
659                    self.island_events
660                        .push(crate::dynamics::ImpulseJointIslandEvent::Unlink {
661                            handle: to_delete_handle,
662                        });
663                }
664
665                if let Some(other) = self.joint_graph.remove_node(deleted_id) {
666                    // One rigid-body joint graph index may have been invalidated
667                    // so we need to update it.
668                    self.rb_graph_ids.insert(other.0, deleted_id);
669                }
670            }
671        }
672
673        deleted
674    }
675}