rapier3d/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}