Wave Warp: cover the Top/Bottom edge pinning paths end to end (#1181)

The pinning regression only exercised All Edges (and Left Edge in the CPU
unit test), so the reported 'does not work with Top/Bottom Edge' had no
coverage to catch or clear it. This holds the pinned first/last row in a
CPU test at Direction 90, and adds CPU/GPU parity cases for Top (4),
Bottom (6), diagonal directions, and single-edge pinning on shape layers.
CPU pinning verified correct on main: the guards in dst_wave_pin/wave_pin
agree; the gap was that neither edge path was tested.
This commit is contained in:
Vikram Bhaskaran
2026-10-13 23:40:32 +11:00
parent 549e9d8251
commit 2720ae5fe2
2 changed files with 21 additions and 1 deletions
+13 -1
View File
@@ -556,8 +556,20 @@ mod tests {
let (a, b) = (o.img.get(pad, y + pad), im.get(0, y));
assert!((0..4).all(|i| (a[i] - b[i]).abs() < 0.01), "row {y}: {a:?} vs {b:?}");
}
let free = run("ec.distort.wavewarp", &[("direction", num(0.0)), ("height", num(5.0))], im);
let free = run("ec.distort.wavewarp", &[("direction", num(0.0)), ("height", num(5.0))], im.clone());
assert!(free.img.data.iter().zip(&o.img.data).any(|(a, b)| differs(*a, *b)));
// #1181: direction 90° → vertical displacement. Top Edge (4) holds the first row,
// Bottom Edge (6) the last, and the rest of the layer still moves.
for (pin, edge_row) in [(Value::Enum(4), 0i64), (Value::Enum(6), 29i64)] {
let set = [("direction", num(90.0)), ("height", num(5.0)), ("pinning", pin)];
let o = run("ec.distort.wavewarp", &set, im.clone());
let pad = ((o.img.width - im.width) / 2) as i64;
for x in 0..40 {
let (a, b) = (o.img.get(x + pad, edge_row + pad), im.get(x, edge_row));
assert!((0..4).all(|i| (a[i] - b[i]).abs() < 0.01), "pin {edge_row} col {x}: {a:?} vs {b:?}");
}
assert!(o.img.data.iter().zip(im.data.iter()).any(|(a, b)| differs(*a, *b)), "pin {edge_row}: layer must still distort");
}
}
#[test]
+8
View File
@@ -28,6 +28,11 @@ fn twirl_bulge_ripple_wave_warp() {
effect_case("ec.distort.wavewarp", &[("waveType", e(2)), ("pinning", e(1)), ("phase", n(400.0)), ("speed", n(-2.0))]);
effect_case("ec.distort.wavewarp", &[("waveType", e(8)), ("pinning", e(2)), ("height", n(-5.0)), ("direction", n(200.0))]);
effect_case("ec.distort.wavewarp", &[("waveType", e(4)), ("pinning", e(7)), ("width", n(33.0))]);
// #1181: the single-edge pinning paths, with the default vertical displacement direction.
effect_case("ec.distort.wavewarp", &[("direction", n(90.0)), ("height", n(7.0)), ("width", n(25.0)), ("pinning", e(4))]);
effect_case("ec.distort.wavewarp", &[("direction", n(90.0)), ("height", n(7.0)), ("width", n(19.0)), ("pinning", e(6)), ("waveType", e(1))]);
effect_case("ec.distort.wavewarp", &[("direction", n(45.0)), ("height", n(-6.0)), ("pinning", e(4)), ("speed", n(2.0))]);
effect_case("ec.distort.wavewarp", &[("direction", n(45.0)), ("height", n(-6.0)), ("pinning", e(6)), ("speed", n(2.0))]);
}
#[test]
@@ -79,6 +84,9 @@ fn shape_case(id: &str, vals: &[(&str, Value)]) {
fn edge_pinning_on_a_shape_layer() {
shape_case("ec.distort.turbulentdisplace", &[("size", n(30.0)), ("amount", n(20.0))]);
shape_case("ec.distort.wavewarp", &[("pinning", e(1)), ("height", n(6.0)), ("width", n(25.0))]);
// #1181: single-edge pinning over the layer rect, not just All Edges.
shape_case("ec.distort.wavewarp", &[("pinning", e(4)), ("direction", n(90.0)), ("height", n(6.0)), ("width", n(25.0))]);
shape_case("ec.distort.wavewarp", &[("pinning", e(6)), ("direction", n(90.0)), ("height", n(6.0)), ("width", n(25.0))]);
shape_case(
"ec.distort.bulge",
&[("pinAllEdges", on()), ("height", n(2.0)), ("hradius", n(40.0)), ("vradius", n(30.0)), ("center", Value::Vec2([28.5, 20.5]))],