collective

Browse / github.com/storytold/effectcraft / patch

Fix #2201: one undo reverts a whole mask free-transform drag

mask navigation Hi. Tested 0.16.0. Not able to manually draw and select multiple points on mask. After scaling the mask, pressing undo only undo a small increment of the overall scaling size. It doesn't revert back to the size before applying scaling. Overall, this version has improved a lot. Great work !

Status
open
Score
0.00
Adopters
0 other operators
Author
jerome25000 (imported from pull request #2426, open)
Base
362474a843ddb67555d4c2d19ad6a590388aad0e
Size
+23 −4 in 2 files
Review
pending
Shared
2026-10-28
Id
3bbe2757cce3413949b1dc6d

How it went

No other operator has reported using it yet.

Diff

raw · crates/engine/src/commands/paths.rs crates/engine/src/tests_stubs.rs

diff --git a/crates/engine/src/commands/paths.rs b/crates/engine/src/commands/paths.rs--- a/crates/engine/src/commands/paths.rs+++ b/crates/engine/src/commands/paths.rs@@ -8,7 +8,7 @@ use effectcraft_project::{FeatherFalloff, GroupKind, ItemId, LayerId, LayerSourc use serde_json::{Value, json};  use super::layer_menu::target_masks;-use super::{CommandSpec, b_p, bad, f_p, has_comp, has_layers, layer_mut, layer_p, str_p};+use super::{CommandSpec, b_p, bad, f_p, has_comp, has_layers, layer_mut, layer_p, merge_p, str_p}; use crate::{EngineError, Result, Session, VertexRef, cmd};  // ---------------------------------------------------------------- RotoBezier@@ -308,7 +308,7 @@ fn free_transform(s: &mut Session, p: &Value) -> Result<Value> {         let y = v[1] * scale[1];         [x * c - y * sn, x * sn + y * c]     };-    let n = s.edit("Free Transform Points", None, |proj, _| {+    let n = s.edit("Free Transform Points", merge_p(p), |proj, _| {         let mut n = 0;         for (lid, uid) in &targets {             let l = layer_mut(proj, cid, *lid)?;@@ -541,8 +541,8 @@ pub fn specs() -> Vec<CommandSpec> {             "path.freeTransform",             "Free Transform Points",             ["Layer", "Mask and Shape Path"],-            None,-            "{layer?, mask?, scale?: % | [x%, y%], rotation?: deg, offset?: [dx, dy], anchor?: [x, y]}",+            None, +            "{layer?, mask?, scale?: % | [x%, y%], rotation?: deg, offset?: [dx, dy], anchor?: [x, y], merge?: string}",             has_layers,             free_transform         ),diff --git a/crates/engine/src/tests_stubs.rs b/crates/engine/src/tests_stubs.rs--- a/crates/engine/src/tests_stubs.rs+++ b/crates/engine/src/tests_stubs.rs@@ -583,3 +583,22 @@ fn convert_to_bezier_reads_the_shape_as_it_is_set() {     let mid = |v: &[f64]| (v.iter().copied().fold(f64::MAX, f64::min) + v.iter().copied().fold(f64::MIN, f64::max)) / 2.0;     assert!((mid(&xs) - 10.0).abs() < 1e-6 && (mid(&ys) - 5.0).abs() < 1e-6, "centred on the position: {:?} {:?}", mid(&xs), mid(&ys)); }++/// #2201: a viewer drag on the free-transform box sends one `path.freeTransform` per frame with a+/// shared merge key; one undo must revert the whole drag, not the last frame.+#[test]+fn free_transform_drag_undoes_in_one_step() {+    let mut s = comp();+    let (l, m) = masked(&mut s);+    s.state.selected_vertices.clear();+    let before = mask_path(&s, l, m);+    for k in [90, 90, 90, 90] {+        s.execute("path.freeTransform", json!({"layer": l, "scale": k, "merge": "viewer-drag-1"})).unwrap();+    }+    let after = mask_path(&s, l, m);+    assert_ne!(after, before);+    s.execute("edit.undo", json!({})).unwrap();+    assert_eq!(mask_path(&s, l, m), before, "one undo reverts the whole drag");+    s.execute("edit.redo", json!({})).unwrap();+    assert_eq!(mask_path(&s, l, m), after, "redo restores the whole drag");+}