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