mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-07 20:23:35 +00:00
Effects: a fill's or stroke's own Transform copies leave room in the effect budget for the pieces painted after them
transform_item_leaf now holds back the leaf's geometry for each plain or transformed piece that follows a transformed fill or stroke, so the copies of an earlier item no longer use up the budget that a later fill or stroke still needs. The nested pass over a transformed piece keeps to the same reduced limit.
This commit is contained in:
@@ -642,7 +642,11 @@ fn transform_item_leaf(leaf: &mut Node, max: Budget, used: &mut Budget) {
|
||||
*used = used.minus(cost);
|
||||
let mut pieces: Vec<Arc<Node>> = vec![];
|
||||
let mut plain: Vec<AppearanceItem> = vec![];
|
||||
for item in &original.appearance.items {
|
||||
// Which items become a transformed piece of their own; every other run of items is one plain piece.
|
||||
let transformed: Vec<bool> = original.appearance.items.iter().map(|i| crate::paints(i) && i.effects().iter().any(reaches_appearance)).collect();
|
||||
// The number of pieces after item `i` in paint order: each costs at least the leaf's geometry.
|
||||
let later = |i: usize| (i + 1..transformed.len()).filter(|&j| transformed[j] || j == i + 1 || transformed[j - 1]).count();
|
||||
for (i, item) in original.appearance.items.iter().enumerate() {
|
||||
let fx = item.effects();
|
||||
let Some(at) = fx.iter().position(reaches_appearance).filter(|_| crate::paints(item)) else {
|
||||
plain.push(item.clone());
|
||||
@@ -653,9 +657,11 @@ fn transform_item_leaf(leaf: &mut Node, max: Budget, used: &mut Budget) {
|
||||
*used = used.plus(cost);
|
||||
}
|
||||
let before = &fx[..at];
|
||||
// What the art's other paths leave of the budget: the geometry before the Transform effect
|
||||
// and its copies keep to it.
|
||||
let room = max.minus(*used);
|
||||
// What the art's other paths leave of the budget, less the leaf's geometry for each piece
|
||||
// still to come: the geometry before the Transform effect and its copies keep to it.
|
||||
let reserved = (0..later(i)).fold(Budget { anchors: 0, lines: 0 }, |r, _| r.plus(cost));
|
||||
let limit = max.minus(reserved);
|
||||
let room = limit.minus(*used);
|
||||
let g = if crate::has_geometry(before) && !path.is_empty() {
|
||||
crate::geometry::apply_geometry_within(before, &path, bounds, &ctx.item(item), room)
|
||||
} else {
|
||||
@@ -673,7 +679,7 @@ fn transform_item_leaf(leaf: &mut Node, max: Budget, used: &mut Budget) {
|
||||
set_geometry(&mut p, g.clone(), rule);
|
||||
let made = transform_leaf(&mut p, &g, map, copies, options, room);
|
||||
*used = used.plus(made);
|
||||
for_each_leaf(&mut p, &mut |l| transform_item_leaf(l, max, used));
|
||||
for_each_leaf(&mut p, &mut |l| transform_item_leaf(l, limit, used));
|
||||
pieces.push(Arc::new(p));
|
||||
}
|
||||
if !plain.is_empty() {
|
||||
|
||||
@@ -336,3 +336,26 @@ fn a_fill_below_a_strokes_own_transform_counts_toward_the_effect_budget() {
|
||||
assert_eq!(stroked_leaves(&art).len(), 2, "{cost:?}");
|
||||
assert!(Budget::EFFECTS.fits(cost), "{cost:?}");
|
||||
}
|
||||
|
||||
/// A path of 600,000 anchors with a stroke whose own Transform effect makes two copies, below a
|
||||
/// fill: the stroke's copies leave room for the fill painted after them.
|
||||
#[test]
|
||||
fn a_strokes_own_transform_leaves_room_for_a_fill_above_it() {
|
||||
use crate::geometry::Budget;
|
||||
use vectorcraft_geom::{PathData, Point, SubPath};
|
||||
let points: Vec<Point> = (0..600_000).map(|i| Point::new(f64::from(i) * 1.0e-4, f64::from(i % 2))).collect();
|
||||
let gray = Paint::solid(Color::rgb(0.5, 0.5, 0.5));
|
||||
let mut n = Node::path(NodeId(13), PathData::single(SubPath::polyline(&points, false)), Appearance::basic(gray, Paint::solid(Color::BLACK), 1.0));
|
||||
if let Some(s) = n.appearance.stroke_mut() {
|
||||
s.effects.push(transform(json!({"moveH": 100, "copies": 2, "strokes": true})));
|
||||
}
|
||||
n.appearance.items.reverse();
|
||||
assert!(matches!(n.appearance.items.first(), Some(AppearanceItem::Stroke(_))));
|
||||
let art = reshape(None, &n, None).unwrap();
|
||||
let cost = leaves(&art)
|
||||
.iter()
|
||||
.filter_map(|p| crate::reshape::leaf_geometry(p))
|
||||
.fold(Budget { anchors: 0, lines: 0 }, |c, (g, _)| c.plus(Budget::of(&g)));
|
||||
assert_eq!(stroked_leaves(&art).len(), 2, "{cost:?}");
|
||||
assert!(Budget::EFFECTS.fits(cost), "{cost:?}");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user