Outline Stroke: a Transform effect that scales strokes or moves patterns outlines as drawn

main's reshape takes the document as its first argument, and since #885
a path whose Transform effect has Scale Strokes & Effects or Transform
Patterns on is drawn through its art (has_transform_art). Outline Stroke,
Expand's Stroke option and Flatten Transparency's Convert All Strokes to
Outlines now take that art for such a path too, so a 4 pt stroke that
the effect scales to 200% outlines at the 8 pt the canvas draws.
This commit is contained in:
dotCooCoo
2026-10-23 22:35:20 -07:00
parent 8b12bf71b3
commit 23619155c8
2 changed files with 23 additions and 5 deletions
+6 -5
View File
@@ -720,17 +720,18 @@ pub(crate) fn outlined_stroke(
/// stroke's own geometry effects then shape the path that stroke outlines; [`outlined_stroke`]
/// gets the stroke without them. A fill with a random Transform effect paints the outline its
/// geometry effects make of the path, on a copy of its own. A path whose Transform effect copies
/// it with a gradient becomes the art the canvas draws for it, each copy with its own gradient and
/// its strokes outlined before the art gets new ids. Random in a Transform effect takes its seed
/// it with a gradient, or has Scale Strokes & Effects or Transform Patterns on, becomes the art the
/// canvas draws for it, each copy with its own gradient, stroke width and pattern, and its strokes
/// outlined before the art gets new ids. Random in a Transform effect takes its seed
/// from the object's id, so these effects give the outline the values the canvas drew.
/// `None` when `l` has no stroke to outline.
pub(crate) fn outline_strokes(d: &mut Document, brushes: &[vectorcraft_brush::Brush], l: &Node) -> Option<Node> {
use vectorcraft_render::effects::{GeomContext, apply_geometry_with, has_geometry, has_gradient_copies, is_geometry, reshape};
use vectorcraft_render::effects::{GeomContext, apply_geometry_with, has_geometry, has_transform_art, is_geometry, reshape};
if !l.appearance.items.iter().any(|i| matches!(i, AppearanceItem::Stroke(st) if outlinable(st))) {
return None;
}
if has_gradient_copies(l) {
let mut art = reshape(l, None)?;
if has_transform_art(l) {
let mut art = reshape(Some(&*d), l, None)?;
outline_member_strokes(d, brushes, &mut art);
return Some(d.reid(&art));
}
+17
View File
@@ -303,6 +303,23 @@ fn each_transform_copy_keeps_its_own_gradient() {
assert_eq!(differ, none_differ);
}
/// With Scale Strokes & Effects on, the canvas draws the stroke of a solid square that its
/// Transform effect scales to 200% at 8 pt; the outlined result draws the same.
#[test]
fn a_transform_that_scales_strokes_outlines_the_scaled_stroke() {
let scale = json!({"scaleH": 200, "scaleV": 200, "strokes": true});
let mut differ = vec![];
for (command, p) in outlining() {
let mut s = session();
square_with(&mut s, json!({"color": "#33aa55"}), Value::Null, &[("distort.transform", scale.clone())]);
let before = render(&s);
outlined_by(&mut s, command, &p);
differ.push((command, differing(&before, &render(&s), 64)));
}
let none_differ: Vec<_> = outlining().map(|(command, _)| (command, 0)).into_iter().collect();
assert_eq!(differ, none_differ);
}
/// Random in a Transform effect takes its seed from the object's id. The outline draws as the
/// canvas drew the object with Random in a Transform effect that copies a gradient square, in a
/// Transform effect on the stroke of such a square, and in a Transform effect on the fill of a