Skip to main content

box2d_rust/
island.rs

1// Port of the island data model from box2d-cpp-reference/src/island.h.
2// Logic from island.c lands in a later bring-up commit.
3//
4// SPDX-FileCopyrightText: 2023 Erin Catto
5// SPDX-License-Identifier: MIT
6
7use crate::core::NULL_INDEX;
8
9/// Cached contact data stored in the island for fast contiguous iteration.
10/// Avoids touching Contact during union-find in island splitting.
11/// (b2ContactLink)
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub struct ContactLink {
14    pub contact_id: i32,
15    pub body_id_a: i32,
16    pub body_id_b: i32,
17}
18
19/// Cached joint data stored in the island for fast contiguous iteration.
20/// (b2JointLink)
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub struct JointLink {
23    pub joint_id: i32,
24    pub body_id_a: i32,
25    pub body_id_b: i32,
26}
27
28/// Persistent island for awake bodies, joints, and contacts. Contacts are
29/// touching. Contacts and joints may connect to static bodies, but static
30/// bodies are not in the island. (b2Island)
31///
32/// <https://en.wikipedia.org/wiki/Component_(graph_theory)>
33/// <https://en.wikipedia.org/wiki/Dynamic_connectivity>
34#[derive(Debug, Clone)]
35pub struct Island {
36    /// index of solver set stored in World. May be NULL_INDEX.
37    pub set_index: i32,
38
39    /// island index within set. May be NULL_INDEX.
40    pub local_index: i32,
41
42    pub island_id: i32,
43
44    /// How many contacts have been removed from this island. Used to determine
45    /// if an island is a candidate for splitting.
46    pub constraint_remove_count: i32,
47
48    pub bodies: Vec<i32>,
49
50    /// Contacts and joints that belong to this island. May connect to static
51    /// bodies not in the island. Each link carries the two body ids so island
52    /// splitting's union-find never needs to touch Contact/Joint.
53    pub contacts: Vec<ContactLink>,
54    pub joints: Vec<JointLink>,
55}
56
57impl Default for Island {
58    fn default() -> Self {
59        Island {
60            set_index: NULL_INDEX,
61            local_index: NULL_INDEX,
62            island_id: NULL_INDEX,
63            constraint_remove_count: 0,
64            bodies: Vec::new(),
65            contacts: Vec::new(),
66            joints: Vec::new(),
67        }
68    }
69}
70
71/// Used to move islands across solver sets. (b2IslandSim)
72#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73pub struct IslandSim {
74    pub island_id: i32,
75}
76
77impl Default for IslandSim {
78    fn default() -> Self {
79        IslandSim {
80            island_id: NULL_INDEX,
81        }
82    }
83}
84
85// ---------------------------------------------------------------------------
86// Island lifecycle from island.c. The link/unlink/merge/split logic lands with
87// the contact and joint slices.
88// ---------------------------------------------------------------------------
89
90use crate::solver_set::{AWAKE_SET, FIRST_SLEEPING_SET};
91use crate::world::World;
92
93/// Create an empty island in the given set. Returns the island id.
94/// (b2CreateIsland — C returns a pointer; Rust returns the id)
95pub fn create_island(world: &mut World, set_index: i32) -> i32 {
96    debug_assert!(set_index == AWAKE_SET || set_index >= FIRST_SLEEPING_SET);
97
98    let island_id = world.island_id_pool.alloc_id();
99
100    if island_id == world.islands.len() as i32 {
101        world.islands.push(Island::default());
102    } else {
103        debug_assert!(world.islands[island_id as usize].set_index == NULL_INDEX);
104    }
105
106    let set = &mut world.solver_sets[set_index as usize];
107
108    let local_index = set.island_sims.len() as i32;
109    set.island_sims.push(IslandSim { island_id });
110
111    let island = &mut world.islands[island_id as usize];
112    island.set_index = set_index;
113    island.local_index = local_index;
114    island.island_id = island_id;
115    island.bodies = Vec::new();
116    island.contacts = Vec::new();
117    island.joints = Vec::new();
118    island.constraint_remove_count = 0;
119
120    island_id
121}
122
123/// (b2DestroyIsland)
124pub fn destroy_island(world: &mut World, island_id: i32) {
125    if world.split_island_id == island_id {
126        world.split_island_id = NULL_INDEX;
127    }
128
129    // assume island is empty
130    let (set_index, local_index) = {
131        let island = &world.islands[island_id as usize];
132        (island.set_index, island.local_index)
133    };
134    let set = &mut world.solver_sets[set_index as usize];
135    {
136        let last_index = set.island_sims.len() - 1;
137        debug_assert!(0 <= local_index && local_index as usize <= last_index);
138        let move_island_id = set.island_sims[last_index].island_id;
139        set.island_sims.swap_remove(local_index as usize);
140        world.islands[move_island_id as usize].local_index = local_index;
141    }
142
143    // Free island and id (preserve island revision)
144    let island = &mut world.islands[island_id as usize];
145    island.bodies = Vec::new();
146    island.contacts = Vec::new();
147    island.joints = Vec::new();
148    island.constraint_remove_count = 0;
149    island.local_index = NULL_INDEX;
150    island.island_id = NULL_INDEX;
151    island.set_index = NULL_INDEX;
152
153    world.island_id_pool.free_id(island_id);
154}
155
156/// Merge two islands, keeping the bigger one. Returns the surviving island id.
157/// (static b2MergeIslands)
158pub(crate) fn merge_islands(world: &mut World, island_id_a: i32, island_id_b: i32) -> i32 {
159    if island_id_a == island_id_b {
160        return island_id_a;
161    }
162
163    if island_id_a == NULL_INDEX {
164        debug_assert!(island_id_b != NULL_INDEX);
165        return island_id_b;
166    }
167
168    if island_id_b == NULL_INDEX {
169        debug_assert!(island_id_a != NULL_INDEX);
170        return island_id_a;
171    }
172
173    // Keep the biggest island to reduce cache misses
174    let (big_island_id, small_island_id) = {
175        let count_a = world.islands[island_id_a as usize].bodies.len();
176        let count_b = world.islands[island_id_b as usize].bodies.len();
177        if count_a >= count_b {
178            (island_id_a, island_id_b)
179        } else {
180            (island_id_b, island_id_a)
181        }
182    };
183
184    // Move bodies from smaller island to larger island
185    let small_bodies = std::mem::take(&mut world.islands[small_island_id as usize].bodies);
186    for &body_id in &small_bodies {
187        let body = &mut world.bodies[body_id as usize];
188        debug_assert!(body.island_id == small_island_id);
189        body.island_id = big_island_id;
190        body.island_index = world.islands[big_island_id as usize].bodies.len() as i32;
191        world.islands[big_island_id as usize].bodies.push(body_id);
192    }
193
194    // Migrate contacts from smaller island to larger island
195    let small_contacts = std::mem::take(&mut world.islands[small_island_id as usize].contacts);
196    for link in &small_contacts {
197        let contact = &mut world.contacts[link.contact_id as usize];
198        contact.island_id = big_island_id;
199        contact.island_index = world.islands[big_island_id as usize].contacts.len() as i32;
200        world.islands[big_island_id as usize].contacts.push(*link);
201    }
202
203    // Migrate joints from smaller island to larger island
204    let small_joints = std::mem::take(&mut world.islands[small_island_id as usize].joints);
205    for link in &small_joints {
206        let joint = &mut world.joints[link.joint_id as usize];
207        joint.island_id = big_island_id;
208        joint.island_index = world.islands[big_island_id as usize].joints.len() as i32;
209        world.islands[big_island_id as usize].joints.push(*link);
210    }
211
212    // Track removed constraints
213    let small_removed = world.islands[small_island_id as usize].constraint_remove_count;
214    world.islands[big_island_id as usize].constraint_remove_count += small_removed;
215
216    destroy_island(world, small_island_id);
217
218    validate_island(world, big_island_id);
219
220    big_island_id
221}
222
223/// (static b2AddContactToIsland)
224fn add_contact_to_island(world: &mut World, island_id: i32, contact_id: i32) {
225    let (edge_body_a, edge_body_b) = {
226        let contact = &world.contacts[contact_id as usize];
227        debug_assert!(contact.island_id == NULL_INDEX);
228        debug_assert!(contact.island_index == NULL_INDEX);
229        (contact.edges[0].body_id, contact.edges[1].body_id)
230    };
231
232    let island = &mut world.islands[island_id as usize];
233
234    let island_index = island.contacts.len() as i32;
235    island.contacts.push(ContactLink {
236        contact_id,
237        body_id_a: edge_body_a,
238        body_id_b: edge_body_b,
239    });
240
241    let contact = &mut world.contacts[contact_id as usize];
242    contact.island_id = island_id;
243    contact.island_index = island_index;
244
245    validate_island(world, island_id);
246}
247
248/// Link a contact into an island when it starts having contact points.
249/// (b2LinkContact)
250pub fn link_contact(world: &mut World, contact_id: i32) {
251    use crate::contact::contact_flags::TOUCHING;
252    use crate::solver_set::DISABLED_SET;
253
254    let (body_id_a, body_id_b) = {
255        let contact = &world.contacts[contact_id as usize];
256        debug_assert!(contact.flags & TOUCHING != 0);
257        (contact.edges[0].body_id, contact.edges[1].body_id)
258    };
259
260    let set_a = world.bodies[body_id_a as usize].set_index;
261    let set_b = world.bodies[body_id_b as usize].set_index;
262
263    debug_assert!(set_a != DISABLED_SET && set_b != DISABLED_SET);
264    debug_assert!(set_a != crate::solver_set::STATIC_SET || set_b != crate::solver_set::STATIC_SET);
265
266    // Wake bodyB if bodyA is awake and bodyB is sleeping
267    if set_a == AWAKE_SET && set_b >= FIRST_SLEEPING_SET {
268        crate::solver_set::wake_solver_set(world, set_b);
269    }
270
271    // Wake bodyA if bodyB is awake and bodyA is sleeping
272    let set_a2 = world.bodies[body_id_a as usize].set_index;
273    let set_b2 = world.bodies[body_id_b as usize].set_index;
274    if set_b2 == AWAKE_SET && set_a2 >= FIRST_SLEEPING_SET {
275        crate::solver_set::wake_solver_set(world, set_a2);
276    }
277
278    let island_id_a = world.bodies[body_id_a as usize].island_id;
279    let island_id_b = world.bodies[body_id_b as usize].island_id;
280
281    // Static bodies have null island indices.
282    debug_assert!(
283        world.bodies[body_id_a as usize].set_index != crate::solver_set::STATIC_SET
284            || island_id_a == NULL_INDEX
285    );
286    debug_assert!(
287        world.bodies[body_id_b as usize].set_index != crate::solver_set::STATIC_SET
288            || island_id_b == NULL_INDEX
289    );
290    debug_assert!(island_id_a != NULL_INDEX || island_id_b != NULL_INDEX);
291
292    // Merge islands. This will destroy one of the islands.
293    let final_island_id = merge_islands(world, island_id_a, island_id_b);
294
295    // Add contact to the island that survived
296    add_contact_to_island(world, final_island_id, contact_id);
297}
298
299/// Unlink a contact from the island graph when it stops having contact points
300/// or is destroyed. (b2UnlinkContact)
301pub fn unlink_contact(world: &mut World, contact_id: i32) {
302    let (island_id, remove_index) = {
303        let contact = &world.contacts[contact_id as usize];
304        debug_assert!(contact.island_id != NULL_INDEX);
305        (contact.island_id, contact.island_index)
306    };
307
308    // remove from island
309    {
310        let island = &mut world.islands[island_id as usize];
311        debug_assert!(0 <= remove_index && (remove_index as usize) < island.contacts.len());
312        debug_assert!(island.contacts[remove_index as usize].contact_id == contact_id);
313
314        let moved_index = island.contacts.len() as i32 - 1;
315        island.contacts.swap_remove(remove_index as usize);
316        if moved_index != remove_index {
317            // Fix islandIndex on the contact that was swapped into removeIndex
318            let moved_contact_id = island.contacts[remove_index as usize].contact_id;
319            let moved_contact = &mut world.contacts[moved_contact_id as usize];
320            debug_assert!(moved_contact.island_index == moved_index);
321            moved_contact.island_index = remove_index;
322        }
323    }
324
325    let contact = &mut world.contacts[contact_id as usize];
326    contact.island_id = NULL_INDEX;
327    contact.island_index = NULL_INDEX;
328    world.islands[island_id as usize].constraint_remove_count += 1;
329
330    validate_island(world, island_id);
331}
332
333/// (static b2AddJointToIsland)
334fn add_joint_to_island(world: &mut World, island_id: i32, joint_id: i32) {
335    let (edge_body_a, edge_body_b) = {
336        let joint = &world.joints[joint_id as usize];
337        debug_assert!(joint.island_id == NULL_INDEX);
338        debug_assert!(joint.island_index == NULL_INDEX);
339        (joint.edges[0].body_id, joint.edges[1].body_id)
340    };
341
342    let island = &mut world.islands[island_id as usize];
343
344    let island_index = island.joints.len() as i32;
345    island.joints.push(JointLink {
346        joint_id,
347        body_id_a: edge_body_a,
348        body_id_b: edge_body_b,
349    });
350
351    let joint = &mut world.joints[joint_id as usize];
352    joint.island_id = island_id;
353    joint.island_index = island_index;
354
355    validate_island(world, island_id);
356}
357
358/// Link a joint into the island graph when it is created. (b2LinkJoint)
359pub fn link_joint(world: &mut World, joint_id: i32) {
360    use crate::types::BodyType;
361
362    let (body_id_a, body_id_b) = {
363        let joint = &world.joints[joint_id as usize];
364        (joint.edges[0].body_id, joint.edges[1].body_id)
365    };
366
367    debug_assert!(
368        world.bodies[body_id_a as usize].type_ == BodyType::Dynamic
369            || world.bodies[body_id_b as usize].type_ == BodyType::Dynamic
370    );
371
372    let set_a = world.bodies[body_id_a as usize].set_index;
373    let set_b = world.bodies[body_id_b as usize].set_index;
374
375    if set_a == AWAKE_SET && set_b >= FIRST_SLEEPING_SET {
376        crate::solver_set::wake_solver_set(world, set_b);
377    } else if set_b == AWAKE_SET && set_a >= FIRST_SLEEPING_SET {
378        crate::solver_set::wake_solver_set(world, set_a);
379    }
380
381    let island_id_a = world.bodies[body_id_a as usize].island_id;
382    let island_id_b = world.bodies[body_id_b as usize].island_id;
383
384    debug_assert!(island_id_a != NULL_INDEX || island_id_b != NULL_INDEX);
385
386    // Merge islands. This will destroy one of the islands.
387    let final_island_id = merge_islands(world, island_id_a, island_id_b);
388
389    // Add joint to the island that survived
390    add_joint_to_island(world, final_island_id, joint_id);
391}
392
393/// Unlink a joint from the island graph when it is destroyed. (b2UnlinkJoint)
394pub fn unlink_joint(world: &mut World, joint_id: i32) {
395    let (island_id, remove_index) = {
396        let joint = &world.joints[joint_id as usize];
397        if joint.island_id == NULL_INDEX {
398            return;
399        }
400        (joint.island_id, joint.island_index)
401    };
402
403    // remove from island
404    {
405        let island = &mut world.islands[island_id as usize];
406        debug_assert!(0 <= remove_index && (remove_index as usize) < island.joints.len());
407        debug_assert!(island.joints[remove_index as usize].joint_id == joint_id);
408
409        let moved_index = island.joints.len() as i32 - 1;
410        island.joints.swap_remove(remove_index as usize);
411        if moved_index != remove_index {
412            // Fix islandIndex on the joint that was swapped into removeIndex
413            let moved_joint_id = island.joints[remove_index as usize].joint_id;
414            let moved_joint = &mut world.joints[moved_joint_id as usize];
415            debug_assert!(moved_joint.island_index == moved_index);
416            moved_joint.island_index = remove_index;
417        }
418    }
419
420    let joint = &mut world.joints[joint_id as usize];
421    joint.island_id = NULL_INDEX;
422    joint.island_index = NULL_INDEX;
423    world.islands[island_id as usize].constraint_remove_count += 1;
424
425    validate_island(world, island_id);
426}
427
428/// Validate island connectivity and bookkeeping. (b2ValidateIsland)
429///
430/// The C version is compiled in only with B2_ENABLE_VALIDATION; here it always
431/// runs when called and asserts in debug builds.
432pub fn validate_island(world: &World, island_id: i32) {
433    // B2_VALIDATE: compiled out in release like the C reference
434    if !cfg!(debug_assertions) {
435        return;
436    }
437
438    if island_id == NULL_INDEX {
439        return;
440    }
441
442    let island = &world.islands[island_id as usize];
443    debug_assert!(island.island_id == island_id);
444    debug_assert!(island.set_index != NULL_INDEX);
445
446    {
447        debug_assert!(!island.bodies.is_empty());
448        debug_assert!(island.bodies.len() as i32 <= world.body_id_pool.id_count());
449
450        for (i, &body_id) in island.bodies.iter().enumerate() {
451            let body = &world.bodies[body_id as usize];
452            debug_assert!(body.island_id == island_id);
453            debug_assert!(body.island_index == i as i32);
454            debug_assert!(body.set_index == island.set_index);
455            let _ = (body, i);
456        }
457    }
458
459    if !island.contacts.is_empty() {
460        debug_assert!(island.contacts.len() as i32 <= world.contact_id_pool.id_count());
461
462        for (i, link) in island.contacts.iter().enumerate() {
463            let contact = &world.contacts[link.contact_id as usize];
464            debug_assert!(contact.set_index == island.set_index);
465            debug_assert!(contact.island_id == island_id);
466            debug_assert!(contact.island_index == i as i32);
467            let _ = (contact, i);
468        }
469    }
470
471    if !island.joints.is_empty() {
472        debug_assert!(island.joints.len() as i32 <= world.joint_id_pool.id_count());
473
474        for (i, link) in island.joints.iter().enumerate() {
475            let joint = &world.joints[link.joint_id as usize];
476            debug_assert!(joint.set_index == island.set_index);
477            debug_assert!(joint.island_id == island_id);
478            debug_assert!(joint.island_index == i as i32);
479            let _ = (joint, i);
480        }
481    }
482}
483
484/// Find parent of a node. Uses path halving to speed up further queries.
485/// (static b2IslandFindParent)
486fn island_find_parent(parents: &mut [i32], mut node: i32) -> i32 {
487    // Walk the chain of parents to find the node that is its own parent (the
488    // root)
489    while parents[node as usize] != node {
490        let grand_parent = parents[parents[node as usize] as usize];
491        parents[node as usize] = grand_parent;
492        node = grand_parent;
493    }
494
495    node
496}
497
498/// Connect the components containing node1 and node2. Uses rank to keep the
499/// tree balanced. Tracks per-component contact and joint counts.
500/// (static b2IslandUnion)
501fn island_union(
502    parents: &mut [i32],
503    ranks: &mut [i32],
504    node1: i32,
505    node2: i32,
506    contact_counts: &mut [i32],
507    joint_counts: &mut [i32],
508) {
509    let root1 = island_find_parent(parents, node1);
510    let root2 = island_find_parent(parents, node2);
511    if root1 != root2 {
512        if ranks[root1 as usize] < ranks[root2 as usize] {
513            parents[root1 as usize] = root2;
514            contact_counts[root2 as usize] += contact_counts[root1 as usize];
515            joint_counts[root2 as usize] += joint_counts[root1 as usize];
516        } else if ranks[root1 as usize] > ranks[root2 as usize] {
517            parents[root2 as usize] = root1;
518            contact_counts[root1 as usize] += contact_counts[root2 as usize];
519            joint_counts[root1 as usize] += joint_counts[root2 as usize];
520        } else {
521            parents[root2 as usize] = root1;
522            ranks[root1 as usize] += 1;
523            contact_counts[root1 as usize] += contact_counts[root2 as usize];
524            joint_counts[root1 as usize] += joint_counts[root2 as usize];
525        }
526    }
527}
528
529/// Split an island because some contacts and/or joints have been removed.
530/// This uses union-find and touches a lot of memory, so it can be slow.
531/// Note: contacts/joints connected to static bodies must belong to an island
532/// but don't affect island connectivity.
533/// Note: static bodies are never in an island.
534/// (b2SplitIsland / b2SplitIslandTask — the C runs this as a task in
535/// parallel with the constraint solve; the serial port calls it inline)
536pub fn split_island(world: &mut World, base_id: i32) {
537    debug_assert!(world.islands[base_id as usize].constraint_remove_count > 0);
538    debug_assert!(world.islands[base_id as usize].set_index == AWAKE_SET);
539
540    validate_island(world, base_id);
541
542    // Detach the base island's arrays. (The C caches raw pointers because
543    // b2CreateIsland may reallocate the island array; taking the Vecs is the
544    // owned equivalent and also protects them from b2DestroyIsland.)
545    let base_body_ids = std::mem::take(&mut world.islands[base_id as usize].bodies);
546    let base_contacts = std::mem::take(&mut world.islands[base_id as usize].contacts);
547    let base_joints = std::mem::take(&mut world.islands[base_id as usize].joints);
548
549    let base_body_count = base_body_ids.len();
550
551    // Arena scratch in C; plain Vecs here.
552    let mut parents: Vec<i32> = (0..base_body_count as i32).collect();
553    let mut contact_counts: Vec<i32> = vec![0; base_body_count];
554    let mut joint_counts: Vec<i32> = vec![0; base_body_count];
555    let mut ranks: Vec<i32> = vec![0; base_body_count];
556
557    // Union over contacts, tracking per-component contact counts
558    for link in &base_contacts {
559        let island_index_a = world.bodies[link.body_id_a as usize].island_index;
560        let island_index_b = world.bodies[link.body_id_b as usize].island_index;
561
562        // Only connect non-static bodies
563        if island_index_a != NULL_INDEX && island_index_b != NULL_INDEX {
564            island_union(
565                &mut parents,
566                &mut ranks,
567                island_index_a,
568                island_index_b,
569                &mut contact_counts,
570                &mut joint_counts,
571            );
572            let root = island_find_parent(&mut parents, island_index_a);
573            contact_counts[root as usize] += 1;
574        } else {
575            let island_index = if island_index_a != NULL_INDEX {
576                island_index_a
577            } else {
578                island_index_b
579            };
580            let root = island_find_parent(&mut parents, island_index);
581            contact_counts[root as usize] += 1;
582        }
583    }
584
585    // Union over joints, tracking per-component joint counts
586    for link in &base_joints {
587        let island_index_a = world.bodies[link.body_id_a as usize].island_index;
588        let island_index_b = world.bodies[link.body_id_b as usize].island_index;
589
590        // Only connect non-static bodies
591        if island_index_a != NULL_INDEX && island_index_b != NULL_INDEX {
592            island_union(
593                &mut parents,
594                &mut ranks,
595                island_index_a,
596                island_index_b,
597                &mut contact_counts,
598                &mut joint_counts,
599            );
600            let root = island_find_parent(&mut parents, island_index_a);
601            joint_counts[root as usize] += 1;
602        } else {
603            let island_index = if island_index_a != NULL_INDEX {
604                island_index_a
605            } else {
606                island_index_b
607            };
608            let root = island_find_parent(&mut parents, island_index);
609            joint_counts[root as usize] += 1;
610        }
611    }
612
613    // Flatten all parent indices and count connected components.
614    let mut component_count = 0usize;
615    #[allow(clippy::needless_range_loop)]
616    for i in 0..base_body_count {
617        let root = island_find_parent(&mut parents, i as i32);
618        parents[i] = root;
619        if root == i as i32 {
620            component_count += 1;
621        }
622    }
623
624    // Early return — island is still fully connected, no split needed.
625    if component_count == 1 {
626        let island = &mut world.islands[base_id as usize];
627        island.constraint_remove_count = 0;
628        island.bodies = base_body_ids;
629        island.contacts = base_contacts;
630        island.joints = base_joints;
631        return;
632    }
633
634    // Map from body index to new island index. Only set for root bodies.
635    let mut root_map: Vec<i32> = vec![NULL_INDEX; base_body_count];
636
637    let mut component_body_counts: Vec<i32> = vec![0; component_count];
638    let mut component_contact_counts: Vec<i32> = vec![0; component_count];
639    let mut component_joint_counts: Vec<i32> = vec![0; component_count];
640    let mut island_count = 0usize;
641
642    // Find the root body for each body and create islands as needed.
643    // Extract per-component counts from the root nodes' accumulated counts.
644    #[allow(clippy::needless_range_loop)]
645    for i in 0..base_body_count {
646        let root_index = parents[i] as usize;
647        if root_map[root_index] == NULL_INDEX {
648            root_map[root_index] = island_count as i32;
649            component_body_counts[island_count] = 0;
650            component_contact_counts[island_count] = contact_counts[root_index];
651            component_joint_counts[island_count] = joint_counts[root_index];
652            island_count += 1;
653        }
654
655        component_body_counts[root_map[root_index] as usize] += 1;
656    }
657
658    debug_assert!(island_count == component_count);
659
660    // Map from new island index to island id
661    let mut island_ids: Vec<i32> = Vec::with_capacity(island_count);
662
663    // Create new islands and reserve body/contact/joint arrays
664    for i in 0..island_count {
665        let new_island_id = create_island(world, AWAKE_SET);
666        island_ids.push(new_island_id);
667
668        // Reserve arrays to avoid wasteful growth
669        let new_island = &mut world.islands[new_island_id as usize];
670        new_island.bodies.reserve(component_body_counts[i] as usize);
671        new_island
672            .contacts
673            .reserve(component_contact_counts[i] as usize);
674        new_island
675            .joints
676            .reserve(component_joint_counts[i] as usize);
677    }
678
679    // Assign bodies to new islands
680    for (i, &body_id) in base_body_ids.iter().enumerate() {
681        let root = island_find_parent(&mut parents, i as i32);
682        let new_island_id = island_ids[root_map[root as usize] as usize];
683
684        let island_index = world.islands[new_island_id as usize].bodies.len() as i32;
685        let body = &mut world.bodies[body_id as usize];
686        body.island_id = new_island_id;
687        body.island_index = island_index;
688
689        world.islands[new_island_id as usize].bodies.push(body_id);
690    }
691
692    // Assign contacts to the island of their bodies
693    for link in &base_contacts {
694        // Static bodies don't have an island id.
695        let island_id_a = world.bodies[link.body_id_a as usize].island_id;
696        let target_island_id = if island_id_a != NULL_INDEX {
697            island_id_a
698        } else {
699            world.bodies[link.body_id_b as usize].island_id
700        };
701
702        let island_index = world.islands[target_island_id as usize].contacts.len() as i32;
703        let contact = &mut world.contacts[link.contact_id as usize];
704        contact.island_id = target_island_id;
705        contact.island_index = island_index;
706
707        world.islands[target_island_id as usize]
708            .contacts
709            .push(*link);
710    }
711
712    // Assign joints to the island of their bodies
713    for link in &base_joints {
714        // Static bodies don't have an island id.
715        let island_id_a = world.bodies[link.body_id_a as usize].island_id;
716        let target_island_id = if island_id_a != NULL_INDEX {
717            island_id_a
718        } else {
719            world.bodies[link.body_id_b as usize].island_id
720        };
721
722        let island_index = world.islands[target_island_id as usize].joints.len() as i32;
723        let joint = &mut world.joints[link.joint_id as usize];
724        joint.island_id = target_island_id;
725        joint.island_index = island_index;
726
727        world.islands[target_island_id as usize].joints.push(*link);
728    }
729
730    // Destroy the base island
731    destroy_island(world, base_id);
732}