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