use alice_physics::interpolation::{
lerp_fix128, lerp_vec3, slerp, BodySnapshot, InterpolationState, WorldSnapshot,
};
use alice_physics::math::{Fix128, QuatFix, Vec3Fix};
use alice_physics::solver::{PhysicsConfig, PhysicsWorld, RigidBody};
fn linear_motion_interpolation() {
println!("[interpolation] -- capture_and_push over a physics step, interpolate_all --");
let config = PhysicsConfig {
gravity: Vec3Fix::ZERO,
damping: Fix128::ONE,
..PhysicsConfig::default()
};
let mut world = PhysicsWorld::new(config);
world.add_body(
RigidBody::new_dynamic(Vec3Fix::from_int(0, 0, 0), Fix128::ONE)
.with_velocity(Vec3Fix::from_int(8, 0, 0)),
);
world.add_body(RigidBody::new_static(Vec3Fix::from_int(5, 5, 5)));
let mut interp = InterpolationState::empty();
println!(
"[interpolation] empty(): body_count = {} (documented 0)",
interp.body_count()
);
assert_eq!(interp.body_count(), 0);
interp.capture_and_push(&world);
println!(
"[interpolation] 1st capture_and_push: current.len() = {}, prev.is_empty() = {}",
interp.current.len(),
interp.prev.is_empty()
);
assert_eq!(interp.current.len(), 2);
assert!(interp.prev.is_empty());
world.step(Fix128::from_ratio(1, 4));
interp.capture_and_push(&world);
println!(
"[interpolation] 2nd capture_and_push: current[0].position = {} (closed form: 0 + 8*1/4 = 2)",
interp.current.bodies[0].position
);
assert_eq!(
interp.current.bodies[0].position,
Vec3Fix::from_int(2, 0, 0)
);
let half = Fix128::from_ratio(1, 2);
let mid = interp.interpolate_all(half);
println!(
"[interpolation] interpolate_all(alpha=1/2)[0].0 = {} (closed form midpoint: (0+2)/2 = 1)",
mid[0].0
);
assert_eq!(mid[0].0, Vec3Fix::from_int(1, 0, 0));
assert_eq!(
mid[1].0,
Vec3Fix::from_int(5, 5, 5),
"static body does not move"
);
let at_zero = interp.interpolate_position(0, Fix128::ZERO);
let at_one = interp.interpolate_position(0, Fix128::ONE);
println!(
"[interpolation] interpolate_position(alpha=0) = {} (== prev), interpolate_position(alpha=1) = {} (== current)",
at_zero, at_one
);
assert_eq!(at_zero, interp.prev.bodies[0].position);
assert_eq!(at_one, interp.current.bodies[0].position);
let (p, q) = interp.interpolate(0, half);
println!("[interpolation] interpolate(0, 1/2) = ({p}, {q})");
assert_eq!(p, mid[0].0);
assert_eq!(q, QuatFix::IDENTITY);
}
fn manual_snapshot_rotation() {
println!("[interpolation] -- from_body / WorldSnapshot::capture / interpolate_rotation --");
let mut world_a = PhysicsWorld::new(PhysicsConfig::default());
world_a.add_body(RigidBody::new_dynamic(
Vec3Fix::from_int(1, 2, 3),
Fix128::ONE,
));
let mut world_b = PhysicsWorld::new(PhysicsConfig::default());
world_b.add_body(
RigidBody::new_dynamic(Vec3Fix::from_int(1, 2, 3), Fix128::ONE).with_rotation(
QuatFix::new(
Fix128::ZERO,
Fix128::from_int(6),
Fix128::ZERO,
Fix128::from_int(7),
),
),
);
let snap_a = WorldSnapshot::capture(&world_a);
let snap_b = WorldSnapshot::capture(&world_b);
println!(
"[interpolation] capture: a.bodies[0].rotation = {}, b.bodies[0].rotation = {}",
snap_a.bodies[0].rotation, snap_b.bodies[0].rotation
);
assert_eq!(
snap_a.bodies[0],
BodySnapshot::from_body(&world_a.bodies[0])
);
assert_eq!(snap_a.bodies[0].rotation, QuatFix::IDENTITY);
let snap_b_rotation = snap_b.bodies[0].rotation;
let interp = InterpolationState::new(snap_a, snap_b);
let half = Fix128::from_ratio(1, 2);
let q_half = interp.interpolate_rotation(0, half);
let b = snap_b_rotation;
let expected = QuatFix::new(Fix128::ZERO, b.y, Fix128::ZERO, Fix128::ONE + b.w).normalize();
println!(
"[interpolation] interpolate_rotation(alpha=1/2) = {q_half} (closed form: {expected})"
);
let close = |p: Fix128, q: Fix128| (p - q).abs() < Fix128::from_raw(0, 1 << 20);
assert!(
close(q_half.x, expected.x)
&& close(q_half.y, expected.y)
&& close(q_half.z, expected.z)
&& close(q_half.w, expected.w),
"slerp at 1/2 is not the bisector"
);
}
fn lerp_primitives() {
println!("[interpolation] -- lerp_fix128 / lerp_vec3 / slerp primitives --");
let a = Fix128::from_int(-4);
let b = Fix128::from_int(12);
let quarter = Fix128::from_ratio(1, 4);
let r = lerp_fix128(a, b, quarter);
println!("[interpolation] lerp_fix128(-4, 12, 1/4) = {r} (closed form: -4 + 16*1/4 = 0)");
assert_eq!(r, Fix128::ZERO);
let va = Vec3Fix::from_int(-4, 0, 12);
let vb = Vec3Fix::from_int(12, 8, -4);
let rv = lerp_vec3(va, vb, quarter);
println!(
"[interpolation] lerp_vec3((-4,0,12), (12,8,-4), 1/4) = {rv} (componentwise lerp_fix128)"
);
assert_eq!(
rv,
Vec3Fix::new(Fix128::ZERO, Fix128::from_int(2), Fix128::from_int(8))
);
let q = QuatFix::new(Fix128::ONE, Fix128::ZERO, Fix128::ZERO, Fix128::ZERO);
let same = slerp(q, q, Fix128::from_ratio(3, 7));
println!("[interpolation] slerp(q, q, 3/7) = {same} (must equal q exactly)");
assert_eq!(same, q);
}
fn main() {
linear_motion_interpolation();
manual_snapshot_rotation();
lerp_primitives();
println!("[interpolation] all closed-form checks passed");
}