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;
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
737    /// that the joint was removed from, if the island still exists.
738    ///
739    /// This will unlink the joint from the island and update the island's joint list.
740    /// The [`PhysicsIsland::constraints_removed`] counter is incremented.
741    ///
742    /// The joint should be removed from the [`JointGraph`] in concert with calling this method.
743    ///
744    /// Called when a joint is destroyed or no longer connected to the bodies.
745    #[expect(
746        unused_variables,
747        reason = "additional parameters are needed with the `validate` feature"
748    )]
749    pub fn remove_joint(
750        &mut self,
751        joint_id: JointId,
752        body_islands: &mut Query<&mut BodyIslandNode, Or<(With<Disabled>, Without<Disabled>)>>,
753        contact_graph: &ContactGraph,
754        joint_graph: &mut JointGraph,
755    ) -> Option<&PhysicsIsland> {
756        let joint = joint_graph.get_mut_by_id(joint_id).unwrap();
757
758        debug_assert!(joint.island.island_id != IslandId::PLACEHOLDER);
759
760        // Remove the island from the joint edge.
761        let joint_island = joint.island;
762
763        // Remove the joint from the island.
764        if let Some(prev_joint_id) = joint_island.prev {
765            let prev_joint = joint_graph.get_mut_by_id(prev_joint_id).unwrap();
766            let prev_island = &mut prev_joint.island;
767            debug_assert!(prev_island.next == Some(joint_id));
768            prev_island.next = joint_island.next;
769        }
770
771        if let Some(next_joint_id) = joint_island.next {
772            let next_joint = joint_graph.get_mut_by_id(next_joint_id).unwrap();
773            let next_island = &mut next_joint.island;
774            debug_assert!(next_island.prev == Some(joint_id));
775            next_island.prev = joint_island.prev;
776        }
777
778        let island = self.islands.get_mut(joint_island.island_id.0 as usize)?;
779
780        if island.head_joint == Some(joint_id) {
781            // The joint is the head of the island.
782            island.head_joint = joint_island.next;
783        }
784
785        if island.tail_joint == Some(joint_id) {
786            // The joint is the tail of the island.
787            island.tail_joint = joint_island.prev;
788        }
789
790        debug_assert!(island.joint_count > 0);
791        island.joint_count -= 1;
792        island.constraints_removed += 1;
793
794        #[cfg(feature = "validate")]
795        {
796            // Validate the island.
797            island.validate(
798                &body_islands.as_readonly(),
799                &contact_graph.edges,
800                joint_graph,
801            );
802        }
803
804        Some(island)
805    }
806
807    /// Merges the [`PhysicsIsland`]s associated with the given bodies. Returns the ID of the resulting island.
808    ///
809    /// If an awake island is merged with a sleeping island, the resulting island will remain sleeping.
810    /// It is up to the caller to wake up the resulting island if needed.
811    ///
812    /// The bodies and contacts of the smaller island are transferred to the larger island,
813    /// and the smaller island is removed.
814    pub fn merge_islands(
815        &mut self,
816        body1: Entity,
817        body2: Entity,
818        body_islands: &mut Query<&mut BodyIslandNode, Or<(With<Disabled>, Without<Disabled>)>>,
819        contact_graph: &mut ContactGraph,
820        joint_graph: &mut JointGraph,
821    ) -> IslandId {
822        let Ok(island_id1) = body_islands.get(body1).map(|island| island.island_id) else {
823            let island2 = body_islands.get(body2).unwrap_or_else(|_| {
824                panic!("Neither body {body1:?} nor {body2:?} is in an island");
825            });
826            return island2.island_id;
827        };
828
829        let Ok(island_id2) = body_islands.get(body2).map(|island| island.island_id) else {
830            let island1 = body_islands.get(body1).unwrap_or_else(|_| {
831                panic!("Neither body {body1:?} nor {body2:?} is in an island");
832            });
833            return island1.island_id;
834        };
835
836        if island_id1 == island_id2 {
837            // Merging an island with itself is a no-op.
838            return island_id1;
839        }
840
841        // Get the islands to merge.
842        // Keep the bigger island to reduce cache misses.
843        let [mut big, mut small] = self
844            .islands
845            .get_disjoint_mut([island_id1.0 as usize, island_id2.0 as usize])
846            .unwrap();
847        if big.body_count < small.body_count {
848            core::mem::swap(&mut big, &mut small);
849        }
850
851        // 1. Remap IDs such that the bodies and constraints in `small` are moved to `big`.
852
853        // Bodies
854        let mut body_entity = small.head_body;
855        while let Some(entity) = body_entity {
856            let mut body_island = body_islands.get_mut(entity).unwrap();
857            body_island.island_id = big.id;
858            body_entity = body_island.next;
859        }
860
861        // Contacts
862        let mut contact_id = small.head_contact;
863        while let Some(id) = contact_id {
864            let contact = contact_graph.get_edge_mut_by_id(id).unwrap();
865            let contact_island = contact.island.as_mut().expect("Contact has no island");
866            contact_island.island_id = big.id;
867            contact_id = contact_island.next;
868        }
869
870        // Joints
871        let mut joint_id = small.head_joint;
872        while let Some(id) = joint_id {
873            let joint = joint_graph.get_mut_by_id(id).unwrap();
874            joint.island.island_id = big.id;
875            joint_id = joint.island.next;
876        }
877
878        // 2. Connect body and constraint lists such that `small` is appended to `big`.
879
880        // Bodies
881        let tail_body_id = big.tail_body.expect("Island {island_id1} has no tail body");
882        let head_body_id = small
883            .head_body
884            .expect("Island {island_id2} has no head body");
885        let [mut tail_body, mut head_body] = body_islands
886            .get_many_mut([tail_body_id, head_body_id])
887            .unwrap();
888        tail_body.next = small.head_body;
889        head_body.prev = big.tail_body;
890
891        big.tail_body = small.tail_body;
892        big.body_count += small.body_count;
893
894        // Contacts
895        if big.head_contact.is_none() {
896            // The big island has no contacts.
897            debug_assert!(big.tail_contact.is_none() && big.contact_count == 0);
898
899            big.head_contact = small.head_contact;
900            big.tail_contact = small.tail_contact;
901            big.contact_count = small.contact_count;
902        } else if small.head_contact.is_some() {
903            // Both islands have contacts.
904            debug_assert!(big.contact_count > 0);
905            debug_assert!(small.contact_count > 0);
906
907            let tail_contact_id = big.tail_contact.expect("Root island has no tail contact");
908            let head_contact_id = small.head_contact.expect("Island has no head contact");
909
910            // Connect the tail of the big island to the head of the small island.
911            let tail_contact = contact_graph.get_edge_mut_by_id(tail_contact_id).unwrap();
912            let tail_contact_island = tail_contact
913                .island
914                .as_mut()
915                .expect("Tail contact has no island");
916            debug_assert!(tail_contact_island.next.is_none());
917            tail_contact_island.next = small.head_contact;
918
919            // Connect the head of the small island to the tail of the big island.
920            let head_contact = contact_graph.get_edge_mut_by_id(head_contact_id).unwrap();
921            let head_contact_island = head_contact
922                .island
923                .as_mut()
924                .expect("Head contact has no island");
925            debug_assert!(head_contact_island.prev.is_none());
926            head_contact_island.prev = big.tail_contact;
927
928            big.tail_contact = small.tail_contact;
929            big.contact_count += small.contact_count;
930        }
931
932        // Joints
933        if big.head_joint.is_none() {
934            // The big island has no joints.
935            debug_assert!(big.tail_joint.is_none() && big.joint_count == 0);
936
937            big.head_joint = small.head_joint;
938            big.tail_joint = small.tail_joint;
939            big.joint_count = small.joint_count;
940        } else if small.head_joint.is_some() {
941            // Both islands have joints.
942            debug_assert!(big.joint_count > 0);
943            debug_assert!(small.joint_count > 0);
944
945            let tail_joint_id = big.tail_joint.expect("Root island has no tail joint");
946            let head_joint_id = small.head_joint.expect("Island has no head joint");
947
948            // Connect the tail of the big island to the head of the small island.
949            let tail_joint = joint_graph.get_mut_by_id(tail_joint_id).unwrap();
950            let tail_island = &mut tail_joint.island;
951            debug_assert!(tail_island.next.is_none());
952            tail_island.next = small.head_joint;
953
954            // Connect the head of the small island to the tail of the big island.
955            let head_joint = joint_graph.get_mut_by_id(head_joint_id).unwrap();
956            let head_island = &mut head_joint.island;
957            debug_assert!(head_island.prev.is_none());
958            head_island.prev = big.tail_joint;
959
960            big.tail_joint = small.tail_joint;
961            big.joint_count += small.joint_count;
962        }
963
964        // Track removed constraints.
965        big.constraints_removed += small.constraints_removed;
966
967        // 3. Update the sleep state of the big island.
968        if small.is_sleeping {
969            // If the small island is sleeping, the big island will remain sleeping.
970            big.is_sleeping = true;
971            big.sleep_timer = small.sleep_timer.max(big.sleep_timer);
972        }
973
974        #[cfg(feature = "validate")]
975        {
976            // Validate the big island.
977            big.validate(
978                &body_islands.as_readonly(),
979                &contact_graph.edges,
980                joint_graph,
981            );
982        }
983
984        // 4. Remove the small island.
985        let big_id = big.id;
986        let small_id = small.id;
987        self.remove_island(small_id);
988
989        big_id
990    }
991
992    /// Splits the [`PhysicsIsland`] associated with the given ID.
993    ///
994    /// Unlike merging, splitting can be deferred and done in parallel with other work.
995    pub fn split_island(
996        &mut self,
997        island_id: IslandId,
998        body_islands: &mut Query<&mut BodyIslandNode, Or<(With<Disabled>, Without<Disabled>)>>,
999        body_colliders: &Query<&RigidBodyColliders>,
1000        contact_graph: &mut ContactGraph,
1001        joint_graph: &mut JointGraph,
1002    ) {
1003        let island = self.islands.get_mut(island_id.0 as usize).unwrap();
1004
1005        if island.is_sleeping {
1006            // Only awake islands can be split.
1007            return;
1008        }
1009
1010        if island.constraints_removed == 0 {
1011            // No constraints have been removed, so no need to split the island.
1012            return;
1013        }
1014
1015        #[cfg(feature = "validate")]
1016        {
1017            // Validate the island before splitting.
1018            island.validate(
1019                &body_islands.as_readonly(),
1020                &contact_graph.edges,
1021                joint_graph,
1022            );
1023        }
1024
1025        let body_count = island.body_count;
1026
1027        // Build a `Vec` of all body IDs in the base island.
1028        // These are seeds for the depth-first search (DFS).
1029        //
1030        // Also clear visited flags for bodies.
1031        let mut body_ids = Vec::with_capacity(body_count as usize);
1032        let mut next_body = island.head_body;
1033
1034        while let Some(body_id) = next_body {
1035            body_ids.push(body_id);
1036
1037            let mut body_island = body_islands.get_mut(body_id).unwrap();
1038
1039            // Clear visited flag.
1040            body_island.is_visited = false;
1041
1042            next_body = body_island.next;
1043        }
1044
1045        debug_assert_eq!(body_ids.len(), body_count as usize);
1046
1047        // Clear visited flags for contacts.
1048        let mut next_contact = island.head_contact;
1049        while let Some(contact_id) = next_contact {
1050            let contact = contact_graph.get_edge_mut_by_id(contact_id).unwrap();
1051            let contact_island = contact
1052                .island
1053                .as_mut()
1054                .unwrap_or_else(|| panic!("Contact {contact_id:?} has no island"));
1055
1056            // Clear visited flag.
1057            contact_island.is_visited = false;
1058
1059            next_contact = contact_island.next;
1060        }
1061
1062        // Clear visited flags for joints.
1063        let mut next_joint = island.head_joint;
1064        while let Some(joint_id) = next_joint {
1065            let joint = joint_graph.get_mut_by_id(joint_id).unwrap();
1066            let joint_island = &mut joint.island;
1067
1068            // Clear visited flag.
1069            joint_island.is_visited = false;
1070
1071            next_joint = joint_island.next;
1072        }
1073
1074        // Destroy the base island.
1075        self.remove_island(island_id);
1076
1077        // Split the island using a depth-first search (DFS) starting from the seeds.
1078        let mut stack = Vec::with_capacity(body_ids.len());
1079        for seed_id in body_ids.into_iter() {
1080            let mut seed = body_islands.get_mut(seed_id).unwrap();
1081
1082            if seed.is_visited {
1083                // The body has already been visited.
1084                continue;
1085            }
1086
1087            seed.is_visited = true;
1088
1089            // Create a new island.
1090            let island_id = self.next_id();
1091            let mut island = PhysicsIsland::new(island_id);
1092
1093            // Initialize the stack with the seed body.
1094            stack.push(seed_id);
1095
1096            // Traverse the constraint graph using a depth-first search (DFS).
1097            while let Some(body) = stack.pop() {
1098                let mut body_island = unsafe { body_islands.get_unchecked(body).unwrap() };
1099
1100                debug_assert!(body_island.is_visited);
1101
1102                // Add the body to the new island.
1103                body_island.island_id = island_id;
1104
1105                if let Some(tail_body_id) = island.tail_body {
1106                    debug_assert_ne!(tail_body_id, body);
1107                    unsafe {
1108                        body_islands.get_unchecked(tail_body_id).unwrap().next = Some(body);
1109                    }
1110                }
1111
1112                body_island.prev = island.tail_body;
1113                body_island.next = None;
1114                island.tail_body = Some(body);
1115
1116                if island.head_body.is_none() {
1117                    island.head_body = Some(body);
1118                }
1119
1120                island.body_count += 1;
1121
1122                // Traverse the contacts of the body.
1123                // TODO: Avoid collecting here and only iterate once.
1124                let contact_edges: Vec<(ContactId, Entity)> = body_colliders
1125                    .get(body)
1126                    .iter()
1127                    .flat_map(|colliders| {
1128                        colliders.into_iter().flat_map(|collider| {
1129                            contact_graph
1130                                .contact_edges_with(collider)
1131                                .filter_map(|contact_edge| {
1132                                    if contact_edge.island.is_some_and(|island| island.is_visited) {
1133                                        // Only consider contacts that have not been visited yet.
1134                                        return None;
1135                                    }
1136
1137                                    if contact_edge.constraint_handles.is_empty() {
1138                                        // Only consider touching contacts.
1139                                        return None;
1140                                    }
1141
1142                                    // TODO: Remove this once the contact graph is reworked to only have rigid body collisions.
1143                                    let (Some(body1), Some(body2)) =
1144                                        (contact_edge.body1, contact_edge.body2)
1145                                    else {
1146                                        // Only consider contacts with two bodies.
1147                                        return None;
1148                                    };
1149
1150                                    Some((
1151                                        contact_edge.id,
1152                                        if body1 == body { body2 } else { body1 },
1153                                    ))
1154                                })
1155                        })
1156                    })
1157                    .collect();
1158
1159                for (contact_id, other_body) in contact_edges {
1160                    // Maybe add the other body to the stack.
1161                    if let Ok(mut other_body_island) = body_islands.get_mut(other_body)
1162                        && !other_body_island.is_visited
1163                    {
1164                        stack.push(other_body);
1165                        other_body_island.is_visited = true;
1166                    }
1167
1168                    // Add the contact to the new island.
1169                    if let Some(tail_contact_id) = island.tail_contact {
1170                        let tail_contact =
1171                            contact_graph.get_edge_mut_by_id(tail_contact_id).unwrap();
1172                        let tail_contact_island =
1173                            tail_contact.island.as_mut().unwrap_or_else(|| {
1174                                panic!("Tail contact {tail_contact_id:?} has no island")
1175                            });
1176                        tail_contact_island.next = Some(contact_id);
1177                    }
1178
1179                    let contact_edge = contact_graph.get_edge_mut_by_id(contact_id).unwrap();
1180                    let contact_island = contact_edge
1181                        .island
1182                        .as_mut()
1183                        .unwrap_or_else(|| panic!("Contact {contact_id:?} has no island"));
1184
1185                    contact_island.is_visited = true;
1186                    contact_island.island_id = island_id;
1187                    contact_island.prev = island.tail_contact;
1188                    contact_island.next = None;
1189
1190                    island.tail_contact = Some(contact_id);
1191
1192                    if island.head_contact.is_none() {
1193                        island.head_contact = Some(contact_id);
1194                    }
1195
1196                    island.contact_count += 1;
1197                }
1198
1199                // Traverse the joints of the body.
1200                let joint_edges: Vec<(JointId, Entity)> = joint_graph
1201                    .joints_of(body)
1202                    .filter_map(|joint_edge| {
1203                        if joint_edge.island.is_visited {
1204                            // Only consider joints that have not been visited yet.
1205                            return None;
1206                        }
1207
1208                        Some((
1209                            joint_edge.id,
1210                            if joint_edge.body1 == body {
1211                                joint_edge.body2
1212                            } else {
1213                                joint_edge.body1
1214                            },
1215                        ))
1216                    })
1217                    .collect();
1218
1219                for (joint_id, other_body) in joint_edges {
1220                    // Maybe add the other body to the stack.
1221                    if let Ok(mut other_body_island) = body_islands.get_mut(other_body)
1222                        && !other_body_island.is_visited
1223                    {
1224                        stack.push(other_body);
1225                        other_body_island.is_visited = true;
1226                    }
1227
1228                    // Add the joint to the new island.
1229                    if let Some(tail_joint_id) = island.tail_joint {
1230                        let tail_joint = joint_graph.get_mut_by_id(tail_joint_id).unwrap();
1231                        let tail_island = &mut tail_joint.island;
1232                        tail_island.next = Some(joint_id);
1233                    }
1234
1235                    let joint = joint_graph.get_mut_by_id(joint_id).unwrap();
1236                    let joint_island = &mut joint.island;
1237
1238                    joint_island.is_visited = true;
1239                    joint_island.island_id = island_id;
1240                    joint_island.prev = island.tail_joint;
1241                    joint_island.next = None;
1242
1243                    island.tail_joint = Some(joint_id);
1244
1245                    if island.head_joint.is_none() {
1246                        island.head_joint = Some(joint_id);
1247                    }
1248
1249                    island.joint_count += 1;
1250                }
1251            }
1252
1253            #[cfg(feature = "validate")]
1254            {
1255                // Validate the new island.
1256                island.validate(
1257                    &body_islands.as_readonly(),
1258                    &contact_graph.edges,
1259                    joint_graph,
1260                );
1261            }
1262
1263            // Add the new island to the list.
1264            self.islands.insert(island);
1265        }
1266    }
1267}
1268
1269/// A node in a linked list in a [`PhysicsIsland`].
1270#[derive(Clone, Debug, PartialEq, Eq, Reflect)]
1271#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1272pub struct IslandNode<Id> {
1273    /// The ID of the island that the node belongs to.
1274    pub(crate) island_id: IslandId,
1275    /// The ID of the previous node in the linked list.
1276    pub(crate) prev: Option<Id>,
1277    /// The ID of the next node in the linked list.
1278    pub(crate) next: Option<Id>,
1279    /// A flag to mark the node as visited during depth-first traversal (DFS) for island splitting.
1280    pub(crate) is_visited: bool,
1281}
1282
1283impl<Id> IslandNode<Id> {
1284    /// A placeholder [`IslandNode`] that has not been initialized yet.
1285    pub const PLACEHOLDER: Self = Self::new(IslandId::PLACEHOLDER);
1286
1287    /// Creates a new [`IslandNode`] with the given island ID.
1288    #[inline]
1289    pub const fn new(island_id: IslandId) -> Self {
1290        Self {
1291            island_id,
1292            prev: None,
1293            next: None,
1294            is_visited: false,
1295        }
1296    }
1297
1298    /// Returns the [`IslandId`] of the island that the node belongs to.
1299    #[inline]
1300    pub const fn island_id(&self) -> IslandId {
1301        self.island_id
1302    }
1303}
1304
1305impl<Id> Default for IslandNode<Id> {
1306    fn default() -> Self {
1307        Self::PLACEHOLDER
1308    }
1309}
1310
1311impl<Id: Copy> Copy for IslandNode<Id> {}
1312
1313/// A component that stores [`PhysicsIsland`] connectivity data for a rigid body.
1314#[derive(Component, Clone, Debug, Default, Deref, DerefMut, PartialEq, Eq, Reflect)]
1315#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
1316#[component(on_add = BodyIslandNode::on_add, on_remove = BodyIslandNode::on_remove)]
1317pub struct BodyIslandNode(IslandNode<Entity>);
1318
1319impl BodyIslandNode {
1320    /// Creates a new [`BodyIslandNode`] with the given island ID.
1321    #[inline]
1322    pub const fn new(island_id: IslandId) -> Self {
1323        Self(IslandNode::new(island_id))
1324    }
1325
1326    // Initialize a new island when `BodyIslandNode` is added to a body.
1327    fn on_add(mut world: DeferredWorld, ctx: HookContext) {
1328        // Create a new island for the body.
1329        let mut islands = world.resource_mut::<PhysicsIslands>();
1330        let island_id = islands.create_island_with(|island| {
1331            island.head_body = Some(ctx.entity);
1332            island.tail_body = Some(ctx.entity);
1333            island.body_count = 1;
1334        });
1335
1336        // Set the island ID for the body.
1337        let mut body_island = world.get_mut::<BodyIslandNode>(ctx.entity).unwrap();
1338        body_island.island_id = island_id;
1339    }
1340
1341    // Remove the body from the island when `BodyIslandNode` is removed.
1342    fn on_remove(mut world: DeferredWorld, ctx: HookContext) {
1343        let body_island = world.get::<BodyIslandNode>(ctx.entity).unwrap();
1344        let island_id = body_island.island_id;
1345        let prev_body_entity = body_island.prev;
1346        let next_body_entity = body_island.next;
1347
1348        // Fix the linked list of bodies in the island.
1349        if let Some(entity) = prev_body_entity {
1350            let mut prev_body_island = world.get_mut::<BodyIslandNode>(entity).unwrap();
1351            prev_body_island.next = next_body_entity;
1352        }
1353        if let Some(entity) = next_body_entity {
1354            let mut next_body_island = world.get_mut::<BodyIslandNode>(entity).unwrap();
1355            next_body_island.prev = prev_body_entity;
1356        }
1357
1358        let mut islands = world.resource_mut::<PhysicsIslands>();
1359        let island = islands
1360            .get_mut(island_id)
1361            .unwrap_or_else(|| panic!("Island {island_id} does not exist"));
1362
1363        debug_assert!(island.body_count > 0);
1364        island.body_count -= 1;
1365
1366        #[cfg(feature = "validate")]
1367        let mut island_removed = false;
1368
1369        if island.head_body == Some(ctx.entity) {
1370            island.head_body = next_body_entity;
1371
1372            if island.head_body.is_none() {
1373                // The island is empty. Remove it.
1374                debug_assert!(island.tail_body == Some(ctx.entity));
1375                debug_assert!(island.body_count == 0);
1376                debug_assert!(island.contact_count == 0);
1377
1378                world
1379                    .resource_mut::<PhysicsIslands>()
1380                    .remove_island(island_id);
1381
1382                #[cfg(feature = "validate")]
1383                {
1384                    island_removed = true;
1385                }
1386            }
1387        } else if island.tail_body == Some(ctx.entity) {
1388            island.tail_body = prev_body_entity;
1389        }
1390
1391        #[cfg(feature = "validate")]
1392        if !island_removed {
1393            // Validate the island.
1394            world.commands().queue(move |world: &mut World| {
1395                use bevy::ecs::system::RunSystemOnce;
1396                // TODO: This is probably quite inefficient.
1397                let _ = world.run_system_once(
1398                    move |bodies: Query<
1399                        &BodyIslandNode,
1400                        Or<(With<Disabled>, Without<Disabled>)>,
1401                    >,
1402                          islands: Res<PhysicsIslands>,
1403                          contact_graph: Res<ContactGraph>,
1404                          joint_graph: Res<JointGraph>| {
1405                        let island = islands
1406                            .get(island_id)
1407                            .unwrap_or_else(|| panic!("Island {island_id} does not exist"));
1408                        island.validate(&bodies, &contact_graph.edges, &joint_graph);
1409                    },
1410                );
1411            });
1412        }
1413    }
1414}