use crate::body::{body_get_transform, create_body};
use crate::core::{hash, HASH_INIT};
use crate::geometry::make_square;
use crate::id::BodyId;
use crate::joint::create_revolute_joint;
use crate::math_functions::{make_rot, to_pos, Vec2, WorldTransform, PI};
use crate::shape::create_polygon_shape;
use crate::types::{
default_body_def, default_revolute_joint_def, default_shape_def, default_world_def, BodyType,
};
use crate::world::{world_get_awake_body_count, world_get_body_events, world_step, World};
struct FallingHingeData {
body_ids: Vec<BodyId>,
step_count: i32,
sleep_step: i32,
hash: u32,
}
fn hash_transform(h: u32, transform: &WorldTransform) -> u32 {
let mut bytes: Vec<u8> = Vec::with_capacity(24);
bytes.extend_from_slice(&transform.p.x.to_le_bytes());
bytes.extend_from_slice(&transform.p.y.to_le_bytes());
bytes.extend_from_slice(&transform.q.c.to_le_bytes());
bytes.extend_from_slice(&transform.q.s.to_le_bytes());
hash(h, &bytes)
}
fn create_falling_hinges(world: &mut World) -> FallingHingeData {
{
let mut body_def = default_body_def();
body_def.position = to_pos(Vec2 { x: 0.0, y: -1.0 });
let ground_id = create_body(world, &body_def);
let box_poly = crate::geometry::make_box(40.0, 1.0);
let shape_def = default_shape_def();
create_polygon_shape(world, ground_id, &shape_def, &box_poly);
}
let column_count = 4usize;
let row_count = 20usize;
let body_count = row_count * column_count;
let mut body_ids: Vec<BodyId> = Vec::with_capacity(body_count);
let h = 0.25f32;
let box_poly = make_square(h);
let shape_def = default_shape_def();
let mut joint_def = default_revolute_joint_def();
joint_def.enable_limit = true;
joint_def.lower_angle = -0.1 * PI;
joint_def.upper_angle = 0.2 * PI;
joint_def.enable_spring = true;
joint_def.hertz = 1.0;
joint_def.damping_ratio = 1.0;
joint_def.enable_motor = true;
joint_def.max_motor_torque = 0.25;
joint_def.base.local_frame_a.p = Vec2 { x: -h, y: h };
joint_def.base.local_frame_b.p = Vec2 { x: -h, y: -h };
joint_def.base.constraint_hertz = 60.0;
joint_def.base.constraint_damping_ratio = 0.0;
joint_def.base.draw_scale = 0.5;
let offset = 0.4 * h;
let dx = 10.0 * h;
let x_base = -0.5 * dx * (column_count as f32 - 1.0);
for j in 0..column_count {
let x = x_base + j as f32 * dx;
let mut prev_body_id = BodyId::default();
for i in 0..row_count {
let mut body_def = default_body_def();
body_def.type_ = BodyType::Dynamic;
body_def.position = to_pos(Vec2 {
x: x + offset * i as f32,
y: h + 2.0 * h * i as f32,
});
let angle = if (i & 1) == 0 { -0.1 } else { 0.1 };
body_def.rotation = make_rot(angle);
let body_id = create_body(world, &body_def);
if (i & 1) == 0 {
prev_body_id = body_id;
} else {
joint_def.base.body_id_a = prev_body_id;
joint_def.base.body_id_b = body_id;
create_revolute_joint(world, &joint_def);
prev_body_id = BodyId::default();
}
create_polygon_shape(world, body_id, &shape_def, &box_poly);
body_ids.push(body_id);
}
}
assert_eq!(body_ids.len(), body_count);
FallingHingeData {
body_ids,
step_count: 0,
sleep_step: -1,
hash: 0,
}
}
fn update_falling_hinges(world: &World, data: &mut FallingHingeData) -> bool {
if data.hash == 0 {
let body_events = world_get_body_events(world);
if body_events.is_empty() {
let awake_count = world_get_awake_body_count(world);
assert_eq!(awake_count, 0);
data.hash = HASH_INIT;
for body_id in &data.body_ids {
let transform = body_get_transform(world, *body_id);
data.hash = hash_transform(data.hash, &transform);
}
data.sleep_step = data.step_count;
}
}
data.step_count += 1;
data.hash != 0
}
#[cfg(feature = "double-precision")]
const EXPECTED_SLEEP_STEP: i32 = 313;
#[cfg(feature = "double-precision")]
const EXPECTED_HASH: u32 = 0xF7C3082A;
#[cfg(not(feature = "double-precision"))]
const EXPECTED_SLEEP_STEP: i32 = 294;
#[cfg(not(feature = "double-precision"))]
const EXPECTED_HASH: u32 = 0x006F0F5E;
#[test]
fn falling_hinges() {
let world_def = default_world_def();
let mut world = World::new(&world_def);
let mut data = create_falling_hinges(&mut world);
let time_step = 1.0 / 60.0;
let step_limit = 500;
for _ in 0..step_limit {
let sub_step_count = 4;
world_step(&mut world, time_step, sub_step_count);
let done = update_falling_hinges(&world, &mut data);
if done {
break;
}
}
if data.sleep_step != EXPECTED_SLEEP_STEP || data.hash != EXPECTED_HASH {
println!(" sleepStep={} hash=0x{:08X}", data.sleep_step, data.hash);
}
assert_eq!(data.sleep_step, EXPECTED_SLEEP_STEP);
assert_eq!(data.hash, EXPECTED_HASH);
}