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