1use crate::body::{BodySim, BodyState};
7use crate::core::NULL_INDEX;
8use crate::island::IslandSim;
9use crate::joint::JointSim;
10use crate::world::World;
11
12pub const STATIC_SET: i32 = 0;
14pub const DISABLED_SET: i32 = 1;
16pub const AWAKE_SET: i32 = 2;
18pub const FIRST_SLEEPING_SET: i32 = 3;
20
21#[derive(Debug, Clone, Default)]
32pub struct SolverSet {
33 pub body_sims: Vec<BodySim>,
35
36 pub body_states: Vec<BodyState>,
38
39 pub joint_sims: Vec<JointSim>,
41
42 pub contact_indices: Vec<i32>,
45
46 pub island_sims: Vec<IslandSim>,
50
51 pub set_index: i32,
53}
54
55impl SolverSet {
56 pub fn free_slot() -> SolverSet {
58 SolverSet {
59 set_index: NULL_INDEX,
60 ..Default::default()
61 }
62 }
63}
64
65pub fn destroy_solver_set(world: &mut World, set_index: i32) {
67 let set = &mut world.solver_sets[set_index as usize];
68 *set = SolverSet::default();
69 set.set_index = NULL_INDEX;
70 world.solver_set_id_pool.free_id(set_index);
71}
72
73pub fn wake_solver_set(world: &mut World, set_index: i32) {
82 use crate::body::IDENTITY_BODY_STATE;
83 use crate::constraint_graph::{add_contact_to_graph, add_joint_to_graph};
84 use crate::contact::contact_flags;
85
86 debug_assert!(set_index >= FIRST_SLEEPING_SET);
87
88 let body_count = world.solver_sets[set_index as usize].body_sims.len();
89 for i in 0..body_count {
90 let sim_src = world.solver_sets[set_index as usize].body_sims[i];
91 let body_id = sim_src.body_id;
92
93 debug_assert!(world.bodies[body_id as usize].set_index == set_index);
94 let awake_body_count = world.solver_sets[AWAKE_SET as usize].body_sims.len() as i32;
95 let flags = {
96 let body = &mut world.bodies[body_id as usize];
97 body.set_index = AWAKE_SET;
98 body.local_index = awake_body_count;
99 body.sleep_time = 0.0;
101 body.flags
102 };
103
104 world.solver_sets[AWAKE_SET as usize]
105 .body_sims
106 .push(sim_src);
107
108 let mut state = IDENTITY_BODY_STATE;
109 state.flags = flags;
110 world.solver_sets[AWAKE_SET as usize]
111 .body_states
112 .push(state);
113
114 let mut contact_key = world.bodies[body_id as usize].head_contact_key;
116 while contact_key != NULL_INDEX {
117 let edge_index = (contact_key & 1) as usize;
118 let contact_id = contact_key >> 1;
119
120 contact_key = world.contacts[contact_id as usize].edges[edge_index].next_key;
121
122 if world.contacts[contact_id as usize].set_index != DISABLED_SET {
123 debug_assert!(
124 world.contacts[contact_id as usize].set_index == AWAKE_SET
125 || world.contacts[contact_id as usize].set_index == set_index
126 );
127 continue;
128 }
129
130 let local_index = world.contacts[contact_id as usize].local_index;
131
132 debug_assert!(
133 0 <= local_index
134 && (local_index as usize)
135 < world.solver_sets[DISABLED_SET as usize]
136 .contact_indices
137 .len()
138 );
139 debug_assert!(
140 world.solver_sets[DISABLED_SET as usize].contact_indices[local_index as usize]
141 == contact_id
142 );
143
144 debug_assert!(
145 (world.contacts[contact_id as usize].flags & contact_flags::TOUCHING) == 0
146 && world.contacts[contact_id as usize].manifold_count() == 0
147 );
148
149 {
150 let awake_contact_count =
151 world.solver_sets[AWAKE_SET as usize].contact_indices.len() as i32;
152 let contact = &mut world.contacts[contact_id as usize];
153 contact.set_index = AWAKE_SET;
154 contact.local_index = awake_contact_count;
155 }
156 world.solver_sets[AWAKE_SET as usize]
157 .contact_indices
158 .push(contact_id);
159
160 let disabled_contacts = &mut world.solver_sets[DISABLED_SET as usize].contact_indices;
161 let moved_local_index = disabled_contacts.len() as i32 - 1;
162 disabled_contacts.swap_remove(local_index as usize);
163 if moved_local_index != local_index {
164 let moved_contact_index = disabled_contacts[local_index as usize];
166 let moved_contact = &mut world.contacts[moved_contact_index as usize];
167 debug_assert!(moved_contact.local_index == moved_local_index);
168 moved_contact.local_index = local_index;
169 }
170 }
171 }
172
173 {
175 let contact_count = world.solver_sets[set_index as usize].contact_indices.len();
176 for i in 0..contact_count {
177 let contact_id = world.solver_sets[set_index as usize].contact_indices[i];
178 debug_assert!(
179 (world.contacts[contact_id as usize].flags & contact_flags::TOUCHING) != 0
180 );
181 debug_assert!(
182 (world.contacts[contact_id as usize].flags & contact_flags::SIM_TOUCHING) != 0
183 );
184 debug_assert!(world.contacts[contact_id as usize].set_index == set_index);
185 add_contact_to_graph(world, contact_id);
186 world.contacts[contact_id as usize].set_index = AWAKE_SET;
187 }
188 }
189
190 {
192 let joint_count = world.solver_sets[set_index as usize].joint_sims.len();
193 for i in 0..joint_count {
194 let joint_sim = world.solver_sets[set_index as usize].joint_sims[i];
195 let joint_id = joint_sim.joint_id;
196 debug_assert!(world.joints[joint_id as usize].set_index == set_index);
197 add_joint_to_graph(world, joint_sim, joint_id);
198 world.joints[joint_id as usize].set_index = AWAKE_SET;
199 }
200 }
201
202 let island_count = world.solver_sets[set_index as usize].island_sims.len();
207 for i in 0..island_count {
208 let island_src = world.solver_sets[set_index as usize].island_sims[i];
209 let island_id = island_src.island_id;
210 let awake_island_count = world.solver_sets[AWAKE_SET as usize].island_sims.len() as i32;
211 {
212 let island = &mut world.islands[island_id as usize];
213 island.set_index = AWAKE_SET;
214 island.local_index = awake_island_count;
215 }
216 world.solver_sets[AWAKE_SET as usize]
217 .island_sims
218 .push(island_src);
219 }
220
221 destroy_solver_set(world, set_index);
223}
224
225pub fn try_sleep_island(world: &mut World, island_id: i32) {
228 use crate::constants::GRAPH_COLOR_COUNT;
229 use crate::constraint_graph::OVERFLOW_INDEX;
230 use crate::contact::contact_flags;
231
232 debug_assert!(world.islands[island_id as usize].set_index == AWAKE_SET);
233
234 if world.islands[island_id as usize].constraint_remove_count > 0
236 && world.islands[island_id as usize].bodies.len() > 1
237 {
238 return;
239 }
240
241 let sleep_set_id = world.solver_set_id_pool.alloc_id();
248 if sleep_set_id == world.solver_sets.len() as i32 {
249 world.solver_sets.push(SolverSet::free_slot());
250 }
251
252 {
253 let island = &world.islands[island_id as usize];
254 debug_assert!(
255 0 <= island.local_index
256 && (island.local_index as usize)
257 < world.solver_sets[AWAKE_SET as usize].island_sims.len()
258 );
259
260 let body_capacity = island.bodies.len();
261 let contact_capacity = island.contacts.len();
262 let joint_capacity = island.joints.len();
263
264 let sleep_set = &mut world.solver_sets[sleep_set_id as usize];
265 *sleep_set = SolverSet::default();
266 sleep_set.set_index = sleep_set_id;
267 sleep_set.body_sims.reserve(body_capacity);
268 sleep_set.contact_indices.reserve(contact_capacity);
269 sleep_set.joint_sims.reserve(joint_capacity);
270 }
271
272 {
275 let body_count = world.islands[island_id as usize].bodies.len();
276 for i in 0..body_count {
277 let body_id = world.islands[island_id as usize].bodies[i];
278 debug_assert!(world.bodies[body_id as usize].set_index == AWAKE_SET);
279 debug_assert!(world.bodies[body_id as usize].island_id == island_id);
280 debug_assert!(world.bodies[body_id as usize].island_index == i as i32);
281
282 let body_move_index = world.bodies[body_id as usize].body_move_index;
285 if body_move_index != NULL_INDEX {
286 let move_event = &mut world.body_move_events[body_move_index as usize];
287 debug_assert!(move_event.body_id.index1 - 1 == body_id);
288 debug_assert!(
289 move_event.body_id.generation == world.bodies[body_id as usize].generation
290 );
291 move_event.fell_asleep = true;
292 world.bodies[body_id as usize].body_move_index = NULL_INDEX;
293 }
294
295 let awake_body_index = world.bodies[body_id as usize].local_index;
296
297 let awake_sim =
299 world.solver_sets[AWAKE_SET as usize].body_sims[awake_body_index as usize];
300 let sleep_body_index = world.solver_sets[sleep_set_id as usize].body_sims.len() as i32;
301 world.solver_sets[sleep_set_id as usize]
302 .body_sims
303 .push(awake_sim);
304
305 {
306 let awake_sims = &mut world.solver_sets[AWAKE_SET as usize].body_sims;
307 let moved_index = awake_sims.len() as i32 - 1;
308 awake_sims.swap_remove(awake_body_index as usize);
309 if moved_index != awake_body_index {
310 let moved_id = awake_sims[awake_body_index as usize].body_id;
312 let moved_body = &mut world.bodies[moved_id as usize];
313 debug_assert!(moved_body.local_index == moved_index);
314 moved_body.local_index = awake_body_index;
315 }
316 }
317
318 world.solver_sets[AWAKE_SET as usize]
320 .body_states
321 .swap_remove(awake_body_index as usize);
322
323 {
324 let body = &mut world.bodies[body_id as usize];
325 body.set_index = sleep_set_id;
326 body.local_index = sleep_body_index;
327 }
328
329 let mut contact_key = world.bodies[body_id as usize].head_contact_key;
332 while contact_key != NULL_INDEX {
333 let contact_id = contact_key >> 1;
334 let edge_index = (contact_key & 1) as usize;
335
336 debug_assert!(
337 world.contacts[contact_id as usize].set_index == AWAKE_SET
338 || world.contacts[contact_id as usize].set_index == DISABLED_SET
339 );
340 contact_key = world.contacts[contact_id as usize].edges[edge_index].next_key;
341
342 if world.contacts[contact_id as usize].set_index == DISABLED_SET {
343 continue;
345 }
346
347 if world.contacts[contact_id as usize].color_index != NULL_INDEX {
348 debug_assert!(
350 (world.contacts[contact_id as usize].flags & contact_flags::TOUCHING) != 0
351 );
352 continue;
353 }
354
355 let other_edge_index = edge_index ^ 1;
358 let other_body_id =
359 world.contacts[contact_id as usize].edges[other_edge_index].body_id;
360 if world.bodies[other_body_id as usize].set_index == AWAKE_SET {
361 continue;
362 }
363
364 let local_index = world.contacts[contact_id as usize].local_index;
365 debug_assert!(
366 world.solver_sets[AWAKE_SET as usize].contact_indices[local_index as usize]
367 == contact_id
368 );
369
370 debug_assert!(world.contacts[contact_id as usize].manifold_count() == 0);
371 debug_assert!(
372 (world.contacts[contact_id as usize].flags & contact_flags::TOUCHING) == 0
373 );
374
375 {
377 let disabled_contact_count = world.solver_sets[DISABLED_SET as usize]
378 .contact_indices
379 .len() as i32;
380 let contact = &mut world.contacts[contact_id as usize];
381 contact.set_index = DISABLED_SET;
382
383 contact.local_index = disabled_contact_count;
385 }
386 world.solver_sets[DISABLED_SET as usize]
387 .contact_indices
388 .push(contact_id);
389
390 let awake_contacts = &mut world.solver_sets[AWAKE_SET as usize].contact_indices;
391 let moved_local_index = awake_contacts.len() as i32 - 1;
392 awake_contacts.swap_remove(local_index as usize);
393 if moved_local_index != local_index {
394 let moved_contact_index = awake_contacts[local_index as usize];
396 let moved_contact = &mut world.contacts[moved_contact_index as usize];
397 debug_assert!(moved_contact.local_index == moved_local_index);
398 moved_contact.local_index = local_index;
399 }
400 }
401 }
402 }
403
404 {
407 let contact_count = world.islands[island_id as usize].contacts.len();
408 for i in 0..contact_count {
409 let contact_id = world.islands[island_id as usize].contacts[i].contact_id;
410 debug_assert!(world.contacts[contact_id as usize].set_index == AWAKE_SET);
411 debug_assert!(world.contacts[contact_id as usize].island_id == island_id);
412 debug_assert!(world.contacts[contact_id as usize].island_index == i as i32);
413 let color_index = world.contacts[contact_id as usize].color_index;
414 debug_assert!(0 <= color_index && color_index < GRAPH_COLOR_COUNT);
415
416 if color_index != OVERFLOW_INDEX {
418 let body_id_a = world.contacts[contact_id as usize].edges[0].body_id;
420 let body_id_b = world.contacts[contact_id as usize].edges[1].body_id;
421 let color = &mut world.constraint_graph.colors[color_index as usize];
422 color.body_set.clear_bit(body_id_a as u32);
423 color.body_set.clear_bit(body_id_b as u32);
424 }
425
426 let sleep_contact_index = world.solver_sets[sleep_set_id as usize]
427 .contact_indices
428 .len() as i32;
429 world.solver_sets[sleep_set_id as usize]
430 .contact_indices
431 .push(contact_id);
432
433 let local_index = world.contacts[contact_id as usize].local_index;
434 let is_mesh =
435 (world.contacts[contact_id as usize].flags & contact_flags::SIM_MESH_CONTACT) != 0;
436 if is_mesh || color_index == OVERFLOW_INDEX {
437 let color = &mut world.constraint_graph.colors[color_index as usize];
438 let moved_local_index = color.contacts.len() as i32 - 1;
439 color.contacts.swap_remove(local_index as usize);
440 if moved_local_index != local_index {
441 let moved_contact_id = color.contacts[local_index as usize].contact_id;
443 let moved_contact = &mut world.contacts[moved_contact_id as usize];
444 debug_assert!(moved_contact.local_index == moved_local_index);
445 moved_contact.local_index = local_index;
446 }
447 } else {
448 let color = &mut world.constraint_graph.colors[color_index as usize];
449 let moved_local_index = color.convex_contacts.len() as i32 - 1;
450 color.convex_contacts.swap_remove(local_index as usize);
451 if moved_local_index != local_index {
452 let moved_contact_id = color.convex_contacts[local_index as usize];
454 let moved_contact = &mut world.contacts[moved_contact_id as usize];
455 debug_assert!(moved_contact.local_index == moved_local_index);
456 moved_contact.local_index = local_index;
457 }
458 }
459
460 let contact = &mut world.contacts[contact_id as usize];
461 contact.set_index = sleep_set_id;
462 contact.color_index = NULL_INDEX;
463 contact.local_index = sleep_contact_index;
464 }
465 }
466
467 {
470 let joint_count = world.islands[island_id as usize].joints.len();
471 for i in 0..joint_count {
472 let joint_id = world.islands[island_id as usize].joints[i].joint_id;
473 debug_assert!(world.joints[joint_id as usize].set_index == AWAKE_SET);
474 debug_assert!(world.joints[joint_id as usize].island_id == island_id);
475 debug_assert!(world.joints[joint_id as usize].island_index == i as i32);
476 let color_index = world.joints[joint_id as usize].color_index;
477 let local_index = world.joints[joint_id as usize].local_index;
478
479 debug_assert!(0 <= color_index && color_index < GRAPH_COLOR_COUNT);
480
481 let awake_joint_sim = world.constraint_graph.colors[color_index as usize].joint_sims
482 [local_index as usize];
483
484 if color_index != OVERFLOW_INDEX {
485 let body_id_a = world.joints[joint_id as usize].edges[0].body_id;
487 let body_id_b = world.joints[joint_id as usize].edges[1].body_id;
488 let color = &mut world.constraint_graph.colors[color_index as usize];
489 color.body_set.clear_bit(body_id_a as u32);
490 color.body_set.clear_bit(body_id_b as u32);
491 }
492
493 let sleep_joint_index =
494 world.solver_sets[sleep_set_id as usize].joint_sims.len() as i32;
495 world.solver_sets[sleep_set_id as usize]
496 .joint_sims
497 .push(awake_joint_sim);
498
499 {
500 let color = &mut world.constraint_graph.colors[color_index as usize];
501 let moved_index = color.joint_sims.len() as i32 - 1;
502 color.joint_sims.swap_remove(local_index as usize);
503 if moved_index != local_index {
504 let moved_id = color.joint_sims[local_index as usize].joint_id;
506 let moved_joint = &mut world.joints[moved_id as usize];
507 debug_assert!(moved_joint.local_index == moved_index);
508 moved_joint.local_index = local_index;
509 }
510 }
511
512 let joint = &mut world.joints[joint_id as usize];
513 joint.set_index = sleep_set_id;
514 joint.color_index = NULL_INDEX;
515 joint.local_index = sleep_joint_index;
516 }
517 }
518
519 {
521 debug_assert!(world.islands[island_id as usize].set_index == AWAKE_SET);
522
523 let island_index = world.islands[island_id as usize].local_index;
524 world.solver_sets[sleep_set_id as usize]
525 .island_sims
526 .push(IslandSim { island_id });
527
528 let awake_islands = &mut world.solver_sets[AWAKE_SET as usize].island_sims;
529 let moved_island_index = awake_islands.len() as i32 - 1;
530 awake_islands.swap_remove(island_index as usize);
531 if moved_island_index != island_index {
532 let moved_island_id = awake_islands[island_index as usize].island_id;
534 let moved_island = &mut world.islands[moved_island_id as usize];
535 debug_assert!(moved_island.local_index == moved_island_index);
536 moved_island.local_index = island_index;
537 }
538
539 let island = &mut world.islands[island_id as usize];
540 island.set_index = sleep_set_id;
541 island.local_index = 0;
542 }
543
544 if world.split_island_id == island_id {
545 world.split_island_id = NULL_INDEX;
546 }
547
548 world.validate_solver_sets();
549}
550
551pub fn merge_solver_sets(world: &mut World, set_id1: i32, set_id2: i32) {
555 debug_assert!(set_id1 >= FIRST_SLEEPING_SET);
556 debug_assert!(set_id2 >= FIRST_SLEEPING_SET);
557
558 let count1 = world.solver_sets[set_id1 as usize].body_sims.len();
560 let count2 = world.solver_sets[set_id2 as usize].body_sims.len();
561 let (set_id1, set_id2) = if count1 < count2 {
562 (set_id2, set_id1)
563 } else {
564 (set_id1, set_id2)
565 };
566
567 {
569 let body_count = world.solver_sets[set_id2 as usize].body_sims.len();
570 for i in 0..body_count {
571 let sim_src = world.solver_sets[set_id2 as usize].body_sims[i];
572
573 let target_count = world.solver_sets[set_id1 as usize].body_sims.len() as i32;
574 let body = &mut world.bodies[sim_src.body_id as usize];
575 debug_assert!(body.set_index == set_id2);
576 body.set_index = set_id1;
577 body.local_index = target_count;
578
579 world.solver_sets[set_id1 as usize].body_sims.push(sim_src);
580 }
581 }
582
583 {
585 let contact_count = world.solver_sets[set_id2 as usize].contact_indices.len();
586 for i in 0..contact_count {
587 let contact_index = world.solver_sets[set_id2 as usize].contact_indices[i];
588
589 let target_count = world.solver_sets[set_id1 as usize].contact_indices.len() as i32;
590 let contact = &mut world.contacts[contact_index as usize];
591 debug_assert!(contact.set_index == set_id2);
592 contact.set_index = set_id1;
593 contact.local_index = target_count;
594
595 world.solver_sets[set_id1 as usize]
596 .contact_indices
597 .push(contact_index);
598 }
599 }
600
601 {
603 let joint_count = world.solver_sets[set_id2 as usize].joint_sims.len();
604 for i in 0..joint_count {
605 let joint_src = world.solver_sets[set_id2 as usize].joint_sims[i];
606
607 let target_count = world.solver_sets[set_id1 as usize].joint_sims.len() as i32;
608 let joint = &mut world.joints[joint_src.joint_id as usize];
609 debug_assert!(joint.set_index == set_id2);
610 joint.set_index = set_id1;
611 joint.local_index = target_count;
612
613 world.solver_sets[set_id1 as usize]
614 .joint_sims
615 .push(joint_src);
616 }
617 }
618
619 {
621 let island_count = world.solver_sets[set_id2 as usize].island_sims.len();
622 for i in 0..island_count {
623 let island_src = world.solver_sets[set_id2 as usize].island_sims[i];
624 let island_id = island_src.island_id;
625
626 let target_count = world.solver_sets[set_id1 as usize].island_sims.len() as i32;
627 let island = &mut world.islands[island_id as usize];
628 island.set_index = set_id1;
629 island.local_index = target_count;
630
631 world.solver_sets[set_id1 as usize]
632 .island_sims
633 .push(island_src);
634 }
635 }
636
637 destroy_solver_set(world, set_id2);
639
640 world.validate_solver_sets();
641}
642
643pub fn transfer_body(
645 world: &mut World,
646 target_set_index: i32,
647 source_set_index: i32,
648 body_id: i32,
649) {
650 use crate::body::{body_flags, IDENTITY_BODY_STATE};
651
652 if target_set_index == source_set_index {
653 return;
654 }
655
656 let source_index = world.bodies[body_id as usize].local_index;
657 let mut source_sim =
658 world.solver_sets[source_set_index as usize].body_sims[source_index as usize];
659
660 source_sim.flags &=
662 !(body_flags::IS_FAST | body_flags::IS_SPEED_CAPPED | body_flags::HAD_TIME_OF_IMPACT);
663
664 let target_index = world.solver_sets[target_set_index as usize].body_sims.len() as i32;
665 world.solver_sets[target_set_index as usize]
666 .body_sims
667 .push(source_sim);
668
669 {
671 let source_sims = &mut world.solver_sets[source_set_index as usize].body_sims;
672 let moved_index = source_sims.len() as i32 - 1;
673 source_sims.swap_remove(source_index as usize);
674 if moved_index != source_index {
675 let moved_id = source_sims[source_index as usize].body_id;
677 let moved_body = &mut world.bodies[moved_id as usize];
678 debug_assert!(moved_body.local_index == moved_index);
679 moved_body.local_index = source_index;
680 }
681 }
682
683 if source_set_index == AWAKE_SET {
684 world.solver_sets[source_set_index as usize]
685 .body_states
686 .swap_remove(source_index as usize);
687 } else if target_set_index == AWAKE_SET {
688 let mut state = IDENTITY_BODY_STATE;
689 state.flags = world.bodies[body_id as usize].flags;
690 world.solver_sets[target_set_index as usize]
691 .body_states
692 .push(state);
693 }
694
695 let body = &mut world.bodies[body_id as usize];
696 body.set_index = target_set_index;
697 body.local_index = target_index;
698}
699
700pub fn transfer_joint(
702 world: &mut World,
703 target_set_index: i32,
704 source_set_index: i32,
705 joint_id: i32,
706) {
707 use crate::constants::GRAPH_COLOR_COUNT;
708 use crate::constraint_graph::{add_joint_to_graph, remove_joint_from_graph};
709
710 if target_set_index == source_set_index {
711 return;
712 }
713
714 let local_index = world.joints[joint_id as usize].local_index;
715 let color_index = world.joints[joint_id as usize].color_index;
716
717 let source_sim = if source_set_index == AWAKE_SET {
719 debug_assert!(0 <= color_index && color_index < GRAPH_COLOR_COUNT);
720 world.constraint_graph.colors[color_index as usize].joint_sims[local_index as usize]
721 } else {
722 debug_assert!(color_index == NULL_INDEX);
723 world.solver_sets[source_set_index as usize].joint_sims[local_index as usize]
724 };
725
726 if target_set_index == AWAKE_SET {
728 add_joint_to_graph(world, source_sim, joint_id);
729 world.joints[joint_id as usize].set_index = AWAKE_SET;
730 } else {
731 let target_count = world.solver_sets[target_set_index as usize]
732 .joint_sims
733 .len() as i32;
734 {
735 let joint = &mut world.joints[joint_id as usize];
736 joint.set_index = target_set_index;
737 joint.local_index = target_count;
738 joint.color_index = NULL_INDEX;
739 }
740 world.solver_sets[target_set_index as usize]
741 .joint_sims
742 .push(source_sim);
743 }
744
745 if source_set_index == AWAKE_SET {
747 let body_id_a = world.joints[joint_id as usize].edges[0].body_id;
748 let body_id_b = world.joints[joint_id as usize].edges[1].body_id;
749 remove_joint_from_graph(world, body_id_a, body_id_b, color_index, local_index);
750 } else {
751 let source_sims = &mut world.solver_sets[source_set_index as usize].joint_sims;
752 let moved_index = source_sims.len() as i32 - 1;
753 source_sims.swap_remove(local_index as usize);
754 if moved_index != local_index {
755 let moved_id = source_sims[local_index as usize].joint_id;
757 world.joints[moved_id as usize].local_index = local_index;
758 }
759 }
760}