mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-31 21:48:20 -06:00
#1010: recover a static Orientation's Y angle without losing precision near gimbal lock
uniform_parent_pose took the Y angle from asin, which loses precision within about 1e-6 degrees of +-90. The Y angle now comes from atan2 on cos(Y), as the X and Z angles already do. The gimbal branch and the principal-range output are unchanged. A new test parents a static Orientation with Y 1e-6 degrees off gimbal lock under a uniform parent. Its largest corner displacement was 3.3e-8 px before this change and 1.5e-14 px after. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Haiku 5.5
parent
470ea7a3af
commit
be99fecfdc
@@ -383,6 +383,29 @@ fn parenting_static_orientation_handles_signed_uniform_scale_and_gimbal_order()
|
||||
assert!(max_corners < 1e-8 && max_pixels < 2e-5, "static uniform pose must use the existing Euler order including signed scale and gimbal angles");
|
||||
}
|
||||
|
||||
/// A static Orientation just off gimbal lock (Y within 1e-6 degrees of ±90) keeps its world pose
|
||||
/// under a uniform parent. Recovering Euler angles from the matrix must not lose precision there.
|
||||
#[test]
|
||||
fn parenting_static_orientation_is_exact_just_off_gimbal_lock() {
|
||||
let mut max_corners = 0.0f64;
|
||||
for factor in [120.0, -120.0] {
|
||||
for y in [90.0 - 1e-6, -90.0 + 1e-6] {
|
||||
let (mut s, p, c) = fixture(true, true);
|
||||
set(&mut s, p, "transform/orientation", json!([0.0, 0.0, 0.0]));
|
||||
set(&mut s, p, "transform/rotationX", json!(0.0));
|
||||
set(&mut s, p, "transform/rotationY", json!(0.0));
|
||||
set(&mut s, p, "transform/rotation", json!(42.0));
|
||||
set(&mut s, p, "transform/scale", json!([factor, factor, factor]));
|
||||
set(&mut s, c, "transform/orientation", json!([23.0, y, -37.0]));
|
||||
set(&mut s, c, "transform/scale", json!([200.0, 160.0, 150.0]));
|
||||
let before = world(&s, c, Tick::ZERO);
|
||||
parent(&mut s, c, Some(p), true);
|
||||
max_corners = max_corners.max(displacement(before, world(&s, c, Tick::ZERO)));
|
||||
}
|
||||
}
|
||||
assert!(max_corners < 1e-9, "a static Orientation near gimbal lock must keep its world pose: corners {max_corners}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parenting_animated_orientation_and_nonuniform_parent_keep_authored_pose_boundary() {
|
||||
for animated in [true, false] {
|
||||
|
||||
Reference in New Issue
Block a user