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}