diff --git a/crates/effects/src/keying2.rs b/crates/effects/src/keying2.rs index f594bfbb..a03fe804 100644 --- a/crates/effects/src/keying2.rs +++ b/crates/effects/src/keying2.rs @@ -363,7 +363,9 @@ fn wire_removal(ctx: &EffectCtx, mut b: Buf) -> Buf { let (qx, qy) = (x as f64 + 0.5 - ax, y as f64 + 0.5 - ay); let d = qx * nx + qy * ny; let ad = d.abs(); - if ad > half + soft_w { + // The wire is the segment A to B, not an endless line. + let along = qx * tx + qy * ty; + if !(0.0..=len).contains(&along) || ad > half + soft_w { continue; } // Positions just outside the wire on both sides. @@ -580,6 +582,24 @@ mod tests { } } + #[test] + fn wire_removal_stays_between_a_and_b() { + let mut img = Image::filled(20, 20, [0.5, 0.5, 0.5, 1.0]); + for y in 0..20 { + img.set(10, y, [0.0, 0.0, 0.0, 1.0]); + } + let out = run_fx( + "ec.key.ccsimplewireremoval", + &[("pointA", Value::Vec2([10.5, 5.0])), ("pointB", Value::Vec2([10.5, 15.0])), ("removalStyle", Value::Enum(0)), ("thickness", num(3.0))], + img.clone(), + 0.0, + EffectEnv::default(), + ); + assert!((out.img.get(10, 10)[0] - 0.5).abs() < 0.05, "inside the segment"); + assert_eq!(out.img.get(10, 2), img.get(10, 2), "before A"); + assert_eq!(out.img.get(10, 18), img.get(10, 18), "after B"); + } + #[test] fn wire_removal_frame_offset_uses_neighbour_frame() { use crate::{EffectHost, LayerPixels}; diff --git a/crates/gpu/src/fx_pixel2.rs b/crates/gpu/src/fx_pixel2.rs index 9de26f01..acba936d 100644 --- a/crates/gpu/src/fx_pixel2.rs +++ b/crates/gpu/src/fx_pixel2.rs @@ -493,7 +493,7 @@ fn wire_removal(e: &mut Enc, ctx: &EffectCtx, b: GBuf) -> Option { let mut p = Params::default(); p.u[0][0] = ctx.params.e("removalStyle"); p.f[0] = [a.0 as f32, a.1 as f32, -ty as f32, tx as f32]; - p.f[1] = [half as f32, (half * slope as f64) as f32, mirror, 0.0]; + p.f[1] = [half as f32, (half * slope as f64) as f32, mirror, len as f32]; run(e, "fp2_wire", &p, b, None, None) } diff --git a/crates/gpu/src/shaders/fx_pixel2.wgsl b/crates/gpu/src/shaders/fx_pixel2.wgsl index 21bb8b5d..a63a91fe 100644 --- a/crates/gpu/src/shaders/fx_pixel2.wgsl +++ b/crates/gpu/src/shaders/fx_pixel2.wgsl @@ -828,7 +828,7 @@ fn fp2_io_final(@builtin(global_invocation_id) gid: vec3) { } // ---------------------------------------------------------------- CC Simple Wire Removal (keying2::wire_removal) -// u[0].x = style; f[0] = (ax, ay, nx, ny); f[1] = (hw thickness, soft width, mirror). +// u[0].x = style; f[0] = (ax, ay, nx, ny); f[1] = (hw thickness, soft width, mirror, segment length). fn fp2_wire_pos(base: vec2, nrm: vec2, s: f32) -> vec4 { return sample_bilinear_clamped(src, base.x + nrm.x * s, base.y + nrm.y * s); @@ -851,7 +851,9 @@ fn fp2_wire(@builtin(global_invocation_id) gid: vec3) { let q = vec2(fx - a.x, fy - a.y); let d = q.x * nrm.x + q.y * nrm.y; let ad = abs(d); - if (ad > hw + soft_w) { + // The wire is the segment A to B: the tangent is the normal turned back by 90 degrees. + let along = q.x * nrm.y - q.y * nrm.x; + if (along < 0.0 || along > P.f[1].w || ad > hw + soft_w) { textureStore(out, p, px); return; }