Skip to main content

avian3d/dynamics/solver/islands/
mod.rs

1//! Persistent simulation islands for sleeping and waking.
2//!
3//! Islands are retained across time steps. Each dynamic body starts with its own island,
4//! and when a constraint between two dynamic bodies is created, the islands are merged with union find.
5//!
6//! Splitting is deferred and done using depth-first search (DFS). Only one island is split per time step,
7//! choosing the sleepiest island with one or more constraints removed as the candidate.
8//!
9//! Islands are only used for sleeping and waking. Solver parallelism is achieved with [graph coloring](super::constraint_graph)
10//! using the [`ConstraintGraph`](super::constraint_graph::ConstraintGraph).
11//!
12//! If simulation islands are not needed, the [`IslandPlugin`] can be omitted.
13//!
14//! # References
15//!
16//! - [Box2D - Simulation Islands] by [Erin Catto]
17//!
18//! [Box2D - Simulation Islands]: https://box2d.org/posts/2023/10/simulation-islands/
19//! [Erin Catto]: https://github.com/erincatto
20
21// Options:
22//
23// 1. Depth-first search (DFS): Islands are built from scratch with DFS every timestep.
24//    - Looks for awake bodies to be the seeds of islands, and traverses connected constraints.
25//    - Traversal mark management can be expensive.
26//    - Waking is cheap and straightforward.
27// 2. Union-find (UF): Each body starts in its own island. As constraints are added, the islands are merged.
28//    - Every island has a unique root body that is used to identify the island.
29//    - Reuires additional care to handle waking for connected bodies.
30//    - Can be parallelized at the cost of determinism, unless performing constraint sorting with quicksort,
31//      which is expensive for large islands.
32// 3. Persistent islands: Retain islands across time steps, and merge or split them as constraints are added or removed.
33//    - Each dynamic body starts with its own island. When a constraint between two dynamic bodies is created,
34//      the islands are merged with union find. When constraints between two bodies are removed, their islands
35//      are marked as candidates for splitting.
36//    - Splitting can be deferred and done in parallel, using union find, DFS, or any other island-finding algorithm.
37//    - Fast and deterministic!
38//
39// Avian uses persistent islands. See Erin Catto's "Simulation Islands" article for more details.
40// https://box2d.org/posts/2023/10/simulation-islands/
41//
42// The implementation is largely based on Box2D:
43// https://github.com/erincatto/box2d/blob/df9787b59e4480135fbd73d275f007b5d931a83f/src/island.c#L57
44
45mod sleeping;
46pub use sleeping::{IslandSleepingPlugin, SleepBody, SleepIslands, WakeBody, WakeIslands};
47
48use bevy::{
49    ecs::{
50        entity::{ComponentCloneCtx, SourceComponent},
51        entity_disabling::Disabled,
52        lifecycle::HookContext,
53        world::DeferredWorld,
54    },
55    prelude::*,
56};
57
58use crate::{
59    collision::contact_types::ContactId,
60    data_structures::stable_vec::StableVec,
61    dynamics::{
62        joints::joint_graph::{JointGraph, JointId},
63        solver::solver_body::SolverBodyIndex,
64    },
65    prelude::{
66        ContactGraph, PhysicsSchedule, RigidBody, RigidBodyColliders, RigidBodyDisabled,
67        SolverSystems,
68    },
69};
70
71/// A plugin for managing [`PhysicsIsland`]s.
72///
73/// See the [module-level documentation](self) for more information.
74pub struct IslandPlugin;
75
76impl Plugin for IslandPlugin {
77    fn build(&self, app: &mut App) {
78        app.init_resource::<PhysicsIslands>();
79
80        // Insert `BodyIslandNode` for each body that has a solver body.
81        app.register_required_components::<SolverBodyIndex, BodyIslandNode>();
82
83        // Add `BodyIslandNode` for each dynamic and kinematic rigid body
84        // when the associated rigid body is enabled.
85        app.add_observer(
86            |trigger: On<Discard<RigidBodyDisabled>>,
87             rb_query: Query<&RigidBody>,
88             mut commands: Commands| {
89                let Ok(rb) = rb_query.get(trigger.entity) else {
90                    return;
91                };
92                if rb.is_dynamic() || rb.is_kinematic() {
93                    commands
94                        .entity(trigger.entity)
95                        .try_insert(BodyIslandNode::default());
96                }
97            },
98        );
99        app.add_observer(
100            |trigger: On<Discard<Disabled>>,
101             rb_query: Query<
102                &RigidBody,
103                (
104                    // The body still has `Disabled` at this point,
105                    // and we need to include in the query to match against the entity.
106                    With<Disabled>,
107                    Without<RigidBodyDisabled>,
108                ),
109            >,
110             mut commands: Commands| {
111                let Ok(rb) = rb_query.get(trigger.entity) else {
112                    return;
113                };
114                if rb.is_dynamic() || rb.is_kinematic() {
115                    commands
116                        .entity(trigger.entity)
117                        .try_insert(BodyIslandNode::default());
118                }
119            },
120        );
121
122        // Remove `BodyIslandNode` when any of the following happens:
123        //
124        // 1. `RigidBody` is removed.
125        // 2. `Disabled` or `RigidBodyDisabled` is added to the body.
126        // 3. The body becomes `RigidBody::Static`.
127        app.add_observer(|trigger: On<Remove<RigidBody>>, mut commands: Commands| {
128            commands
129                .entity(trigger.entity)
130                .try_remove::<BodyIslandNode>();
131        });
132        app.add_observer(
133            |trigger: On<Insert<(Disabled, RigidBodyDisabled)>>,
134             query: Query<&RigidBody, Or<(With<Disabled>, Without<Disabled>)>>,
135             mut commands: Commands| {
136                if query.contains(trigger.entity) {
137                    commands
138                        .entity(trigger.entity)
139                        .try_remove::<BodyIslandNode>();
140                }
141            },
142        );
143        app.add_observer(
144            |trigger: On<Insert<RigidBody>>, query: Query<&RigidBody>, mut commands: Commands| {
145                if let Ok(rb) = query.get(trigger.entity)
146                    && rb.is_static()
147                {
148                    commands
149                        .entity(trigger.entity)
150                        .try_remove::<BodyIslandNode>();
151                }
152            },
153        );
154
155        app.add_systems(
156            PhysicsSchedule,
157            split_island.in_set(SolverSystems::Finalize),
158        );
159    }
160}
161
162fn split_island(
163    mut islands: ResMut<PhysicsIslands>,
164    mut body_islands: Query<&mut BodyIslandNode, Or<(With<Disabled>, Without<Disabled>)>>,
165    body_colliders: Query<&RigidBodyColliders>,
166    contact_graph: Res<ContactGraph>,
167    joint_graph: Res<JointGraph>,
168) {
169    // Splitting is only done when bodies want to sleep.
170    if let Some(island_id) = islands.split_candidate {
171        islands.split_island(
172            island_id,
173            &mut body_islands,
174            &body_colliders,
175            &contact_graph,
176            &joint_graph,
177        );
178    }
179}
180
181/// A stable identifier for a [`PhysicsIsland`].
182#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Reflect)]
183#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
184#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
185#[reflect(Debug, PartialEq)]
186pub struct IslandId(pub u32);
187
188impl IslandId {
189    /// A placeholder identifier for a [`PhysicsIsland`].
190    pub const PLACEHOLDER: Self = Self(u32::MAX);
191}
192
193impl core::fmt::Display for IslandId {
194    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
195        write!(f, "IslandId({})", self.0)
196    }
197}
198
199/// A [simulation island](self) that contains bodies, contacts, and joints. Used for sleeping and waking.
200///
201/// Islands are retained across time steps. Each dynamic body starts with its own island,
202/// and when a constraint between two dynamic bodies is created, the islands are merged with union find.
203///
204/// Splitting is deferred and done using depth-first search (DFS). Only one island is split per time step,
205/// choosing the sleepiest island with one or more constraints removed as the candidate.
206///
207/// Bodies, contacts, and joints are linked to islands using linked lists for efficient addition, removal,
208/// and merging. Each island stores the head and tail of each list, while an [`IslandNode`] links each item
209/// to the next and previous item in the list. Bodies store their node in the [`BodyIslandNode`] component,
210/// while [`PhysicsIslands`] stores the nodes for contacts and joints.
211#[derive(Clone, Debug, PartialEq)]
212pub struct PhysicsIsland {
213    pub(crate) id: IslandId,
214
215    pub(crate) head_body: Option<Entity>,
216    pub(crate) tail_body: Option<Entity>,
217    pub(crate) body_count: u32,
218
219    pub(crate) head_contact: Option<ContactId>,
220    pub(crate) tail_contact: Option<ContactId>,
221    pub(crate) contact_count: u32,
222
223    pub(crate) head_joint: Option<JointId>,
224    pub(crate) tail_joint: Option<JointId>,
225    pub(crate) joint_count: u32,
226
227    pub(crate) sleep_timer: f32,
228    pub(crate) is_sleeping: bool,
229
230    pub(crate) constraints_removed: u32,
231}
232
233impl PhysicsIsland {
234    /// Creates a new [`PhysicsIsland`] with the given ID.
235    #[inline]
236    pub const fn new(id: IslandId) -> Self {
237        Self {
238            id,
239            head_body: None,
240            tail_body: None,
241            body_count: 0,
242            head_contact: None,
243            tail_contact: None,
244            contact_count: 0,
245            head_joint: None,
246            tail_joint: None,
247            joint_count: 0,
248            sleep_timer: 0.0,
249            is_sleeping: false,
250            constraints_removed: 0,
251        }
252    }
253
254    /// Returns the island ID.
255    #[inline]
256    pub const fn id(&self) -> IslandId {
257        self.id
258    }
259
260    /// Returns the number of bodies in the island.
261    #[inline]
262    pub const fn body_count(&self) -> u32 {
263        self.body_count
264    }
265
266    /// Returns the number of contacts in the island.
267    #[inline]
268    pub const fn contact_count(&self) -> u32 {
269        self.contact_count
270    }
271
272    /// Returns the number of joints in the island.
273    #[inline]
274    pub const fn joint_count(&self) -> u32 {
275        self.joint_count
276    }
277
278    /// Returns `true` if the island is sleeping.
279    #[inline]
280    pub const fn is_sleeping(&self) -> bool {
281        self.is_sleeping
282    }
283
284    /// Returns the number of constraints that have been removed from the island,
285    #[inline]
286    pub const fn constraints_removed(&self) -> u32 {
287        self.constraints_removed
288    }
289
290    // TODO: Use errors rather than panics.
291    /// Validates the island.
292    #[inline]
293    pub fn validate(
294        &self,
295        body_islands: &Query<&BodyIslandNode, Or<(With<Disabled>, Without<Disabled>)>>,
296        contact_nodes: &[Option<IslandNode<ContactId>>],
297        joint_nodes: &[Option<IslandNode<JointId>>],
298    ) {
299        self.validate_bodies(body_islands);
300        self.validate_contacts(contact_nodes);
301        self.validate_joints(joint_nodes);
302    }
303
304    /// Validates the body linked list.
305    pub fn validate_bodies(
306        &self,
307        body_islands: &Query<&BodyIslandNode, Or<(With<Disabled>, Without<Disabled>)>>,
308    ) {
309        if self.head_body.is_none() {
310            assert!(self.tail_body.is_none());
311            assert_eq!(self.body_count, 0);
312            return;
313        }
314
315        assert!(self.tail_body.is_some());
316        assert!(self.body_count > 0);
317
318        if self.body_count > 1 {
319            assert_ne!(self.head_body, self.tail_body);
320        }
321
322        let mut count = 0;
323        let mut body_id = self.head_body;
324
325        while let Some(entity) = body_id {
326            let body = body_islands.get(entity).unwrap();
327            assert_eq!(body.island_id, self.id);
328
329            count += 1;
330
331            if count == self.body_count {
332                assert_eq!(body_id, self.tail_body);
333            }
334
335            body_id = body.next;
336        }
337
338        assert_eq!(count, self.body_count);
339    }
340
341    /// Validates the contact linked list.
342    pub fn validate_contacts(&self, contact_nodes: &[Option<IslandNode<ContactId>>]) {
343        if self.head_contact.is_none() {
344            assert!(self.tail_contact.is_none());
345            assert_eq!(self.contact_count, 0);
346            return;
347        }
348
349        assert!(self.tail_contact.is_some());
350        assert!(self.contact_count > 0);
351
352        if self.contact_count > 1 {
353            assert_ne!(self.head_contact, self.tail_contact);
354        }
355
356        let mut count = 0;
357        let mut contact_id = self.head_contact;
358
359        while let Some(id) = contact_id {
360            let contact_island = contact_nodes
361                .get(id.0 as usize)
362                .and_then(|node| node.as_ref())
363                .unwrap_or_else(|| panic!("Contact {id:?} has no island"));
364            assert_eq!(contact_island.island_id, self.id);
365
366            count += 1;
367
368            if count == self.contact_count {
369                assert_eq!(contact_id, self.tail_contact);
370            }
371
372            contact_id = contact_island.next;
373        }
374
375        assert_eq!(count, self.contact_count);
376    }
377
378    /// Validates the joint linked list.
379    pub fn validate_joints(&self, joint_nodes: &[Option<IslandNode<JointId>>]) {
380        if self.head_joint.is_none() {
381            assert!(self.tail_joint.is_none());
382            assert_eq!(self.joint_count, 0);
383            return;
384        }
385
386        assert!(self.tail_joint.is_some());
387        assert!(self.joint_count > 0);
388
389        if self.joint_count > 1 {
390            assert_ne!(self.head_joint, self.tail_joint);
391        }
392
393        let mut count = 0;
394        let mut joint_id = self.head_joint;
395
396        while let Some(id) = joint_id {
397            let joint_island = joint_nodes
398                .get(id.0 as usize)
399                .and_then(|node| node.as_ref())
400                .unwrap_or_else(|| panic!("Joint {id:?} has no island"));
401            assert_eq!(joint_island.island_id, self.id);
402
403            count += 1;
404
405            if count == self.joint_count {
406                assert_eq!(joint_id, self.tail_joint);
407            }
408
409            joint_id = joint_island.next;
410        }
411
412        assert_eq!(count, self.joint_count);
413    }
414}
415
416/// A resource for the [`PhysicsIsland`]s in the simulation.
417#[derive(Resource, Debug, Default, Clone)]
418pub struct PhysicsIslands {
419    /// The list of islands.
420    islands: StableVec<PhysicsIsland>,
421    /// The island linked-list nodes for contacts, indexed by [`ContactId`].
422    ///
423    /// Contacts that are not part of any island have a `None` entry.
424    contact_nodes: Vec<Option<IslandNode<ContactId>>>,
425    /// The island linked-list nodes for joints, indexed by [`JointId`].
426    ///
427    /// Joints that are not part of any island have a `None` entry.
428    joint_nodes: Vec<Option<IslandNode<JointId>>>,
429    /// The current island candidate for splitting.
430    ///
431    /// This is chosen based on which island with one or more constraints removed
432    /// has the largest sleep timer.
433    pub split_candidate: Option<IslandId>,
434    /// The largest [`SleepTimer`] of the split candidate.
435    ///
436    /// [`SleepTimer`]: crate::dynamics::rigid_body::sleeping::SleepTimer
437    pub split_candidate_sleep_timer: f32,
438}
439
440impl PhysicsIslands {
441    /// Creates a new [`PhysicsIsland`], calling the given `init` function before pushing the island to the list.
442    #[inline]
443    pub fn create_island_with<F>(&mut self, init: F) -> IslandId
444    where
445        F: FnOnce(&mut PhysicsIsland),
446    {
447        // Create a new island.
448        let island_id = self.next_id();
449        let mut island = PhysicsIsland::new(island_id);
450
451        // Initialize the island with the provided function.
452        init(&mut island);
453
454        // Add the island to the list.
455        IslandId(self.islands.push(island) as u32)
456    }
457
458    /// Removes a [`PhysicsIsland`] with the given ID. The island is assumed to be empty,
459    ///
460    /// # Panics
461    ///
462    /// Panics if `island_id` is out of bounds.
463    #[inline]
464    pub fn remove_island(&mut self, island_id: IslandId) -> PhysicsIsland {
465        if self.split_candidate == Some(island_id) {
466            self.split_candidate = None;
467        }
468
469        // Assume the island is empty, and remove it.
470        self.islands.remove(island_id.0 as usize)
471    }
472
473    /// Removes the [`PhysicsIsland`] with the given ID if it exists and is completely empty,
474    /// meaning it has no bodies, contacts, or joints.
475    ///
476    /// This is used to clean up islands whose teardown spans multiple operations. For example,
477    /// when a body is despawned, its constraint and island removal is deferred, so the island may
478    /// temporarily have no bodies but still reference contacts. It is removed once the deferred
479    /// contact removals drain.
480    #[inline]
481    pub fn remove_island_if_empty(&mut self, island_id: IslandId) {
482        if let Some(island) = self.islands.get(island_id.0 as usize)
483            && island.body_count == 0
484            && island.contact_count == 0
485            && island.joint_count == 0
486        {
487            self.remove_island(island_id);
488        }
489    }
490
491    /// Returns the next available island ID.
492    #[inline]
493    pub fn next_id(&self) -> IslandId {
494        IslandId(self.islands.next_push_index() as u32)
495    }
496
497    /// Returns the [`IslandNode`] linking the given contact to its island,
498    /// or `None` if the contact is not part of any island.
499    #[inline]
500    pub fn contact_node(&self, contact_id: ContactId) -> Option<&IslandNode<ContactId>> {
501        self.contact_nodes
502            .get(contact_id.0 as usize)
503            .and_then(|node| node.as_ref())
504    }
505
506    /// Returns the slice of contact [`IslandNode`]s, indexed by [`ContactId`].
507    #[cfg(feature = "validate")]
508    #[inline]
509    pub(crate) fn contact_nodes(&self) -> &[Option<IslandNode<ContactId>>] {
510        &self.contact_nodes
511    }
512
513    /// Returns the [`IslandNode`] linking the given joint to its island,
514    /// or `None` if the joint is not part of any island.
515    #[inline]
516    pub fn joint_node(&self, joint_id: JointId) -> Option<&IslandNode<JointId>> {
517        self.joint_nodes
518            .get(joint_id.0 as usize)
519            .and_then(|node| node.as_ref())
520    }
521
522    /// Returns the slice of joint [`IslandNode`]s, indexed by [`JointId`].
523    #[cfg(feature = "validate")]
524    #[inline]
525    pub(crate) fn joint_nodes(&self) -> &[Option<IslandNode<JointId>>] {
526        &self.joint_nodes
527    }
528
529    /// Returns a reference to the [`PhysicsIsland`] with the given ID.
530    #[inline]
531    pub fn get(&self, island_id: IslandId) -> Option<&PhysicsIsland> {
532        self.islands.get(island_id.0 as usize)
533    }
534
535    /// Returns a mutable reference to the [`PhysicsIsland`] with the given ID.
536    #[inline]
537    pub fn get_mut(&mut self, island_id: IslandId) -> Option<&mut PhysicsIsland> {
538        self.islands.get_mut(island_id.0 as usize)
539    }
540
541    /// Returns an iterator over all [`PhysicsIsland`]s.
542    #[inline]
543    pub fn iter(&self) -> impl Iterator<Item = &PhysicsIsland> {
544        self.islands.iter().map(|(_, island)| island)
545    }
546
547    /// Returns a mutable iterator over all [`PhysicsIsland`]s.
548    #[inline]
549    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut PhysicsIsland> {
550        self.islands.iter_mut().map(|(_, island)| island)
551    }
552
553    /// Returns the number of [`PhysicsIsland`]s.
554    #[inline]
555    pub fn len(&self) -> usize {
556        self.islands.len()
557    }
558
559    /// Returns `true` if there are no [`PhysicsIsland`]s.
560    #[inline]
561    pub fn is_empty(&self) -> bool {
562        self.islands.is_empty()
563    }
564
565    /// Adds a contact to the island manager. Returns a reference to the island that the contact was added to.
566    ///
567    /// This will merge the islands of the bodies involved in the contact,
568    /// and link the contact to the resulting island.
569    ///
570    /// Called when a touching contact is created between two bodies.
571    pub fn add_contact(
572        &mut self,
573        contact_id: ContactId,
574        body_islands: &mut Query<&mut BodyIslandNode, Or<(With<Disabled>, Without<Disabled>)>>,
575        contact_graph: &ContactGraph,
576    ) -> Option<&PhysicsIsland> {
577        let contact = contact_graph.get_edge_by_id(contact_id).unwrap();
578
579        debug_assert!(self.contact_node(contact_id).is_none());
580        debug_assert!(contact.is_touching());
581
582        let (Some(body1), Some(body2)) = (contact.body1, contact.body2) else {
583            return None;
584        };
585
586        // Merge the islands.
587        let island_id = self.merge_islands(body1, body2, body_islands);
588
589        // Ensure there is a slot for this contact's island node.
590        let contact_index = contact_id.0 as usize;
591        if contact_index >= self.contact_nodes.len() {
592            self.contact_nodes.resize_with(contact_index + 1, || None);
593        }
594
595        // Link the contact to the island.
596        let island = self
597            .islands
598            .get_mut(island_id.0 as usize)
599            .unwrap_or_else(|| {
600                panic!("Island {island_id} does not exist");
601            });
602
603        let mut contact_island = IslandNode {
604            island_id: island.id,
605            prev: None,
606            next: None,
607            is_visited: false,
608        };
609
610        if let Some(head_contact_id) = island.head_contact {
611            // Link the new contact to the head of the island.
612            contact_island.next = Some(head_contact_id);
613
614            let head_contact_island = self.contact_nodes[head_contact_id.0 as usize]
615                .as_mut()
616                .unwrap_or_else(|| panic!("Head contact {head_contact_id:?} has no island"));
617            head_contact_island.prev = Some(contact_id);
618        }
619
620        island.head_contact = Some(contact_id);
621
622        if island.tail_contact.is_none() {
623            island.tail_contact = island.head_contact;
624        }
625
626        self.contact_nodes[contact_index] = Some(contact_island);
627
628        island.contact_count += 1;
629
630        #[cfg(feature = "validate")]
631        {
632            // Validate the island.
633            island.validate(
634                &body_islands.as_readonly(),
635                &self.contact_nodes,
636                &self.joint_nodes,
637            );
638        }
639
640        Some(island)
641    }
642
643    /// Removes a contact from the island manager. Returns a reference to the island that the contact was removed from.
644    ///
645    /// This will unlink the contact from the island and update the island's contact list.
646    /// The [`PhysicsIsland::constraints_removed`] counter is incremented.
647    ///
648    /// Called when a contact is destroyed or no longer touching.
649    #[cfg_attr(
650        not(feature = "validate"),
651        expect(
652            unused_variables,
653            reason = "`body_islands` is only used with the `validate` feature"
654        )
655    )]
656    pub fn remove_contact(
657        &mut self,
658        contact_id: ContactId,
659        body_islands: &mut Query<&mut BodyIslandNode, Or<(With<Disabled>, Without<Disabled>)>>,
660    ) -> &PhysicsIsland {
661        debug_assert!(self.contact_node(contact_id).is_some());
662
663        // Remove the island node from the contact.
664        let contact_island = self.contact_nodes[contact_id.0 as usize]
665            .take()
666            .expect("Contact has no island");
667
668        // Remove the contact from the island.
669        if let Some(prev_contact_id) = contact_island.prev {
670            let prev_contact_island = self.contact_nodes[prev_contact_id.0 as usize]
671                .as_mut()
672                .expect("Previous contact has no island");
673            debug_assert!(prev_contact_island.next == Some(contact_id));
674            prev_contact_island.next = contact_island.next;
675        }
676
677        if let Some(next_contact_id) = contact_island.next {
678            let next_contact_island = self.contact_nodes[next_contact_id.0 as usize]
679                .as_mut()
680                .expect("Next contact has no island");
681            debug_assert!(next_contact_island.prev == Some(contact_id));
682            next_contact_island.prev = contact_island.prev;
683        }
684
685        let island = self
686            .islands
687            .get_mut(contact_island.island_id.0 as usize)
688            .unwrap_or_else(|| {
689                panic!("Island {} does not exist", contact_island.island_id);
690            });
691
692        if island.head_contact == Some(contact_id) {
693            // The contact is the head of the island.
694            island.head_contact = contact_island.next;
695        }
696
697        if island.tail_contact == Some(contact_id) {
698            // The contact is the tail of the island.
699            island.tail_contact = contact_island.prev;
700        }
701
702        debug_assert!(island.contact_count > 0);
703        island.contact_count -= 1;
704        island.constraints_removed += 1;
705
706        #[cfg(feature = "validate")]
707        {
708            // Validate the island.
709            island.validate(
710                &body_islands.as_readonly(),
711                &self.contact_nodes,
712                &self.joint_nodes,
713            );
714        }
715
716        island
717    }
718
719    /// Adds a joint to the island manager. Returns a reference to the island that the joint was added to.
720    ///
721    /// This will merge the islands of the bodies connected by the joint,
722    /// and link the joint to the resulting island.
723    ///
724    /// Called when a joint is created between two bodies.
725    pub fn add_joint(
726        &mut self,
727        joint_id: JointId,
728        body_islands: &mut Query<&mut BodyIslandNode, Or<(With<Disabled>, Without<Disabled>)>>,
729        joint_graph: &JointGraph,
730    ) -> Option<&PhysicsIsland> {
731        let joint = joint_graph.get_by_id(joint_id).unwrap();
732
733        debug_assert!(self.joint_node(joint_id).is_none());
734
735        // Merge the islands.
736        let island_id = self.merge_islands(joint.body1, joint.body2, body_islands);
737
738        // Ensure there is a slot for this joint's island node.
739        let joint_index = joint_id.0 as usize;
740        if joint_index >= self.joint_nodes.len() {
741            self.joint_nodes.resize_with(joint_index + 1, || None);
742        }
743
744        // Link the joint to the island.
745        let island = self
746            .islands
747            .get_mut(island_id.0 as usize)
748            .unwrap_or_else(|| {
749                panic!("Island {island_id} does not exist");
750            });
751
752        let mut joint_island = IslandNode {
753            island_id: island.id,
754            prev: None,
755            next: None,
756            is_visited: false,
757        };
758
759        if let Some(head_joint_id) = island.head_joint {
760            // Link the new joint to the head of the island.
761            joint_island.next = Some(head_joint_id);
762
763            let head_joint_island = self.joint_nodes[head_joint_id.0 as usize]
764                .as_mut()
765                .unwrap_or_else(|| panic!("Head joint {head_joint_id:?} has no island"));
766            head_joint_island.prev = Some(joint_id);
767        }
768
769        island.head_joint = Some(joint_id);
770
771        if island.tail_joint.is_none() {
772            island.tail_joint = island.head_joint;
773        }
774
775        self.joint_nodes[joint_index] = Some(joint_island);
776
777        island.joint_count += 1;
778
779        #[cfg(feature = "validate")]
780        {
781            // Validate the island.
782            island.validate(
783                &body_islands.as_readonly(),
784                &self.contact_nodes,
785                &self.joint_nodes,
786            );
787        }
788
789        Some(island)
790    }
791
792    /// Removes a joint from the island manager. Returns a reference to the island
793    /// that the joint was removed from, if the island still exists.
794    ///
795    /// This will unlink the joint from the island and update the island's joint list.
796    /// The [`PhysicsIsland::constraints_removed`] counter is incremented.
797    ///
798    /// The joint should be removed from the [`JointGraph`] in concert with calling this method.
799    ///
800    /// Called when a joint is destroyed or no longer connected to the bodies.
801    #[cfg_attr(
802        not(feature = "validate"),
803        expect(
804            unused_variables,
805            reason = "`body_islands` is only used with the `validate` feature"
806        )
807    )]
808    pub fn remove_joint(
809        &mut self,
810        joint_id: JointId,
811        body_islands: &mut Query<&mut BodyIslandNode, Or<(With<Disabled>, Without<Disabled>)>>,
812    ) -> Option<&PhysicsIsland> {
813        debug_assert!(self.joint_node(joint_id).is_some());
814
815        // Remove the island node from the joint.
816        let joint_island = self.joint_nodes[joint_id.0 as usize]
817            .take()
818            .expect("Joint has no island");
819
820        // Remove the joint from the island.
821        if let Some(prev_joint_id) = joint_island.prev {
822            let prev_joint_island = self.joint_nodes[prev_joint_id.0 as usize]
823                .as_mut()
824                .expect("Previous joint has no island");
825            debug_assert!(prev_joint_island.next == Some(joint_id));
826            prev_joint_island.next = joint_island.next;
827        }
828
829        if let Some(next_joint_id) = joint_island.next {
830            let next_joint_island = self.joint_nodes[next_joint_id.0 as usize]
831                .as_mut()
832                .expect("Next joint has no island");
833            debug_assert!(next_joint_island.prev == Some(joint_id));
834            next_joint_island.prev = joint_island.prev;
835        }
836
837        let island = self.islands.get_mut(joint_island.island_id.0 as usize)?;
838
839        if island.head_joint == Some(joint_id) {
840            // The joint is the head of the island.
841            island.head_joint = joint_island.next;
842        }
843
844        if island.tail_joint == Some(joint_id) {
845            // The joint is the tail of the island.
846            island.tail_joint = joint_island.prev;
847        }
848
849        debug_assert!(island.joint_count > 0);
850        island.joint_count -= 1;
851        island.constraints_removed += 1;
852
853        #[cfg(feature = "validate")]
854        {
855            // Validate the island.
856            island.validate(
857                &body_islands.as_readonly(),
858                &self.contact_nodes,
859                &self.joint_nodes,
860            );
861        }
862
863        Some(island)
864    }
865
866    /// Merges the [`PhysicsIsland`]s associated with the given bodies. Returns the ID of the resulting island.
867    ///
868    /// If an awake island is merged with a sleeping island, the resulting island will remain sleeping.
869    /// It is up to the caller to wake up the resulting island if needed.
870    ///
871    /// The bodies and contacts of the smaller island are transferred to the larger island,
872    /// and the smaller island is removed.
873    pub fn merge_islands(
874        &mut self,
875        body1: Entity,
876        body2: Entity,
877        body_islands: &mut Query<&mut BodyIslandNode, Or<(With<Disabled>, Without<Disabled>)>>,
878    ) -> IslandId {
879        let Ok(island_id1) = body_islands.get(body1).map(|island| island.island_id) else {
880            let island2 = body_islands.get(body2).unwrap_or_else(|_| {
881                panic!("Neither body {body1:?} nor {body2:?} is in an island");
882            });
883            return island2.island_id;
884        };
885
886        let Ok(island_id2) = body_islands.get(body2).map(|island| island.island_id) else {
887            let island1 = body_islands.get(body1).unwrap_or_else(|_| {
888                panic!("Neither body {body1:?} nor {body2:?} is in an island");
889            });
890            return island1.island_id;
891        };
892
893        if island_id1 == island_id2 {
894            // Merging an island with itself is a no-op.
895            return island_id1;
896        }
897
898        // Get the islands to merge.
899        // Keep the bigger island to reduce cache misses.
900        let [mut big, mut small] = self
901            .islands
902            .get_disjoint_mut2(island_id1.0 as usize, island_id2.0 as usize)
903            .unwrap();
904        if big.body_count < small.body_count {
905            core::mem::swap(&mut big, &mut small);
906        }
907
908        // 1. Remap IDs such that the bodies and constraints in `small` are moved to `big`.
909
910        // Bodies
911        let mut body_entity = small.head_body;
912        while let Some(entity) = body_entity {
913            let mut body_island = body_islands.get_mut(entity).unwrap();
914            body_island.island_id = big.id;
915            body_entity = body_island.next;
916        }
917
918        // Contacts
919        let mut contact_id = small.head_contact;
920        while let Some(id) = contact_id {
921            let contact_island = self.contact_nodes[id.0 as usize]
922                .as_mut()
923                .expect("Contact has no island");
924            contact_island.island_id = big.id;
925            contact_id = contact_island.next;
926        }
927
928        // Joints
929        let mut joint_id = small.head_joint;
930        while let Some(id) = joint_id {
931            let joint_island = self.joint_nodes[id.0 as usize]
932                .as_mut()
933                .expect("Joint has no island");
934            joint_island.island_id = big.id;
935            joint_id = joint_island.next;
936        }
937
938        // 2. Connect body and constraint lists such that `small` is appended to `big`.
939
940        // Bodies
941        let tail_body_id = big.tail_body.expect("Island {island_id1} has no tail body");
942        let head_body_id = small
943            .head_body
944            .expect("Island {island_id2} has no head body");
945        let [mut tail_body, mut head_body] = body_islands
946            .get_many_mut([tail_body_id, head_body_id])
947            .unwrap();
948        tail_body.next = small.head_body;
949        head_body.prev = big.tail_body;
950
951        big.tail_body = small.tail_body;
952        big.body_count += small.body_count;
953
954        // Contacts
955        if big.head_contact.is_none() {
956            // The big island has no contacts.
957            debug_assert!(big.tail_contact.is_none() && big.contact_count == 0);
958
959            big.head_contact = small.head_contact;
960            big.tail_contact = small.tail_contact;
961            big.contact_count = small.contact_count;
962        } else if small.head_contact.is_some() {
963            // Both islands have contacts.
964            debug_assert!(big.contact_count > 0);
965            debug_assert!(small.contact_count > 0);
966
967            let tail_contact_id = big.tail_contact.expect("Root island has no tail contact");
968            let head_contact_id = small.head_contact.expect("Island has no head contact");
969
970            // Connect the tail of the big island to the head of the small island.
971            let tail_contact_island = self.contact_nodes[tail_contact_id.0 as usize]
972                .as_mut()
973                .expect("Tail contact has no island");
974            debug_assert!(tail_contact_island.next.is_none());
975            tail_contact_island.next = small.head_contact;
976
977            // Connect the head of the small island to the tail of the big island.
978            let head_contact_island = self.contact_nodes[head_contact_id.0 as usize]
979                .as_mut()
980                .expect("Head contact has no island");
981            debug_assert!(head_contact_island.prev.is_none());
982            head_contact_island.prev = big.tail_contact;
983
984            big.tail_contact = small.tail_contact;
985            big.contact_count += small.contact_count;
986        }
987
988        // Joints
989        if big.head_joint.is_none() {
990            // The big island has no joints.
991            debug_assert!(big.tail_joint.is_none() && big.joint_count == 0);
992
993            big.head_joint = small.head_joint;
994            big.tail_joint = small.tail_joint;
995            big.joint_count = small.joint_count;
996        } else if small.head_joint.is_some() {
997            // Both islands have joints.
998            debug_assert!(big.joint_count > 0);
999            debug_assert!(small.joint_count > 0);
1000
1001            let tail_joint_id = big.tail_joint.expect("Root island has no tail joint");
1002            let head_joint_id = small.head_joint.expect("Island has no head joint");
1003
1004            // Connect the tail of the big island to the head of the small island.
1005            let tail_joint_island = self.joint_nodes[tail_joint_id.0 as usize]
1006                .as_mut()
1007                .expect("Tail joint has no island");
1008            debug_assert!(tail_joint_island.next.is_none());
1009            tail_joint_island.next = small.head_joint;
1010
1011            // Connect the head of the small island to the tail of the big island.
1012            let head_joint_island = self.joint_nodes[head_joint_id.0 as usize]
1013                .as_mut()
1014                .expect("Head joint has no island");
1015            debug_assert!(head_joint_island.prev.is_none());
1016            head_joint_island.prev = big.tail_joint;
1017
1018            big.tail_joint = small.tail_joint;
1019            big.joint_count += small.joint_count;
1020        }
1021
1022        // Track removed constraints.
1023        big.constraints_removed += small.constraints_removed;
1024
1025        // 3. Update the sleep state of the big island.
1026        if small.is_sleeping {
1027            // If the small island is sleeping, the big island will remain sleeping.
1028            big.is_sleeping = true;
1029            big.sleep_timer = small.sleep_timer.max(big.sleep_timer);
1030        }
1031
1032        #[cfg(feature = "validate")]
1033        {
1034            // Validate the big island.
1035            big.validate(
1036                &body_islands.as_readonly(),
1037                &self.contact_nodes,
1038                &self.joint_nodes,
1039            );
1040        }
1041
1042        // 4. Remove the small island.
1043        let big_id = big.id;
1044        let small_id = small.id;
1045        self.remove_island(small_id);
1046
1047        big_id
1048    }
1049
1050    /// Splits the [`PhysicsIsland`] associated with the given ID.
1051    ///
1052    /// Unlike merging, splitting can be deferred and done in parallel with other work.
1053    pub fn split_island(
1054        &mut self,
1055        island_id: IslandId,
1056        body_islands: &mut Query<&mut BodyIslandNode, Or<(With<Disabled>, Without<Disabled>)>>,
1057        body_colliders: &Query<&RigidBodyColliders>,
1058        contact_graph: &ContactGraph,
1059        joint_graph: &JointGraph,
1060    ) {
1061        let island = self.islands.get_mut(island_id.0 as usize).unwrap();
1062
1063        if island.is_sleeping {
1064            // Only awake islands can be split.
1065            return;
1066        }
1067
1068        if island.constraints_removed == 0 {
1069            // No constraints have been removed, so no need to split the island.
1070            return;
1071        }
1072
1073        #[cfg(feature = "validate")]
1074        {
1075            // Validate the island before splitting.
1076            island.validate(
1077                &body_islands.as_readonly(),
1078                &self.contact_nodes,
1079                &self.joint_nodes,
1080            );
1081        }
1082
1083        let body_count = island.body_count;
1084
1085        // Build a `Vec` of all body IDs in the base island.
1086        // These are seeds for the depth-first search (DFS).
1087        //
1088        // Also clear visited flags for bodies.
1089        let mut body_ids = Vec::with_capacity(body_count as usize);
1090        let mut next_body = island.head_body;
1091
1092        while let Some(body_id) = next_body {
1093            body_ids.push(body_id);
1094
1095            let mut body_island = body_islands.get_mut(body_id).unwrap();
1096
1097            // Clear visited flag.
1098            body_island.is_visited = false;
1099
1100            next_body = body_island.next;
1101        }
1102
1103        debug_assert_eq!(body_ids.len(), body_count as usize);
1104
1105        // Clear visited flags for contacts.
1106        let mut next_contact = island.head_contact;
1107        while let Some(contact_id) = next_contact {
1108            let contact_island = self.contact_nodes[contact_id.0 as usize]
1109                .as_mut()
1110                .unwrap_or_else(|| panic!("Contact {contact_id:?} has no island"));
1111
1112            // Clear visited flag.
1113            contact_island.is_visited = false;
1114
1115            next_contact = contact_island.next;
1116        }
1117
1118        // Clear visited flags for joints.
1119        let mut next_joint = island.head_joint;
1120        while let Some(joint_id) = next_joint {
1121            let joint_island = self.joint_nodes[joint_id.0 as usize]
1122                .as_mut()
1123                .unwrap_or_else(|| panic!("Joint {joint_id:?} has no island"));
1124
1125            // Clear visited flag.
1126            joint_island.is_visited = false;
1127
1128            next_joint = joint_island.next;
1129        }
1130
1131        // Destroy the base island.
1132        self.remove_island(island_id);
1133
1134        // Split the island using a depth-first search (DFS) starting from the seeds.
1135        let mut stack = Vec::with_capacity(body_ids.len());
1136        for seed_id in body_ids.into_iter() {
1137            let mut seed = body_islands.get_mut(seed_id).unwrap();
1138
1139            if seed.is_visited {
1140                // The body has already been visited.
1141                continue;
1142            }
1143
1144            seed.is_visited = true;
1145
1146            // Create a new island.
1147            let island_id = self.next_id();
1148            let mut island = PhysicsIsland::new(island_id);
1149
1150            // Initialize the stack with the seed body.
1151            stack.push(seed_id);
1152
1153            // Traverse the constraint graph using a depth-first search (DFS).
1154            while let Some(body) = stack.pop() {
1155                let mut body_island = unsafe { body_islands.get_unchecked(body).unwrap() };
1156
1157                debug_assert!(body_island.is_visited);
1158
1159                // Add the body to the new island.
1160                body_island.island_id = island_id;
1161
1162                if let Some(tail_body_id) = island.tail_body {
1163                    debug_assert_ne!(tail_body_id, body);
1164                    unsafe {
1165                        body_islands.get_unchecked(tail_body_id).unwrap().next = Some(body);
1166                    }
1167                }
1168
1169                body_island.prev = island.tail_body;
1170                body_island.next = None;
1171                island.tail_body = Some(body);
1172
1173                if island.head_body.is_none() {
1174                    island.head_body = Some(body);
1175                }
1176
1177                island.body_count += 1;
1178
1179                // Traverse the contacts of the body.
1180                // TODO: Avoid collecting here and only iterate once.
1181                let contact_edges: Vec<(ContactId, Entity)> = body_colliders
1182                    .get(body)
1183                    .iter()
1184                    .flat_map(|colliders| {
1185                        colliders.into_iter().flat_map(|collider| {
1186                            contact_graph
1187                                .contact_edges_with(collider)
1188                                .filter_map(|contact_edge| {
1189                                    if self
1190                                        .contact_node(contact_edge.id)
1191                                        .is_none_or(|node| node.is_visited)
1192                                    {
1193                                        // Only consider contacts that generate constraints
1194                                        // and have not been visited yet.
1195                                        return None;
1196                                    }
1197
1198                                    // TODO: Remove this once the contact graph is reworked to only have rigid body collisions.
1199                                    let (Some(body1), Some(body2)) =
1200                                        (contact_edge.body1, contact_edge.body2)
1201                                    else {
1202                                        // Only consider contacts with two bodies.
1203                                        return None;
1204                                    };
1205
1206                                    Some((
1207                                        contact_edge.id,
1208                                        if body1 == body { body2 } else { body1 },
1209                                    ))
1210                                })
1211                        })
1212                    })
1213                    .collect();
1214
1215                for (contact_id, other_body) in contact_edges {
1216                    // Maybe add the other body to the stack.
1217                    if let Ok(mut other_body_island) = body_islands.get_mut(other_body)
1218                        && !other_body_island.is_visited
1219                    {
1220                        stack.push(other_body);
1221                        other_body_island.is_visited = true;
1222                    }
1223
1224                    // Add the contact to the new island.
1225                    if let Some(tail_contact_id) = island.tail_contact {
1226                        let tail_contact_island = self.contact_nodes[tail_contact_id.0 as usize]
1227                            .as_mut()
1228                            .unwrap_or_else(|| {
1229                                panic!("Tail contact {tail_contact_id:?} has no island")
1230                            });
1231                        tail_contact_island.next = Some(contact_id);
1232                    }
1233
1234                    let contact_island = self.contact_nodes[contact_id.0 as usize]
1235                        .as_mut()
1236                        .unwrap_or_else(|| panic!("Contact {contact_id:?} has no island"));
1237
1238                    contact_island.is_visited = true;
1239                    contact_island.island_id = island_id;
1240                    contact_island.prev = island.tail_contact;
1241                    contact_island.next = None;
1242
1243                    island.tail_contact = Some(contact_id);
1244
1245                    if island.head_contact.is_none() {
1246                        island.head_contact = Some(contact_id);
1247                    }
1248
1249                    island.contact_count += 1;
1250                }
1251
1252                // Traverse the joints of the body.
1253                let joint_edges: Vec<(JointId, Entity)> = joint_graph
1254                    .joints_of(body)
1255                    .filter_map(|joint_edge| {
1256                        if self
1257                            .joint_node(joint_edge.id)
1258                            .is_none_or(|node| node.is_visited)
1259                        {
1260                            // Only consider joints that have not been visited yet.
1261                            return None;
1262                        }
1263
1264                        Some((
1265                            joint_edge.id,
1266                            if joint_edge.body1 == body {
1267                                joint_edge.body2
1268                            } else {
1269                                joint_edge.body1
1270                            },
1271                        ))
1272                    })
1273                    .collect();
1274
1275                for (joint_id, other_body) in joint_edges {
1276                    // Maybe add the other body to the stack.
1277                    if let Ok(mut other_body_island) = body_islands.get_mut(other_body)
1278                        && !other_body_island.is_visited
1279                    {
1280                        stack.push(other_body);
1281                        other_body_island.is_visited = true;
1282                    }
1283
1284                    // Add the joint to the new island.
1285                    if let Some(tail_joint_id) = island.tail_joint {
1286                        let tail_joint_island = self.joint_nodes[tail_joint_id.0 as usize]
1287                            .as_mut()
1288                            .unwrap_or_else(|| {
1289                                panic!("Tail joint {tail_joint_id:?} has no island")
1290                            });
1291                        tail_joint_island.next = Some(joint_id);
1292                    }
1293
1294                    let joint_island = self.joint_nodes[joint_id.0 as usize]
1295                        .as_mut()
1296                        .unwrap_or_else(|| panic!("Joint {joint_id:?} has no island"));
1297
1298                    joint_island.is_visited = true;
1299                    joint_island.island_id = island_id;
1300                    joint_island.prev = island.tail_joint;
1301                    joint_island.next = None;
1302
1303                    island.tail_joint = Some(joint_id);
1304
1305                    if island.head_joint.is_none() {
1306                        island.head_joint = Some(joint_id);
1307                    }
1308
1309                    island.joint_count += 1;
1310                }
1311            }
1312
1313            #[cfg(feature = "validate")]
1314            {
1315                // Validate the new island.
1316                island.validate(
1317                    &body_islands.as_readonly(),
1318                    &self.contact_nodes,
1319                    &self.joint_nodes,
1320                );
1321            }
1322
1323            // Add the new island to the list.
1324            self.islands.push(island);
1325        }
1326    }
1327}
1328
1329/// A node in a linked list in a [`PhysicsIsland`].
1330#[derive(Clone, Debug, PartialEq, Eq, Reflect)]
1331#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1332pub struct IslandNode<Id> {
1333    /// The ID of the island that the node belongs to.
1334    pub(crate) island_id: IslandId,
1335    /// The ID of the previous node in the linked list.
1336    pub(crate) prev: Option<Id>,
1337    /// The ID of the next node in the linked list.
1338    pub(crate) next: Option<Id>,
1339    /// A flag to mark the node as visited during depth-first traversal (DFS) for island splitting.
1340    pub(crate) is_visited: bool,
1341}
1342
1343impl<Id> IslandNode<Id> {
1344    /// A placeholder [`IslandNode`] that has not been initialized yet.
1345    pub const PLACEHOLDER: Self = Self::new(IslandId::PLACEHOLDER);
1346
1347    /// Creates a new [`IslandNode`] with the given island ID.
1348    #[inline]
1349    pub const fn new(island_id: IslandId) -> Self {
1350        Self {
1351            island_id,
1352            prev: None,
1353            next: None,
1354            is_visited: false,
1355        }
1356    }
1357
1358    /// Returns the [`IslandId`] of the island that the node belongs to.
1359    #[inline]
1360    pub const fn island_id(&self) -> IslandId {
1361        self.island_id
1362    }
1363}
1364
1365impl<Id> Default for IslandNode<Id> {
1366    fn default() -> Self {
1367        Self::PLACEHOLDER
1368    }
1369}
1370
1371impl<Id: Copy> Copy for IslandNode<Id> {}
1372
1373/// A component that stores [`PhysicsIsland`] connectivity data for a rigid body.
1374#[derive(Component, Clone, Debug, Default, Deref, DerefMut, PartialEq, Eq, Reflect)]
1375#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1376#[component(
1377    on_add = BodyIslandNode::on_add,
1378    on_remove = BodyIslandNode::on_remove,
1379    clone_behavior = Custom(clone_placeholder_body_island_node),
1380)]
1381pub struct BodyIslandNode(IslandNode<Entity>);
1382
1383// Avoid incorrect prev/next links when cloning a rigid body
1384fn clone_placeholder_body_island_node(_source: &SourceComponent, ctx: &mut ComponentCloneCtx) {
1385    ctx.write_target_component(BodyIslandNode::default());
1386}
1387
1388impl BodyIslandNode {
1389    /// Creates a new [`BodyIslandNode`] with the given island ID.
1390    #[inline]
1391    pub const fn new(island_id: IslandId) -> Self {
1392        Self(IslandNode::new(island_id))
1393    }
1394
1395    // Initialize a new island when `BodyIslandNode` is added to a body.
1396    fn on_add(mut world: DeferredWorld, ctx: HookContext) {
1397        // Create a new island for the body.
1398        let mut islands = world.resource_mut::<PhysicsIslands>();
1399        let island_id = islands.create_island_with(|island| {
1400            island.head_body = Some(ctx.entity);
1401            island.tail_body = Some(ctx.entity);
1402            island.body_count = 1;
1403        });
1404
1405        // Set the island ID for the body.
1406        let mut body_island = world.get_mut::<BodyIslandNode>(ctx.entity).unwrap();
1407        body_island.island_id = island_id;
1408    }
1409
1410    // Remove the body from the island when `BodyIslandNode` is removed.
1411    fn on_remove(mut world: DeferredWorld, ctx: HookContext) {
1412        let body_island = world.get::<BodyIslandNode>(ctx.entity).unwrap();
1413        let island_id = body_island.island_id;
1414        let prev_body_entity = body_island.prev;
1415        let next_body_entity = body_island.next;
1416
1417        // Fix the linked list of bodies in the island.
1418        if let Some(entity) = prev_body_entity {
1419            let mut prev_body_island = world.get_mut::<BodyIslandNode>(entity).unwrap();
1420            prev_body_island.next = next_body_entity;
1421        }
1422        if let Some(entity) = next_body_entity {
1423            let mut next_body_island = world.get_mut::<BodyIslandNode>(entity).unwrap();
1424            next_body_island.prev = prev_body_entity;
1425        }
1426
1427        let mut islands = world.resource_mut::<PhysicsIslands>();
1428        let island = islands
1429            .get_mut(island_id)
1430            .unwrap_or_else(|| panic!("Island {island_id} does not exist"));
1431
1432        debug_assert!(island.body_count > 0);
1433        island.body_count -= 1;
1434
1435        // Update the head and tail of the island's body list. The body may be both the head and the
1436        // tail (if it was the only body), so these are handled independently.
1437        if island.head_body == Some(ctx.entity) {
1438            island.head_body = next_body_entity;
1439        }
1440        if island.tail_body == Some(ctx.entity) {
1441            island.tail_body = prev_body_entity;
1442        }
1443
1444        // Remove the island if it is now completely empty.
1445        //
1446        // Note that it may still reference contacts whose removal was deferred.
1447        // In that case, the island is kept and removed once those deferred removals drain.
1448        islands.remove_island_if_empty(island_id);
1449
1450        #[cfg(feature = "validate")]
1451        let island_removed = islands.get(island_id).is_none();
1452
1453        #[cfg(feature = "validate")]
1454        if !island_removed {
1455            // Validate the island.
1456            world.commands().queue(move |world: &mut World| {
1457                use bevy::ecs::system::RunSystemOnce;
1458                // TODO: This is probably quite inefficient.
1459                let _ = world.run_system_once(
1460                    move |bodies: Query<
1461                        &BodyIslandNode,
1462                        Or<(With<Disabled>, Without<Disabled>)>,
1463                    >,
1464                          islands: Res<PhysicsIslands>| {
1465                        let island = islands
1466                            .get(island_id)
1467                            .unwrap_or_else(|| panic!("Island {island_id} does not exist"));
1468                        island.validate(&bodies, islands.contact_nodes(), islands.joint_nodes());
1469                    },
1470                );
1471            });
1472        }
1473    }
1474}