mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-01 05:58:19 -06:00
Puppet: the first pin no longer turns the layer about it (#313)
With one Position pin and no Bend pins, every rotation and scale about the pin is free in the ARAP energy. The 1e-9 regulariser (about 1e-12 after the edge-length scaling) is too weak to hold them, so the CG solve turned round-off into a visible rotation of several pixels, depending on where the pin landed. The exact solution is the rest mesh translated by the pin's offset, so deform() returns it directly in that case. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
d60cb71e29
commit
2cd7a36d66
@@ -306,3 +306,20 @@ fn groups_round_trip_serde() {
|
||||
assert_eq!(m[0].pins[1].kind, PinKind::Starch);
|
||||
assert_eq!(m[0].pins[0].position, [3.0, 4.0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_pin_on_a_traced_solid_does_not_rotate_it() {
|
||||
// #313: an 800×600 solid traced from its alpha, as the Puppet Pin tool builds it. One
|
||||
// pin is not enough to rotate or scale the mesh, so it may only translate it.
|
||||
let (w, h) = (800, 600);
|
||||
let o = outline(&vec![1.0; w * h], w, h, 1.0, [0.0; 2], [400.0, 300.0], 3.0);
|
||||
let m = triangulate(&o, &MeshOpts::default());
|
||||
check_valid(&m);
|
||||
for at in [[40.0, 40.0], [760.0, 30.0], [20.0, 580.0], [790.0, 590.0], [400.0, 300.0], [123.0, 456.0]] {
|
||||
for t in [[0.0, 0.0], [35.0, -20.0]] {
|
||||
let d = solve(&m, &[pin(PinKind::Position, at, [at[0] + t[0], at[1] + t[1]])], 2);
|
||||
let err = d.iter().zip(&m.verts).map(|(a, b)| (a[0] - b[0] - t[0]).hypot(a[1] - b[1] - t[1])).fold(0.0, f64::max);
|
||||
assert!(err < 1e-6, "pin at {at:?} moved by {t:?}: vertices off by up to {err} px");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user