From 7380b2475df87fa1d1d2273ef45c1d740a8ff262 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Thu, 12 Nov 2026 18:23:31 -0800 Subject: [PATCH] Mesh: a color at a mesh envelope's point records no undo step A mesh envelope's points carry no color, so object.mesh.addLine with a color at a point the envelope already has changes nothing. It now compares only the envelope's points and handles and records no undo step. The command text and docs/mcp.md say so. --- crates/engine/src/cmd/live/commands.rs | 2 +- crates/engine/src/cmd/live/mesh.rs | 7 +++++-- crates/engine/src/tests/envelope.rs | 6 +++++- docs/mcp.md | 3 ++- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/crates/engine/src/cmd/live/commands.rs b/crates/engine/src/cmd/live/commands.rs index 2a427be32d..0c835c7b5d 100644 --- a/crates/engine/src/cmd/live/commands.rs +++ b/crates/engine/src/cmd/live/commands.rs @@ -290,7 +290,7 @@ pub fn specs() -> Vec { "Add Mesh Line", [], None, - "{id, x, y, color? (#rrggbbaa and #rgba are refused), keepModel?: bool (as paint.setFill), tolerance?: pt (0)} add a row and a column through (x, y), inside or on the edge, to a gradient mesh (new point in `color`) or a mesh envelope; a point outside within `tolerance` of the edge adds them through the nearest point of the edge; at a point that is already a mesh point it adds no line, and records no undo step unless `color` changes it → {index}", + "{id, x, y, color? (#rrggbbaa and #rgba are refused), keepModel?: bool (as paint.setFill), tolerance?: pt (0)} add a row and a column through (x, y), inside or on the edge, to a gradient mesh (new point in `color`) or a mesh envelope; a point outside within `tolerance` of the edge adds them through the nearest point of the edge; at a point that is already a mesh point it adds no line, and records no undo step unless `color` changes a gradient mesh's point (a mesh envelope's points have no color) → {index}", has_doc, mesh_add_line ), diff --git a/crates/engine/src/cmd/live/mesh.rs b/crates/engine/src/cmd/live/mesh.rs index 3a4d1851a0..2290b56af4 100644 --- a/crates/engine/src/cmd/live/mesh.rs +++ b/crates/engine/src/cmd/live/mesh.rs @@ -298,13 +298,16 @@ pub(super) fn mesh_add_line(s: &mut Session, p: &Value) -> Result { } // The lines are added to a copy of the object. A point that is already a mesh point adds none: // when its color stays too, nothing changes and no undo step is recorded. The grid is compared, - // not the node: a mesh envelope stored without handles takes them in any edit. + // not the node: a mesh envelope stored without handles takes them in any edit, and keeps no + // colors, so only its points and handles count. let orig = s.doc()?.doc.node(id).ok_or(EngineError::NoNode(id))?.kind.clone(); + let colored = matches!(orig, NodeKind::Mesh(_)); let mut kind = orig.clone(); let (i, same) = edit_kind(&mut kind, C, true, |m| { let before = m.clone(); let i = add(m)?; - Ok((i, *m == before)) + let shape = |g: &GradientMesh| (g.rows, g.cols, g.points.iter().map(|q| (q.p, q.handles)).collect::>()); + Ok((i, if colored { *m == before } else { shape(m) == shape(&before) })) })?; if same { return Ok(json!({ "index": i })); diff --git a/crates/engine/src/tests/envelope.rs b/crates/engine/src/tests/envelope.rs index 4a9e6395c3..9d8999e388 100644 --- a/crates/engine/src/tests/envelope.rs +++ b/crates/engine/src/tests/envelope.rs @@ -124,7 +124,8 @@ fn reset_with_warp_and_mesh_switch_the_kind_and_keep_the_content() { } /// `object.mesh.addLine` at a point a mesh envelope already has (one stored without handles) adds -/// no line and records no undo step; a point between its lines adds them as one step. +/// no line and records no undo step, with or without a `color`; a point between its lines adds +/// them as one step. #[test] fn mesh_envelope_add_line_at_a_mesh_point_records_no_undo_step() { let mut s = session(); @@ -137,6 +138,9 @@ fn mesh_envelope_add_line_at_a_mesh_point_records_no_undo_step() { s.execute("object.mesh.addLine", &json!({"id": e.0, "x": x, "y": y})).unwrap(); assert_eq!((kind(&s, e), undo_len(&s)), (before.clone(), n), "({x}, {y}): nothing changed"); } + // An envelope's points carry no color, so a color there changes nothing either. + s.execute("object.mesh.addLine", &json!({"id": e.0, "x": 200, "y": 150, "color": "#00ff00"})).unwrap(); + assert_eq!((kind(&s, e), undo_len(&s)), (before.clone(), n), "a color changes nothing"); s.execute("object.mesh.addLine", &json!({"id": e.0, "x": 150, "y": 125})).unwrap(); assert!(matches!(kind(&s, e), EnvelopeKind::Mesh { rows: 3, cols: 3, .. }), "{:?}", kind(&s, e)); assert_eq!(undo_len(&s), n + 1); diff --git a/docs/mcp.md b/docs/mcp.md index a75646d618..54d1b3f99b 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -9628,7 +9628,8 @@ right/left/down/up; none stored means the smooth mesh through the points, as old shows its mesh; the Mesh tool and Direct Selection drag its points and the clicked point's handles, and the Mesh tool adds a row and a column where it clicks inside. `object.mesh.addLine`, and `object.mesh.create` with `at`, take a point exactly on the mesh's outline (within a millionth of a point) on any side without `tolerance`; a point outside needs `tolerance`. `object.mesh.addLine` at a point that is already a mesh -point adds no line and records no undo step unless its `color` changes the point. `press_key` Escape during a drag of a point or handle of a mesh +point adds no line and records no undo step unless its `color` changes a gradient mesh's point (a mesh envelope's points +have no color). `press_key` Escape during a drag of a point or handle of a mesh envelope or a gradient mesh cancels it: the point goes back and no undo step is added. The Control bar shows Edit Envelope / Edit Contents, the warp's style, orientation, bend and distortions (or the mesh's rows and columns), Reset (an unbent warp, a flat mesh) and Envelope Options.