mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-02 22:42:24 -06:00
Effects: Turbulent Displace displaces the whole layer, not one quadrant (#227)
Edge pinning (Turbulent Displace's default Pin All, Wave Warp's Pinning, Bulge's Pin All Edges) holds the layer bounds still: a comp-sized rectangle for shape and text layers. That rectangle started at the layer's origin, but a shape layer's content surrounds its origin (anchor 0,0), so everything left of or above the origin counted as outside the layer and was pinned: only the bottom-right quadrant of a centred shape moved. The renderer now passes the bounds' top-left in layer coordinates (EffectEnv::bounds_origin): (-w/2, -h/2) for layers without a source rectangle, (0, 0) otherwise. util::pin_rect places the bounds there and the three effects use it on the CPU and the GPU. Effect point parameters keep their current layer coordinates. Test: edge_pinning_displaces_all_of_a_centred_shape_layer, edge_pinning_on_a_shape_layer (GPU vs CPU). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
172b7cfcc0
commit
754e80bdf9
@@ -895,7 +895,14 @@ impl<'a> Renderer<'a> {
|
||||
let Some(fx) = layer.effects() else { return };
|
||||
let lt = layer.layer_time(ctx.time);
|
||||
let size = source_size(self.project, layer);
|
||||
let layer_size = if size.0 == 0 { [ctx.comp.width as f64, ctx.comp.height as f64] } else { [size.0 as f64, size.1 as f64] };
|
||||
// Layers without a source rectangle (shape, text) have comp-sized bounds centred on
|
||||
// their origin, which their content surrounds.
|
||||
let (layer_size, bounds_origin) = if size.0 == 0 {
|
||||
let (w, h) = (ctx.comp.width as f64, ctx.comp.height as f64);
|
||||
([w, h], [-w / 2.0, -h / 2.0])
|
||||
} else {
|
||||
([size.0 as f64, size.1 as f64], [0.0; 2])
|
||||
};
|
||||
let mask_shapes = masks::shapes(ctx, layer);
|
||||
let host = FxHost { r: self, ctx, layer, index: Default::default() };
|
||||
let env = EffectEnv {
|
||||
@@ -907,6 +914,7 @@ impl<'a> Renderer<'a> {
|
||||
working_space: self.pipe.space,
|
||||
working_linear: self.pipe.linear,
|
||||
shutter: self.mb_on(ctx, layer).then_some((ctx.comp.shutter_angle, ctx.comp.shutter_phase, ctx.comp.motion_blur_samples)),
|
||||
bounds_origin,
|
||||
};
|
||||
// Video effects in stack order (index, group, spec); disabled and audio effects skipped.
|
||||
let stack: Vec<(usize, &effectcraft_project::PropGroup, &'static effectcraft_effects::EffectSpec)> = fx
|
||||
|
||||
@@ -425,3 +425,34 @@ fn auto_backend_sends_2d_and_3d_comps_to_the_gpu() {
|
||||
p.settings.gpu_acceleration = false;
|
||||
assert!(!asked(&p, crate::Backend::Auto));
|
||||
}
|
||||
|
||||
/// #227: a shape layer's content surrounds its origin, so edge pinning (Turbulent Displace's
|
||||
/// default Pin All, Wave Warp's All Edges) must measure from comp-sized bounds centred on it;
|
||||
/// bounds starting at the origin pinned all but the bottom-right quadrant.
|
||||
#[test]
|
||||
fn edge_pinning_displaces_all_of_a_centred_shape_layer() {
|
||||
for (id, vals) in [("ec.distort.turbulentdisplace", vec![]), ("ec.distort.wavewarp", vec![("pinning", Value::Enum(1))])] {
|
||||
let (mut p, cid, comp) = setup();
|
||||
let mut l = build::layer(&mut p, &comp, "Shape Layer 1", LayerSource::Shape, (200, 100), None);
|
||||
let mut next = p.next_id;
|
||||
let mut ids = Ids(&mut next);
|
||||
let rect = build::shape_rect(&mut ids, [180.0, 80.0], [0.0, 0.0], 0.0);
|
||||
let fill = build::shape_fill(&mut ids, [1.0, 0.0, 0.0, 1.0]);
|
||||
let g = build::shape_group(&mut ids, "Rectangle 1", vec![rect, fill]);
|
||||
p.next_id = next;
|
||||
l.props.sub_mut("contents").unwrap().children.push(g.into());
|
||||
p.comp_mut(cid).unwrap().layers.push(l.clone());
|
||||
let plain = render_frame(&p, cid, Tick::ZERO, 1.0);
|
||||
add_effect_200(&mut p, &mut l, id, &vals);
|
||||
p.comp_mut(cid).unwrap().layers = vec![l];
|
||||
let warped = render_frame(&p, cid, Tick::ZERO, 1.0);
|
||||
// Alpha change per comp quadrant (TL, TR, BL, BR): the rectangle's edges move in each.
|
||||
let mut q = [0.0f32; 4];
|
||||
for y in 0..100 {
|
||||
for x in 0..200 {
|
||||
q[(x >= 100) as usize + 2 * (y >= 50) as usize] += (plain.get(x, y)[3] - warped.get(x, y)[3]).abs();
|
||||
}
|
||||
}
|
||||
assert!(q.iter().all(|&d| d > 20.0), "{id}: per-quadrant change {q:?}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user