Render: a moved path reuses the slices of a placed gradient stroke

A move also moves where a stroke's gradient is placed, but the slices of a
gradient along or across the stroke take only its stops and kind. The
moved stroke is now compared with the stroke the slices were made for
with the gradient's placement left out, so a placed gradient no longer
makes its slices again after every move.
This commit is contained in:
dotCooCoo
2026-11-05 13:58:16 +07:00
committed by bflatastic
parent d88f5eeed9
commit 63c48588b5
2 changed files with 29 additions and 7 deletions
+17 -6
View File
@@ -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) {
+12 -1
View File
@@ -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
}