diff --git a/crates/render/src/renderer/paint.rs b/crates/render/src/renderer/paint.rs index 6d9f0ea3e..9e6080428 100644 --- a/crates/render/src/renderer/paint.rs +++ b/crates/render/src/renderer/paint.rs @@ -395,11 +395,12 @@ impl Renderer { /// The slices of stroke `st` of `node` along `bp` (filled with `rule`), made from the latest /// slices kept under `mk` ([`MovedSliceKey`]) when those were made along their object's own - /// path, their object is `node` changed, its stroke there is `st` and its fill rule `rule`, and - /// `bp` is its path moved (to within rounding): the node they were made from, those slices, - /// and the offset from that node to `bp`, which the slices are drawn moved by. The anchors of - /// the two objects are compared first ([`anchors_moved`]), and the earlier path is made again - /// from its object only when they match. `None` when there are no such slices. + /// path, their object is `node` changed, its stroke there differs from `st` at most in where + /// its gradient is placed, its fill rule is `rule`, and `bp` is its path moved (to within + /// rounding): the node they were made from, those slices, and the offset from that node to + /// `bp`, which the slices are drawn moved by. The anchors of the two objects are compared first + /// ([`anchors_moved`]), and the earlier path is made again from its object only when they + /// match. `None` when there are no such slices. fn moved_slices( &self, mk: MovedSliceKey, @@ -415,7 +416,17 @@ impl Renderer { let Some(AppearanceItem::Stroke(old)) = e.made.appearance.items.get(mk.1) else { return None; }; - if !std::ptr::eq(old, st) && old != st { + // The slices take only the gradient's stops and kind, not where it is placed, which a move + // moves too. + let unplaced = |s: &StrokeLayer| { + let mut s = s.clone(); + if let vectorcraft_color::Paint::Gradient(g) = &mut s.paint { + g.geom = None; + g.angle = 0.0; + } + s + }; + if !std::ptr::eq(old, st) && unplaced(old) != unplaced(st) { return None; } if !anchors_moved(&e.made, node) { diff --git a/crates/render/src/tests/movedstroke.rs b/crates/render/src/tests/movedstroke.rs index 81fa40da8..f08e341c2 100644 --- a/crates/render/src/tests/movedstroke.rs +++ b/crates/render/src/tests/movedstroke.rs @@ -3,7 +3,7 @@ //! offset, with the pixels a new renderer draws. A change other than a move outlines the stroke //! and makes the slices again. -use vectorcraft_color::{Color, Gradient, GradientKind, GradientPaint, GradientStop, Paint}; +use vectorcraft_color::{Color, Gradient, GradientGeom, GradientKind, GradientPaint, GradientStop, Paint}; use vectorcraft_doc::{ Appearance, AppearanceItem, ArrowAlign, Arrowhead, Dash, FillLayer, LineCap, LineJoin, Node, NodeId, NodeKind, StrokeGradientMode, StrokeLayer, WidthProfile, @@ -353,6 +353,17 @@ fn gradient_cases() -> Vec<(String, (Document, NodeId), Point)> { out.push((format!("a short closed path, {mode:?}"), doc(&ring(40, 80.0), only()), Point::new(80.0, 0.0))); out.push((format!("a long open path, {mode:?}"), doc(&zigzag(3000), only()), Point::new(12_000.0, 15.0))); out.push((format!("a long closed path, {mode:?}"), doc(&ring(4000, 5000.0), only()), Point::new(0.0, 5000.0))); + // A placed gradient, whose start and end a move moves too. + let mut st = gradient_stroke(12.0, mode); + if let Paint::Gradient(g) = &mut st.paint { + g.geom = Some(GradientGeom { start: Point::new(0.0, 0.0), end: Point::new(140.0, 30.0), aspect: 1.0, focal: None }); + g.angle = 12.0; + } + out.push(( + format!("a short open path with a placed gradient, {mode:?}"), + doc(&zigzag(20), vec![AppearanceItem::Stroke(st)]), + Point::new(70.0, 15.0), + )); } out }