use rapier3d::pipeline::PhysicsWorld;
use rapier3d::prelude::*;
fn world_with_ground() -> PhysicsWorld {
let mut world = PhysicsWorld::new();
let ground = RigidBodyBuilder::fixed().translation(Vector::new(0.0, -0.5, 0.0));
world.insert(ground, ColliderBuilder::cuboid(100.0, 0.5, 100.0));
world
}
fn island_of(world: &PhysicsWorld, h: RigidBodyHandle) -> Option<u32> {
world.islands.persistent_island_of(&world.bodies, h)
}
fn same_island(world: &PhysicsWorld, h1: RigidBodyHandle, h2: RigidBodyHandle) -> bool {
let i1 = island_of(world, h1);
i1.is_some() && i1 == island_of(world, h2)
}
fn insert_box(world: &mut PhysicsWorld, x: Real, y: Real) -> RigidBodyHandle {
world
.insert(
RigidBodyBuilder::dynamic().translation(Vector::new(x, y, 0.0)),
ColliderBuilder::cuboid(0.5, 0.5, 0.5),
)
.0
}
const SETTLE_STEPS: usize = 240;
#[test]
fn merge_on_touch_split_on_separation() {
let mut world = world_with_ground();
let bottom = insert_box(&mut world, 0.0, 0.5);
let top = insert_box(&mut world, 0.0, 1.5);
let lone = insert_box(&mut world, 20.0, 0.5);
for _ in 0..SETTLE_STEPS {
world.step();
}
assert!(
same_island(&world, bottom, top),
"touching boxes must merge"
);
assert!(
!same_island(&world, bottom, lone),
"distant boxes must not share an island"
);
world.bodies[top].set_translation(Vector::new(40.0, 0.5, 0.0), true);
for _ in 0..SETTLE_STEPS {
world.step();
}
assert!(
!same_island(&world, bottom, top),
"separated boxes must end up in distinct islands after the deferred split"
);
}
#[test]
fn joint_links_and_split_after_removal() {
let mut world = world_with_ground();
let a = insert_box(&mut world, 0.0, 0.5);
let b = insert_box(&mut world, 20.0, 0.5);
for _ in 0..SETTLE_STEPS {
world.step();
}
assert!(!same_island(&world, a, b));
let joint = world.impulse_joints.insert(
a,
b,
RopeJointBuilder::new(30.0)
.local_anchor1(Vector::ZERO)
.local_anchor2(Vector::ZERO),
true,
);
world.step();
assert!(same_island(&world, a, b), "a joint must merge islands");
world.impulse_joints.remove(joint, true);
for _ in 0..SETTLE_STEPS {
world.step();
}
assert!(
!same_island(&world, a, b),
"the deferred split must separate joint-disconnected islands"
);
}
#[test]
fn body_removal_splits_row() {
let mut world = world_with_ground();
let left = insert_box(&mut world, 0.0, 0.5);
let middle = insert_box(&mut world, 1.0, 0.5);
let right = insert_box(&mut world, 2.0, 0.5);
for _ in 0..SETTLE_STEPS {
world.step();
}
assert!(same_island(&world, left, right));
world.remove_body(middle);
for _ in 0..SETTLE_STEPS {
world.step();
}
assert!(
!same_island(&world, left, right),
"removing the bridging body must split the island"
);
}
#[test]
fn separation_splits_immediately() {
let mut world = world_with_ground();
let bottom = insert_box(&mut world, 0.0, 0.5);
let top = insert_box(&mut world, 0.0, 1.5);
for _ in 0..SETTLE_STEPS {
world.step();
}
assert!(same_island(&world, bottom, top));
world.bodies[top].set_translation(Vector::new(40.0, 0.5, 0.0), true);
world.step();
assert!(
!same_island(&world, bottom, top),
"the local split must separate them in the very step they stop touching"
);
}
#[test]
fn detached_chunk_moves_out_with_its_links() {
let mut world = world_with_ground();
let row: Vec<_> = (0..6)
.map(|i| insert_box(&mut world, i as Real * 1.0, 0.5))
.collect();
for _ in 0..SETTLE_STEPS {
world.step();
}
assert!(same_island(&world, row[0], row[5]), "the row is one island");
for (i, handle) in row.iter().enumerate().skip(3) {
let x = 30.0 + (i - 3) as Real;
world.bodies[*handle].set_translation(Vector::new(x, 0.5, 0.0), true);
}
for _ in 0..SETTLE_STEPS {
world.step();
}
assert!(
!same_island(&world, row[0], row[3]),
"the two halves must end up in different islands"
);
assert!(
same_island(&world, row[0], row[2]),
"the left half stays one island"
);
assert!(
same_island(&world, row[3], row[5]),
"the detached half moves out as one island, links included"
);
}
#[test]
fn type_change_bridges_and_unbridges() {
let mut world = world_with_ground();
let left = insert_box(&mut world, 0.0, 0.5);
let middle = insert_box(&mut world, 1.0, 0.5);
let right = insert_box(&mut world, 2.0, 0.5);
for _ in 0..SETTLE_STEPS {
world.step();
}
assert!(same_island(&world, left, right));
world.bodies[middle].set_body_type(RigidBodyType::Fixed, true);
for _ in 0..SETTLE_STEPS {
world.step();
}
assert_eq!(
island_of(&world, middle),
None,
"fixed bodies have no island"
);
assert!(
!same_island(&world, left, right),
"a fixed body doesn't connect its neighbors"
);
world.bodies[middle].set_body_type(RigidBodyType::Dynamic, true);
for _ in 0..8 {
world.step();
}
assert!(
same_island(&world, left, right),
"back to dynamic, the middle body must re-merge the row"
);
}
#[test]
fn disable_enable_bridges_and_unbridges() {
let mut world = world_with_ground();
let left = insert_box(&mut world, 0.0, 0.5);
let middle = insert_box(&mut world, 1.0, 0.5);
let right = insert_box(&mut world, 2.0, 0.5);
for _ in 0..SETTLE_STEPS {
world.step();
}
assert!(same_island(&world, left, right));
world.bodies[middle].set_enabled(false);
for _ in 0..SETTLE_STEPS {
world.step();
}
assert_eq!(island_of(&world, middle), None);
assert!(!same_island(&world, left, right));
world.bodies[middle].set_enabled(true);
for _ in 0..8 {
world.step();
}
assert!(same_island(&world, left, right));
}
#[test]
fn multibody_fixed_root_branches_share_island() {
let mut world = world_with_ground();
let root = world
.insert(
RigidBodyBuilder::fixed().translation(Vector::new(0.0, 5.0, 0.0)),
ColliderBuilder::ball(0.1),
)
.0;
let child_a = insert_box(&mut world, -2.0, 5.0);
let child_b = insert_box(&mut world, 2.0, 5.0);
let joint_a = world
.multibody_joints
.insert(
root,
child_a,
FixedJointBuilder::new().local_anchor1(Vector::new(-2.0, 0.0, 0.0)),
true,
)
.unwrap();
let _joint_b = world
.multibody_joints
.insert(
root,
child_b,
FixedJointBuilder::new().local_anchor1(Vector::new(2.0, 0.0, 0.0)),
true,
)
.unwrap();
world.step();
assert!(
same_island(&world, child_a, child_b),
"both branches of a fixed-root multibody must share an island"
);
assert_eq!(island_of(&world, root), None);
world.multibody_joints.remove(joint_a, true);
for _ in 0..SETTLE_STEPS {
world.step();
}
assert!(
!same_island(&world, child_a, child_b),
"a detached branch must split off"
);
}