mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-13 10:45:45 +00:00
Stroke: a group used as a clipping path adds nothing to a custom arrowhead
A custom arrowhead takes a clipping path's own fills and strokes only where the clipping path paints them on the canvas. A group used as the clipping path paints nothing, so its members' fills no longer widen the head past the clipped art.
This commit is contained in:
@@ -222,7 +222,8 @@ impl Shapes {
|
||||
/// their strokes' outlines, its groups' members included (baking has already made a group's own
|
||||
/// fills and strokes art among its members), and the areas its gradient meshes cover. A clip
|
||||
/// group's clipped art is clipped to
|
||||
/// its clipping path, whose own fill and stroke paint unclipped, as on the canvas. Hidden or fully
|
||||
/// its clipping path, whose own fill and stroke paint unclipped, as on the canvas; a group used as
|
||||
/// the clipping path paints nothing ([`clip_paint`]). Hidden or fully
|
||||
/// transparent art is left out.
|
||||
fn painted(d: &mut Document, brushes: &[vectorcraft_brush::Brush], n: &Node, out: &mut Shapes) {
|
||||
if !n.visible || n.opacity <= 0.0 || out.too_detailed {
|
||||
@@ -239,7 +240,7 @@ fn painted(d: &mut Document, brushes: &[vectorcraft_brush::Brush], n: &Node, out
|
||||
}
|
||||
NodeKind::Group { children, clip: true } => {
|
||||
let Some((clip, rest)) = children.split_first() else { return };
|
||||
painted(d, brushes, clip, out);
|
||||
clip_paint(clip).iter().for_each(|part| painted(d, brushes, part, out));
|
||||
// Nothing to clip by hides the clipped art, as on the canvas.
|
||||
let Some((outline, rule)) = vectorcraft_render::effects::clip_outline(clip) else { return };
|
||||
let mut inside = Shapes { anchors: out.anchors, ..Default::default() };
|
||||
@@ -253,6 +254,14 @@ fn painted(d: &mut Document, brushes: &[vectorcraft_brush::Brush], n: &Node, out
|
||||
}
|
||||
}
|
||||
|
||||
/// What clipping path `clip` paints ([`Node::clip_paint`]): its fills and its strokes, each as a
|
||||
/// copy of it keeping only those. A group or another object that does not paint as a clipping
|
||||
/// path gives neither.
|
||||
fn clip_paint(clip: &Node) -> Vec<Node> {
|
||||
let paint = clip.clip_paint();
|
||||
[paint.fill, paint.stroke].into_iter().flatten().collect()
|
||||
}
|
||||
|
||||
/// The areas the shown paths and gradient meshes in `n` (an outlined stroke) fill, clipped by its
|
||||
/// clip groups, whose clipping paths' own fills paint unclipped. A stroke with a gradient along or
|
||||
/// across it is outlined as gradient meshes clipped to the stroke's outline.
|
||||
@@ -264,7 +273,7 @@ fn fills_in(n: &Node, out: &mut Shapes) {
|
||||
NodeKind::Mesh(_) => mesh_area(n, out),
|
||||
NodeKind::Group { children, clip: true } => {
|
||||
let Some((clip, rest)) = children.split_first() else { return };
|
||||
fills_in(clip, out);
|
||||
clip_paint(clip).iter().for_each(|part| fills_in(part, out));
|
||||
let Some((outline, rule)) = vectorcraft_render::effects::clip_outline(clip) else { return };
|
||||
let mut inside = Shapes { anchors: out.anchors, ..Default::default() };
|
||||
rest.iter().for_each(|c| fills_in(c, &mut inside));
|
||||
|
||||
@@ -348,3 +348,18 @@ fn a_symbol_whose_shapes_cannot_be_combined_is_skipped() {
|
||||
let skipped: Vec<&str> = read.skipped.iter().map(|(n, _)| n.as_str()).collect();
|
||||
assert_eq!(skipped, ["Broken", "Broken Clip"], "{:?}", read.skipped);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_group_used_as_the_clipping_path_adds_none_of_its_own_fills() {
|
||||
// A group holding a filled 20 × 4 bar clips a filled 4 × 4 square: the group paints nothing as
|
||||
// the clipping path, so the head is the square alone.
|
||||
let bar = polygon(&[(0.0, 0.0), (20.0, 0.0), (20.0, 4.0), (0.0, 4.0)], filled());
|
||||
let mask = Node::group(NodeId(0), vec![Arc::new(bar)]);
|
||||
let square = polygon(&[(0.0, 0.0), (4.0, 0.0), (4.0, 4.0), (0.0, 4.0)], filled());
|
||||
let mut d = Document::new(100.0, 100.0);
|
||||
add_symbol(&mut d, "Group Clip", Node::new(NodeId(0), NodeKind::Group { children: vec![Arc::new(mask), Arc::new(square)], clip: true }));
|
||||
let read = heads_of(&d);
|
||||
assert_eq!(read.heads.len(), 1, "{:?}", read.skipped);
|
||||
let b = read.heads[0].path.bounds().unwrap();
|
||||
assert!((b.width() - 4.0).abs() < 1e-3 && (b.height() - 4.0).abs() < 1e-3, "the square alone: {b:?}");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user