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    if island_id == NULL_INDEX {
434        return;
435    }
436
437    let island = &world.islands[island_id as usize];
438    debug_assert!(island.island_id == island_id);
439    debug_assert!(island.set_index != NULL_INDEX);
440
441    {
442        debug_assert!(!island.bodies.is_empty());
443        debug_assert!(island.bodies.len() as i32 <= world.body_id_pool.id_count());
444
445        for (i, &body_id) in island.bodies.iter().enumerate() {
446            let body = &world.bodies[body_id as usize];
447            debug_assert!(body.island_id == island_id);
448            debug_assert!(body.island_index == i as i32);
449            debug_assert!(body.set_index == island.set_index);
450            let _ = (body, i);
451        }
452    }
453
454    if !island.contacts.is_empty() {
455        debug_assert!(island.contacts.len() as i32 <= world.contact_id_pool.id_count());
456
457        for (i, link) in island.contacts.iter().enumerate() {
458            let contact = &world.contacts[link.contact_id as usize];
459            debug_assert!(contact.set_index == island.set_index);
460            debug_assert!(contact.island_id == island_id);
461            debug_assert!(contact.island_index == i as i32);
462            let _ = (contact, i);
463        }
464    }
465
466    if !island.joints.is_empty() {
467        debug_assert!(island.joints.len() as i32 <= world.joint_id_pool.id_count());
468
469        for (i, link) in island.joints.iter().enumerate() {
470            let joint = &world.joints[link.joint_id as usize];
471            debug_assert!(joint.set_index == island.set_index);
472            debug_assert!(joint.island_id == island_id);
473            debug_assert!(joint.island_index == i as i32);
474            let _ = (joint, i);
475        }
476    }
477}
478
479/// Find parent of a node. Uses path halving to speed up further queries.
480/// (static b2IslandFindParent)
481fn island_find_parent(parents: &mut [i32], mut node: i32) -> i32 {
482    // Walk the chain of parents to find the node that is its own parent (the
483    // root)
484    while parents[node as usize] != node {
485        let grand_parent = parents[parents[node as usize] as usize];
486        parents[node as usize] = grand_parent;
487        node = grand_parent;
488    }
489
490    node
491}
492
493/// Connect the components containing node1 and node2. Uses rank to keep the
494/// tree balanced. Tracks per-component contact and joint counts.
495/// (static b2IslandUnion)
496fn island_union(
497    parents: &mut [i32],
498    ranks: &mut [i32],
499    node1: i32,
500    node2: i32,
501    contact_counts: &mut [i32],
502    joint_counts: &mut [i32],
503) {
504    let root1 = island_find_parent(parents, node1);
505    let root2 = island_find_parent(parents, node2);
506    if root1 != root2 {
507        if ranks[root1 as usize] < ranks[root2 as usize] {
508            parents[root1 as usize] = root2;
509            contact_counts[root2 as usize] += contact_counts[root1 as usize];
510            joint_counts[root2 as usize] += joint_counts[root1 as usize];
511        } else if ranks[root1 as usize] > ranks[root2 as usize] {
512            parents[root2 as usize] = root1;
513            contact_counts[root1 as usize] += contact_counts[root2 as usize];
514            joint_counts[root1 as usize] += joint_counts[root2 as usize];
515        } else {
516            parents[root2 as usize] = root1;
517            ranks[root1 as usize] += 1;
518            contact_counts[root1 as usize] += contact_counts[root2 as usize];
519            joint_counts[root1 as usize] += joint_counts[root2 as usize];
520        }
521    }
522}
523
524/// Split an island because some contacts and/or joints have been removed.
525/// This uses union-find and touches a lot of memory, so it can be slow.
526/// Note: contacts/joints connected to static bodies must belong to an island
527/// but don't affect island connectivity.
528/// Note: static bodies are never in an island.
529/// (b2SplitIsland / b2SplitIslandTask — the C runs this as a task in
530/// parallel with the constraint solve; the serial port calls it inline)
531pub fn split_island(world: &mut World, base_id: i32) {
532    debug_assert!(world.islands[base_id as usize].constraint_remove_count > 0);
533    debug_assert!(world.islands[base_id as usize].set_index == AWAKE_SET);
534
535    validate_island(world, base_id);
536
537    // Detach the base island's arrays. (The C caches raw pointers because
538    // b2CreateIsland may reallocate the island array; taking the Vecs is the
539    // owned equivalent and also protects them from b2DestroyIsland.)
540    let base_body_ids = std::mem::take(&mut world.islands[base_id as usize].bodies);
541    let base_contacts = std::mem::take(&mut world.islands[base_id as usize].contacts);
542    let base_joints = std::mem::take(&mut world.islands[base_id as usize].joints);
543
544    let base_body_count = base_body_ids.len();
545
546    // Arena scratch in C; plain Vecs here.
547    let mut parents: Vec<i32> = (0..base_body_count as i32).collect();
548    let mut contact_counts: Vec<i32> = vec![0; base_body_count];
549    let mut joint_counts: Vec<i32> = vec![0; base_body_count];
550    let mut ranks: Vec<i32> = vec![0; base_body_count];
551
552    // Union over contacts, tracking per-component contact counts
553    for link in &base_contacts {
554        let island_index_a = world.bodies[link.body_id_a as usize].island_index;
555        let island_index_b = world.bodies[link.body_id_b as usize].island_index;
556
557        // Only connect non-static bodies
558        if island_index_a != NULL_INDEX && island_index_b != NULL_INDEX {
559            island_union(
560                &mut parents,
561                &mut ranks,
562                island_index_a,
563                island_index_b,
564                &mut contact_counts,
565                &mut joint_counts,
566            );
567            let root = island_find_parent(&mut parents, island_index_a);
568            contact_counts[root as usize] += 1;
569        } else {
570            let island_index = if island_index_a != NULL_INDEX {
571                island_index_a
572            } else {
573                island_index_b
574            };
575            let root = island_find_parent(&mut parents, island_index);
576            contact_counts[root as usize] += 1;
577        }
578    }
579
580    // Union over joints, tracking per-component joint counts
581    for link in &base_joints {
582        let island_index_a = world.bodies[link.body_id_a as usize].island_index;
583        let island_index_b = world.bodies[link.body_id_b as usize].island_index;
584
585        // Only connect non-static bodies
586        if island_index_a != NULL_INDEX && island_index_b != NULL_INDEX {
587            island_union(
588                &mut parents,
589                &mut ranks,
590                island_index_a,
591                island_index_b,
592                &mut contact_counts,
593                &mut joint_counts,
594            );
595            let root = island_find_parent(&mut parents, island_index_a);
596            joint_counts[root as usize] += 1;
597        } else {
598            let island_index = if island_index_a != NULL_INDEX {
599                island_index_a
600            } else {
601                island_index_b
602            };
603            let root = island_find_parent(&mut parents, island_index);
604            joint_counts[root as usize] += 1;
605        }
606    }
607
608    // Flatten all parent indices and count connected components.
609    let mut component_count = 0usize;
610    #[allow(clippy::needless_range_loop)]
611    for i in 0..base_body_count {
612        let root = island_find_parent(&mut parents, i as i32);
613        parents[i] = root;
614        if root == i as i32 {
615            component_count += 1;
616        }
617    }
618
619    // Early return — island is still fully connected, no split needed.
620    if component_count == 1 {
621        let island = &mut world.islands[base_id as usize];
622        island.constraint_remove_count = 0;
623        island.bodies = base_body_ids;
624        island.contacts = base_contacts;
625        island.joints = base_joints;
626        return;
627    }
628
629    // Map from body index to new island index. Only set for root bodies.
630    let mut root_map: Vec<i32> = vec![NULL_INDEX; base_body_count];
631
632    let mut component_body_counts: Vec<i32> = vec![0; component_count];
633    let mut component_contact_counts: Vec<i32> = vec![0; component_count];
634    let mut component_joint_counts: Vec<i32> = vec![0; component_count];
635    let mut island_count = 0usize;
636
637    // Find the root body for each body and create islands as needed.
638    // Extract per-component counts from the root nodes' accumulated counts.
639    #[allow(clippy::needless_range_loop)]
640    for i in 0..base_body_count {
641        let root_index = parents[i] as usize;
642        if root_map[root_index] == NULL_INDEX {
643            root_map[root_index] = island_count as i32;
644            component_body_counts[island_count] = 0;
645            component_contact_counts[island_count] = contact_counts[root_index];
646            component_joint_counts[island_count] = joint_counts[root_index];
647            island_count += 1;
648        }
649
650        component_body_counts[root_map[root_index] as usize] += 1;
651    }
652
653    debug_assert!(island_count == component_count);
654
655    // Map from new island index to island id
656    let mut island_ids: Vec<i32> = Vec::with_capacity(island_count);
657
658    // Create new islands and reserve body/contact/joint arrays
659    for i in 0..island_count {
660        let new_island_id = create_island(world, AWAKE_SET);
661        island_ids.push(new_island_id);
662
663        // Reserve arrays to avoid wasteful growth
664        let new_island = &mut world.islands[new_island_id as usize];
665        new_island.bodies.reserve(component_body_counts[i] as usize);
666        new_island
667            .contacts
668            .reserve(component_contact_counts[i] as usize);
669        new_island
670            .joints
671            .reserve(component_joint_counts[i] as usize);
672    }
673
674    // Assign bodies to new islands
675    for (i, &body_id) in base_body_ids.iter().enumerate() {
676        let root = island_find_parent(&mut parents, i as i32);
677        let new_island_id = island_ids[root_map[root as usize] as usize];
678
679        let island_index = world.islands[new_island_id as usize].bodies.len() as i32;
680        let body = &mut world.bodies[body_id as usize];
681        body.island_id = new_island_id;
682        body.island_index = island_index;
683
684        world.islands[new_island_id as usize].bodies.push(body_id);
685    }
686
687    // Assign contacts to the island of their bodies
688    for link in &base_contacts {
689        // Static bodies don't have an island id.
690        let island_id_a = world.bodies[link.body_id_a as usize].island_id;
691        let target_island_id = if island_id_a != NULL_INDEX {
692            island_id_a
693        } else {
694            world.bodies[link.body_id_b as usize].island_id
695        };
696
697        let island_index = world.islands[target_island_id as usize].contacts.len() as i32;
698        let contact = &mut world.contacts[link.contact_id as usize];
699        contact.island_id = target_island_id;
700        contact.island_index = island_index;
701
702        world.islands[target_island_id as usize]
703            .contacts
704            .push(*link);
705    }
706
707    // Assign joints to the island of their bodies
708    for link in &base_joints {
709        // Static bodies don't have an island id.
710        let island_id_a = world.bodies[link.body_id_a as usize].island_id;
711        let target_island_id = if island_id_a != NULL_INDEX {
712            island_id_a
713        } else {
714            world.bodies[link.body_id_b as usize].island_id
715        };
716
717        let island_index = world.islands[target_island_id as usize].joints.len() as i32;
718        let joint = &mut world.joints[link.joint_id as usize];
719        joint.island_id = target_island_id;
720        joint.island_index = island_index;
721
722        world.islands[target_island_id as usize].joints.push(*link);
723    }
724
725    // Destroy the base island
726    destroy_island(world, base_id);
727}