Fix #4938: Dynamic symbols keep per-instance appearance linked to the symbol

This commit is contained in:
bflatastic
2026-11-01 04:53:38 +07:00
parent 3f1928efc8
commit f22caa6108
105 changed files with 1218 additions and 212 deletions
+3 -3
View File
@@ -370,7 +370,7 @@ mod tests {
shapes::rectangle(Rect::new(-10.0, -10.0, 10.0, 10.0)),
Appearance::basic(Paint::solid(Color::BLACK), Paint::None, 0.0),
);
d.symbols.push(vectorcraft_doc::Symbol { name: "S".into(), art: std::sync::Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "S".into(), art: std::sync::Arc::new(art) });
let id = d.alloc_id();
let mut inst = Node::new(id, vectorcraft_doc::NodeKind::SymbolInstance { symbol: "S".into(), xf: Affine::translate((50.0, 50.0)) });
inst.appearance.set_fill(Paint::solid(Color::rgb(1.0, 0.0, 0.0)));
@@ -394,7 +394,7 @@ mod tests {
let mut ap = Appearance::basic(Paint::None, Paint::solid(Color::BLACK), 1.0);
ap.stroke_mut().unwrap().brush = Some("3 pt. Round".into());
let art = Node::path(vectorcraft_doc::NodeId(0), shapes::line((0.0, 40.0).into(), (0.0, 60.0).into()), ap);
d.symbols.push(vectorcraft_doc::Symbol { name: "Nib".into(), art: std::sync::Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Nib".into(), art: std::sync::Arc::new(art) });
let id = d.alloc_id();
let inst = Node::new(id, vectorcraft_doc::NodeKind::SymbolInstance { symbol: "Nib".into(), xf: Affine::translate((-6.0, 0.0)) });
let l = d.layers[0].id;
@@ -424,7 +424,7 @@ mod tests {
let moved = Affine::translate((-8.0, 0.0));
let source = if instance {
let art = line(&mut d);
d.symbols.push(vectorcraft_doc::Symbol { name: "Nib".into(), art: std::sync::Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Nib".into(), art: std::sync::Arc::new(art) });
Node::new(d.alloc_id(), vectorcraft_doc::NodeKind::SymbolInstance { symbol: "Nib".into(), xf: moved })
} else {
let mut n = line(&mut d);
+1 -1
View File
@@ -236,7 +236,7 @@ fn art_bounds_take_in_the_effects_in_a_symbols_art() {
let mut art = Node::path(d.alloc_id(), shapes::rectangle(Rect::new(0.0, 0.0, 10.0, 10.0)), black);
let shadow = vectorcraft_doc::Effect { id: "stylize.dropShadow".into(), params: serde_json::json!({"x": 8, "y": 0, "blur": 0}), visible: true };
art.appearance.effects.push(shadow);
d.symbols.push(vectorcraft_doc::Symbol { name: "Box".into(), art: std::sync::Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Box".into(), art: std::sync::Arc::new(art) });
let (id, l) = (d.alloc_id(), d.layers[0].id);
let xf = vectorcraft_geom::Affine::translate((20.0, 20.0));
d.insert(Some(l), 0, Node::new(id, NodeKind::SymbolInstance { symbol: "Box".into(), xf })).unwrap();
+2 -2
View File
@@ -479,7 +479,7 @@ fn thirty_stacked_glows_draw_quickly_cached_or_not() {
shapes::rectangle(Rect::new(0.0, 0.0, 40.0, 40.0)),
Appearance::basic(Paint::solid(Color::BLACK), Paint::None, 0.0),
);
instanced.symbols.push(vectorcraft_doc::Symbol { name: "Square".into(), art: std::sync::Arc::new(art) });
instanced.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Square".into(), art: std::sync::Arc::new(art) });
let mut instance =
Node::new(instanced.alloc_id(), vectorcraft_doc::NodeKind::SymbolInstance { symbol: "Square".into(), xf: Affine::translate((30.0, 30.0)) });
instance.appearance.effects = glows.into_iter().map(|(id, p)| Effect { id: id.into(), params: p, visible: true }).collect();
@@ -754,7 +754,7 @@ fn a_moved_group_draws_the_shadow_of_the_instance_art_in_it_while_interacting()
let mut base = Document::new(100.0, 100.0);
let orange = Appearance::basic(Paint::solid(Color::rgb(0.9, 0.4, 0.1)), Paint::None, 0.0);
let art = Node::path(base.alloc_id(), shapes::rectangle(Rect::new(100.0, 100.0, 130.0, 126.0)), orange);
base.symbols.push(vectorcraft_doc::Symbol { name: "Far".into(), art: std::sync::Arc::new(art) });
base.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Far".into(), art: std::sync::Arc::new(art) });
// The instance draws the art from (20, 20) to (50, 46), and the 20 × 20 square around the
// origin from (-90, -90) to (-70, -70).
let xf = Affine::translate((-80.0, -80.0));
+2 -2
View File
@@ -771,7 +771,7 @@ mod tests {
true => Node::new(d.alloc_id(), NodeKind::SymbolInstance { symbol: format!("S{}", k + 1), xf: Affine::IDENTITY }),
false => square(&mut d),
};
d.symbols.push(Symbol { name: format!("S{k}"), art: Arc::new(art) });
d.symbols.push(Symbol { dynamic: false, name: format!("S{k}"), art: Arc::new(art) });
}
let first = Node::new(d.alloc_id(), NodeKind::SymbolInstance { symbol: "S0".into(), xf: Affine::IDENTITY });
let kind = EnvelopeKind::Warp { style: "arc".into(), bend: 30.0, h: 0.0, v: 0.0, horizontal: true };
@@ -805,7 +805,7 @@ mod tests {
};
let (x, y) = (square(&mut d, 0.0), square(&mut d, 20.0));
for (name, art) in [("A", &x), ("B", &y), ("C", &x)] {
d.symbols.push(Symbol { name: name.into(), art: art.clone() });
d.symbols.push(Symbol { dynamic: false, name: name.into(), art: art.clone() });
}
let content = ["A", "B", "C"].map(|s| Arc::new(Node::new(d.alloc_id(), NodeKind::SymbolInstance { symbol: s.into(), xf: Affine::IDENTITY })));
let kind = EnvelopeKind::Warp { style: "arc".into(), bend: 30.0, h: 0.0, v: 0.0, horizontal: true };
+3 -1
View File
@@ -56,7 +56,9 @@ impl Renderer {
/// placed again when the fonts change.
pub(super) fn placed_art(&mut self, art: &Arc<Node>, xf: Affine, scale_strokes: bool, inst: Option<&Node>) -> (Arc<Node>, bool) {
let kept_kind = matches!(art.kind, NodeKind::Group { .. } | NodeKind::Layer { .. }) || vectorcraft_doc::live::is_live(art);
if !kept_kind || fx::has_object_fx(art) {
// An instance with appearance changes of its own isn't kept: the key doesn't tell them apart.
let overridden = inst.is_some_and(|i| !i.extras().symbol_overrides.is_empty());
if !kept_kind || overridden || fx::has_object_fx(art) {
return (place(art, xf, scale_strokes, inst), false);
}
let stain = stain_bits(inst.and_then(vectorcraft_brush::stain));
+10 -10
View File
@@ -220,7 +220,7 @@ fn hidden_and_culled() {
fn far_instance(art: Rect) -> Document {
let mut d = Document::new(100.0, 100.0);
let art = rect_node(&mut d, art, Paint::solid(Color::rgb(1.0, 0.0, 0.0)), Paint::None, 0.0);
d.symbols.push(vectorcraft_doc::Symbol { name: "Far".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Far".into(), art: Arc::new(art) });
let (id, l) = (d.alloc_id(), d.layers[0].id);
d.insert(Some(l), 0, Node::new(id, NodeKind::SymbolInstance { symbol: "Far".into(), xf: Affine::translate((-50.0, -50.0)) })).unwrap();
d
@@ -250,7 +250,7 @@ fn an_instance_is_culled_by_the_effects_in_its_art() {
let mut art = rect_node(&mut d, Rect::new(-1.0, -1.0, 1.0, 1.0), Paint::solid(Color::BLACK), Paint::None, 0.0);
let shadow = vectorcraft_doc::Effect { id: "stylize.dropShadow".into(), params: serde_json::json!({"x": 8, "y": 0, "blur": 0}), visible: true };
art.appearance.effects.push(shadow);
d.symbols.push(vectorcraft_doc::Symbol { name: "Dot".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Dot".into(), art: Arc::new(art) });
let (id, l) = (d.alloc_id(), d.layers[0].id);
d.insert(Some(l), 0, Node::new(id, NodeKind::SymbolInstance { symbol: "Dot".into(), xf: Affine::translate((-5.0, 20.0)) })).unwrap();
let img = Renderer::new().render(&d, 100, 100, Affine::IDENTITY, &RenderOptions::default());
@@ -264,7 +264,7 @@ fn a_repeat_of_an_instance_is_culled_by_the_effects_in_its_art() {
let mut art = rect_node(&mut d, Rect::new(-1.0, -1.0, 1.0, 1.0), Paint::solid(Color::BLACK), Paint::None, 0.0);
let shadow = vectorcraft_doc::Effect { id: "stylize.dropShadow".into(), params: serde_json::json!({"x": 8, "y": 0, "blur": 0}), visible: true };
art.appearance.effects.push(shadow);
d.symbols.push(vectorcraft_doc::Symbol { name: "Dot".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Dot".into(), art: Arc::new(art) });
let instance = Node::new(d.alloc_id(), NodeKind::SymbolInstance { symbol: "Dot".into(), xf: Affine::translate((-5.0, 20.0)) });
let grid = vectorcraft_doc::TileType::Grid;
let kind =
@@ -287,11 +287,11 @@ fn past_the_measured_placements_an_instance_is_culled_by_an_estimate_of_its_art_
let mut art = rect_node(&mut d, Rect::new(-1.0, -1.0, 1.0, 1.0), Paint::solid(Color::BLACK), Paint::None, 0.0);
let shadow = vectorcraft_doc::Effect { id: "stylize.dropShadow".into(), params: serde_json::json!({"x": 8, "y": 0, "blur": 0}), visible: true };
art.appearance.effects.push(shadow);
d.symbols.push(vectorcraft_doc::Symbol { name: "Spot".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Spot".into(), art: Arc::new(art) });
for name in ["Dot", "Late"] {
let spot = Node::new(d.alloc_id(), NodeKind::SymbolInstance { symbol: "Spot".into(), xf: Affine::IDENTITY });
let group = Node::group(d.alloc_id(), vec![Arc::new(spot)]);
d.symbols.push(vectorcraft_doc::Symbol { name: name.into(), art: Arc::new(group) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: name.into(), art: Arc::new(group) });
}
let l = d.layers[0].id;
for i in 0..4100 {
@@ -317,7 +317,7 @@ fn nested_instances_are_culled_in_bounded_time(effect: Option<vectorcraft_doc::E
let mut art = rect_node(&mut d, Rect::new(0.0, 0.0, 1.0, 1.0), Paint::solid(Color::BLACK), Paint::None, 0.0);
let shadow = vectorcraft_doc::Effect { id: "stylize.dropShadow".into(), params: serde_json::json!({"x": 2, "y": 0, "blur": 0}), visible: true };
art.appearance.effects.push(shadow);
d.symbols.push(vectorcraft_doc::Symbol { name: "S30".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "S30".into(), art: Arc::new(art) });
let instance = |d: &mut Document, symbol: String, xf: Affine| {
let mut n = Node::new(d.alloc_id(), NodeKind::SymbolInstance { symbol, xf });
n.appearance.effects.extend(effect.clone());
@@ -330,7 +330,7 @@ fn nested_instances_are_culled_in_bounded_time(effect: Option<vectorcraft_doc::E
pair.push(Arc::new(instance(&mut d, next.clone(), xf)));
}
let group = Node::group(d.alloc_id(), pair);
d.symbols.push(vectorcraft_doc::Symbol { name: format!("S{i}"), art: Arc::new(group) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: format!("S{i}"), art: Arc::new(group) });
}
let first = instance(&mut d, "S0".into(), Affine::translate((-100_000.0, 0.0)));
let l = d.layers[0].id;
@@ -369,7 +369,7 @@ fn a_scaled_instance_is_culled_by_the_effects_in_its_art_as_placed() {
let mut art = rect_node(&mut d, Rect::new(-1.0, -1.0, 1.0, 1.0), Paint::solid(Color::rgb(1.0, 0.0, 0.0)), Paint::None, 0.0);
let double = vectorcraft_doc::Effect { id: "distort.transform".into(), params: serde_json::json!({"scaleH": 200, "scaleV": 200}), visible: true };
art.appearance.effects.push(double);
d.symbols.push(vectorcraft_doc::Symbol { name: "Dot".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Dot".into(), art: Arc::new(art) });
let (id, l) = (d.alloc_id(), d.layers[0].id);
let xf = Affine::translate((-50.0, 50.0)) * Affine::scale(30.0);
d.insert(Some(l), 0, Node::new(id, NodeKind::SymbolInstance { symbol: "Dot".into(), xf })).unwrap();
@@ -401,7 +401,7 @@ fn an_instance_with_a_geometry_effect_is_culled_by_the_effects_in_its_reshaped_a
let effect = || vectorcraft_doc::Effect { id: "distort.transform".into(), params: scale.clone(), visible: true };
let mut art = rect_node(&mut d, Rect::new(-1.0, -1.0, 1.0, 1.0), Paint::solid(Color::rgb(1.0, 0.0, 0.0)), Paint::None, 0.0);
art.appearance.effects.push(effect());
d.symbols.push(vectorcraft_doc::Symbol { name: "Dot".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Dot".into(), art: Arc::new(art) });
let (id, l) = (d.alloc_id(), d.layers[0].id);
let mut instance = Node::new(id, NodeKind::SymbolInstance { symbol: "Dot".into(), xf: Affine::translate((-11.0, 20.0)) });
instance.appearance.effects.push(effect());
@@ -845,7 +845,7 @@ fn placed_symbol_art_root_applies_its_own_mask() {
let mut art = if grouped { Node::group(d.alloc_id(), vec![Arc::new(red)]) } else { red };
let inner = rect_node(&mut d, Rect::new(10.0, 10.0, 50.0, 90.0), Paint::solid(Color::WHITE), Paint::None, 0.0);
art.extras_mut().mask = Some(Box::new(OpacityMask::new(inner, true)));
d.symbols.push(vectorcraft_doc::Symbol { name: "Masked".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Masked".into(), art: Arc::new(art) });
let instance = Node::new(d.alloc_id(), NodeKind::SymbolInstance { symbol: "Masked".into(), xf: Affine::IDENTITY });
d.insert(Some(d.layers[0].id), 0, instance).unwrap();
+1 -1
View File
@@ -112,7 +112,7 @@ fn an_embedded_image_is_recoloured_and_the_original_pixels_stay() {
fn a_symbol_instance_recolours_its_art() {
let mut d = Document::new(100.0, 100.0);
let art = red_square(-15.0, -15.0);
d.symbols.push(Symbol { name: "Red".into(), art: Arc::new(art) });
d.symbols.push(Symbol { dynamic: false, name: "Red".into(), art: Arc::new(art) });
let mut n = Node::new(NodeId(0), NodeKind::SymbolInstance { symbol: "Red".into(), xf: Affine::translate((50.0, 50.0)) });
n.appearance.effects.push(hue());
let l = d.layers[0].id;
+1 -1
View File
@@ -220,7 +220,7 @@ fn an_unchanged_document_changes_nothing_and_a_new_symbol_everything() {
assert_eq!(redraw(&old, &old.clone()), Changed::Nothing);
let mut new = old.clone();
let art = Arc::new(rect(&mut new, Rect::new(0.0, 0.0, 5.0, 5.0), Color::BLACK));
new.symbols.push(vectorcraft_doc::Symbol { name: "Dot".into(), art });
new.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Dot".into(), art });
assert_eq!(redraw(&old, &new), Changed::Everything);
}
+1 -1
View File
@@ -118,7 +118,7 @@ fn the_transparency_test_is_kept_while_the_art_stays_the_same() {
// A symbol whose art shows transparency, placed nowhere, makes the page draw by ink planes again.
let mut art = rect(&mut e, Rect::new(0.0, 0.0, 5.0, 5.0), CYAN);
art.opacity = 0.5;
e.symbols.push(vectorcraft_doc::Symbol { name: "S".into(), art: Arc::new(art) });
e.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "S".into(), art: Arc::new(art) });
assert!(frame(&mut r, &e) == frame(&mut Renderer::new(), &e));
assert_eq!(kept(&r).map(|(_, shows)| shows), Some(true));
}
+3 -3
View File
@@ -162,7 +162,7 @@ fn symbol_type_redraws_when_its_font_becomes_available() {
let mut t = Node::new(d.alloc_id(), NodeKind::Text(Box::new(TextObject::point(Point::new(-140.0, 25.0), "Hamburg", style))));
t.appearance.effects.push(vectorcraft_doc::Effect { id: "distort.transform".into(), params: serde_json::json!({"moveH": 0.0}), visible: true });
let art = Node::group(d.alloc_id(), vec![Arc::new(t)]);
d.symbols.push(vectorcraft_doc::Symbol { name: "Word".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Word".into(), art: Arc::new(art) });
let (l, id) = (d.layers[0].id, d.alloc_id());
d.insert(Some(l), 0, Node::new(id, NodeKind::SymbolInstance { symbol: "Word".into(), xf: Affine::translate((150.0, 75.0)) })).unwrap();
let opts = RenderOptions { background: Some([255, 255, 255, 255]), ..Default::default() };
@@ -207,7 +207,7 @@ fn type_with_geometry_effects_redraws_when_its_font_becomes_available() {
}
3 => {
let art = Node::group(d.alloc_id(), vec![Arc::new(object)]);
d.symbols.push(vectorcraft_doc::Symbol { name: "Word".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Word".into(), art: Arc::new(art) });
object = Node::new(d.alloc_id(), NodeKind::SymbolInstance { symbol: "Word".into(), xf: Affine::translate((150.0, 75.0)) });
object.appearance.effects.push(moved());
}
@@ -243,7 +243,7 @@ fn enveloped_type_redraws_when_its_font_becomes_available() {
let typed = word(&mut d, Point::new(10.0, 60.0));
let art = word(&mut d, Point::new(-140.0, 20.0));
let art = Node::group(d.alloc_id(), vec![Arc::new(art)]);
d.symbols.push(vectorcraft_doc::Symbol { name: "Word".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Word".into(), art: Arc::new(art) });
let shown = Node::new(d.alloc_id(), NodeKind::SymbolInstance { symbol: "Word".into(), xf: Affine::translate((150.0, 120.0)) });
let l = d.layers[0].id;
for (i, content) in [typed, shown].into_iter().enumerate() {
+33 -33
View File
@@ -389,7 +389,7 @@ fn type_with_an_inline_graphic_draws_its_symbols_new_size() {
for i in 0..4 {
let mut d = Document::new(200.0, 100.0);
let art = symbol_art(&mut d);
d.symbols.push(vectorcraft_doc::Symbol { name: "S".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "S".into(), art: Arc::new(art) });
let style = CharStyle { size: 24.0, ..Default::default() };
let mut t = TextObject::point(vectorcraft_geom::Point::new(10.0, 50.0), "A", style.clone());
t.runs.push(TextRun::inline(InlineArt::new("S"), style.clone()));
@@ -428,7 +428,7 @@ fn symbol_art(d: &mut Document) -> Node {
fn symbol_doc() -> Document {
let mut d = Document::new(200.0, 100.0);
let art = symbol_art(&mut d);
d.symbols.push(vectorcraft_doc::Symbol { name: "S".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "S".into(), art: Arc::new(art) });
let l = d.layers[0].id;
let xfs = [
Affine::translate((30.0, 30.0)),
@@ -579,9 +579,9 @@ fn art_placed_again_in_every_frame_keeps_one_outline_per_stroke() {
if let Some(c) = art.children_mut() {
c.push(Arc::new(image));
}
d.symbols.push(vectorcraft_doc::Symbol { name: "I".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "I".into(), art: Arc::new(art) });
let art = symbol_art(&mut d);
d.symbols.push(vectorcraft_doc::Symbol { name: "S".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "S".into(), art: Arc::new(art) });
let l = d.layers[0].id;
let instance = |d: &mut Document, symbol: &str, x: f64, y: f64| {
let id = d.alloc_id();
@@ -707,7 +707,7 @@ fn strokes_of_brush_art_draw_as_a_new_renderer_does() {
// Three instances show symbol `B`, a group of a wave. A fourth wave is a path of the document,
// and a Mirror repeat draws a fifth wave and its reflection.
let art = Node::group(d.alloc_id(), vec![Arc::new(wave(&mut d, (0.0, 0.0)))]);
d.symbols.push(vectorcraft_doc::Symbol { name: "B".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "B".into(), art: Arc::new(art) });
let l = d.layers[0].id;
for at in [(50.0, 30.0), (150.0, 30.0), (50.0, 70.0)] {
let id = d.alloc_id();
@@ -755,10 +755,10 @@ fn brushed_copies_doc() -> Document {
use vectorcraft_doc::text::{CharStyle, InlineArt, TextObject, TextRun};
let mut d = Document::new(200.0, 100.0);
let line = brushed_line(&mut d);
d.symbols.push(vectorcraft_doc::Symbol { name: "Line".into(), art: Arc::new(line) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Line".into(), art: Arc::new(line) });
let line = brushed_line(&mut d);
let held = Node::group(d.alloc_id(), vec![Arc::new(line)]);
d.symbols.push(vectorcraft_doc::Symbol { name: "Held".into(), art: Arc::new(held) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Held".into(), art: Arc::new(held) });
let l = d.layers[0].id;
for (symbol, at) in [("Line", (20.0, 15.0)), ("Line", (60.0, 15.0)), ("Line", (100.0, 15.0)), ("Held", (20.0, 45.0)), ("Held", (60.0, 45.0))] {
let id = d.alloc_id();
@@ -856,7 +856,7 @@ fn brush_art_draws_edits_of_the_symbols_and_patterns_it_shows() {
let doc = |dot_brushed: bool, fill_brushed: bool| {
let mut d = Document::new(200.0, 100.0);
let dot = art(&mut d, Paint::solid(Color::rgb(0.0, 0.0, 1.0)), dot_brushed);
d.symbols.push(vectorcraft_doc::Symbol { name: "Dot".into(), art: Arc::new(dot) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Dot".into(), art: Arc::new(dot) });
let fill = art(&mut d, Paint::solid(Color::rgb(0.0, 0.6, 0.0)), fill_brushed);
let mut def = PatternDef::new("Fill", vec![Arc::new(fill)]);
def.tile = Rect::new(-6.0, -6.0, 6.0, 6.0);
@@ -917,7 +917,7 @@ fn brush_art_draws_renamed_symbols_and_patterns_it_shows() {
[("Dot", Paint::solid(Color::rgb(0.0, 0.0, 1.0)), false), ("Other", Paint::solid(Color::rgb(1.0, 0.0, 0.0)), true)]
{
let a = art(&mut d, paint, brushed);
d.symbols.push(vectorcraft_doc::Symbol { name: name.into(), art: Arc::new(a) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: name.into(), art: Arc::new(a) });
}
for (name, paint, brushed) in
[("Fill", Paint::solid(Color::rgb(0.0, 0.6, 0.0)), false), ("Spare", Paint::solid(Color::rgb(1.0, 0.0, 0.0)), true)]
@@ -978,7 +978,7 @@ fn live_objects_in_brush_art_keep_their_evaluations_between_frames() {
};
let held = bent(&mut d, (-60.0, 0.0), (60.0, 0.0));
let held = Node::group(d.alloc_id(), vec![Arc::new(held)]);
d.symbols.push(vectorcraft_doc::Symbol { name: "Wave".into(), art: Arc::new(held) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Wave".into(), art: Arc::new(held) });
let path = bent(&mut d, (20.0, 25.0), (180.0, 25.0));
let l = d.layers[0].id;
d.insert(Some(l), usize::MAX, path).unwrap();
@@ -1006,7 +1006,7 @@ fn editing_an_instance_or_a_symbol_places_only_its_art_again() {
let green = vectorcraft_doc::Appearance::basic(Paint::solid(Color::rgb(0.0, 1.0, 0.0)), Paint::None, 0.0);
let square = Node::path(d.alloc_id(), vectorcraft_geom::shapes::rectangle(Rect::new(-5.0, -5.0, 5.0, 5.0)), green);
let t = Node::group(d.alloc_id(), vec![Arc::new(square)]);
d.symbols.push(vectorcraft_doc::Symbol { name: "T".into(), art: Arc::new(t) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "T".into(), art: Arc::new(t) });
let (l, id) = (d.layers[0].id, d.alloc_id());
d.insert(Some(l), 0, Node::new(id, NodeKind::SymbolInstance { symbol: "T".into(), xf: Affine::translate((30.0, 80.0)) })).unwrap();
let opts = RenderOptions::default();
@@ -1056,7 +1056,7 @@ fn instances_of_art_outside_the_square_follow_their_symbols_art() {
// the one from x 200 lies from (40, 40) to (60, 60), in it. The 20 × 20 square around the
// origin lies from (-170, 30) to (-150, 50), left of the view.
let (off, on) = (square_at(&mut d, 100.0), square_at(&mut d, 200.0));
d.symbols.push(vectorcraft_doc::Symbol { name: "Far".into(), art: off.clone() });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Far".into(), art: off.clone() });
let (l, id) = (d.layers[0].id, d.alloc_id());
d.insert(Some(l), 0, Node::new(id, NodeKind::SymbolInstance { symbol: "Far".into(), xf: Affine::translate((-160.0, 40.0)) })).unwrap();
let opts = RenderOptions::default();
@@ -1098,7 +1098,7 @@ fn draws_as_new(d: &mut Document, before: &RenderOptions, after: &RenderOptions,
/// `d` with symbol `Holder` (a group of `members`) shown by one instance at (100, 50).
fn holder_doc(mut d: Document, members: Vec<Node>) -> Document {
let holder = Node::group(d.alloc_id(), members.into_iter().map(Arc::new).collect());
d.symbols.push(vectorcraft_doc::Symbol { name: "Holder".into(), art: Arc::new(holder) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Holder".into(), art: Arc::new(holder) });
let (l, id) = (d.layers[0].id, d.alloc_id());
d.insert(Some(l), 0, Node::new(id, NodeKind::SymbolInstance { symbol: "Holder".into(), xf: Affine::translate((100.0, 50.0)) })).unwrap();
d
@@ -1113,7 +1113,7 @@ fn art_showing_another_symbol_draws_its_edits() {
use vectorcraft_doc::text::{CharStyle, InlineArt, TextObject, TextRun};
let mut d = Document::new(200.0, 100.0);
let art = symbol_art(&mut d);
d.symbols.push(vectorcraft_doc::Symbol { name: "S".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "S".into(), art: Arc::new(art) });
let style = CharStyle { size: 24.0, ..Default::default() };
let mut t = TextObject::point(vectorcraft_geom::Point::new(-60.0, 10.0), "A", style.clone());
t.runs.push(TextRun::inline(InlineArt::new("S"), style.clone()));
@@ -1234,7 +1234,7 @@ fn live_objects_in_symbol_art_keep_their_evaluations_between_frames() {
for (name, art, at) in
[("Bent", bent, (40.0, 25.0)), ("Envelope", envelope, (100.0, 25.0)), ("Blend", blend, (160.0, 25.0)), ("Grid", grid, (20.0, 60.0))]
{
d.symbols.push(vectorcraft_doc::Symbol { name: name.into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: name.into(), art: Arc::new(art) });
let id = d.alloc_id();
d.insert(Some(l), usize::MAX, Node::new(id, NodeKind::SymbolInstance { symbol: name.into(), xf: Affine::translate(at) })).unwrap();
}
@@ -1298,7 +1298,7 @@ fn live_objects_in_symbol_art_draw_symbol_and_pattern_edits() {
for bricks in [false, true] {
let mut d = Document::new(200.0, 100.0);
let art = symbol_art(&mut d);
d.symbols.push(vectorcraft_doc::Symbol { name: "S".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "S".into(), art: Arc::new(art) });
let xf = Affine::translate((10.0, 10.0)) * Affine::scale(0.5);
let dot = Node::new(d.alloc_id(), NodeKind::SymbolInstance { symbol: "S".into(), xf });
let mut dots = PatternDef::new("Dots", vec![Arc::new(dot)]);
@@ -1307,7 +1307,7 @@ fn live_objects_in_symbol_art_draw_symbol_and_pattern_edits() {
let band = filled(&mut d, rectangle(Rect::new(-80.0, -30.0, 80.0, 30.0)), pattern_paint("Dots"), vec![]);
let envelope = arc_envelope(&mut d, vec![band], true);
let art = if grouped { Node::group(d.alloc_id(), vec![Arc::new(envelope)]) } else { envelope };
d.symbols.push(vectorcraft_doc::Symbol { name: "Bent".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Bent".into(), art: Arc::new(art) });
let (l, id) = (d.layers[0].id, d.alloc_id());
d.insert(Some(l), 0, Node::new(id, NodeKind::SymbolInstance { symbol: "Bent".into(), xf: Affine::translate((100.0, 50.0)) })).unwrap();
let opts = RenderOptions { background: Some([255, 255, 255, 255]), ..Default::default() };
@@ -1401,7 +1401,7 @@ fn live_objects_draw_symbol_and_pattern_art_edits() {
let with_s = || {
let mut d = Document::new(200.0, 100.0);
let art = symbol_art(&mut d);
d.symbols.push(vectorcraft_doc::Symbol { name: "S".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "S".into(), art: Arc::new(art) });
d
};
let red_bar = |d: &mut Document| {
@@ -1415,7 +1415,7 @@ fn live_objects_draw_symbol_and_pattern_art_edits() {
let mut d = with_s();
let shown = instance(&mut d, "S", Affine::IDENTITY);
let a = Node::group(d.alloc_id(), vec![Arc::new(shown)]);
d.symbols.push(vectorcraft_doc::Symbol { name: "A".into(), art: Arc::new(a) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "A".into(), art: Arc::new(a) });
let a = instance(&mut d, "A", Affine::translate((100.0, 50.0)) * Affine::scale(2.0));
let envelope = arc_envelope(&mut d, vec![a], false);
edit_draws_as_new(with_art(d, vec![envelope]), red_bar);
@@ -1430,7 +1430,7 @@ fn live_objects_draw_symbol_and_pattern_art_edits() {
let mut d = Document::new(200.0, 100.0);
d.patterns.push(checks(Color::rgb(1.0, 0.0, 0.0)));
let band = filled(&mut d, rectangle(Rect::new(-80.0, -30.0, 80.0, 30.0)), pattern_paint("Checks"), vec![]);
d.symbols.push(vectorcraft_doc::Symbol { name: "Banded".into(), art: Arc::new(band) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Banded".into(), art: Arc::new(band) });
let banded = instance(&mut d, "Banded", Affine::translate((100.0, 50.0)));
let envelope = arc_envelope(&mut d, vec![banded], true);
edit_draws_as_new(with_art(d, vec![envelope]), |d| {
@@ -1457,7 +1457,7 @@ fn symbol_in_live_and_effect_art() -> Document {
use vectorcraft_geom::shapes::rectangle;
let mut d = Document::new(500.0, 100.0);
let art = symbol_art(&mut d);
d.symbols.push(vectorcraft_doc::Symbol { name: "S".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "S".into(), art: Arc::new(art) });
let bent = |d: &mut Document, x: f64| {
let xf = Affine::translate((x, 50.0)) * Affine::scale(2.0);
let s = Node::new(d.alloc_id(), NodeKind::SymbolInstance { symbol: "S".into(), xf });
@@ -1470,7 +1470,7 @@ fn symbol_in_live_and_effect_art() -> Document {
group.appearance.effects.push(drop_shadow());
let copy = symbol_art(&mut d);
let art = arc_envelope(&mut d, vec![copy], false);
d.symbols.push(vectorcraft_doc::Symbol { name: "Bent".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Bent".into(), art: Arc::new(art) });
let xf = Affine::translate((250.0, 50.0)) * Affine::scale(2.0);
let symbol = Node::new(d.alloc_id(), NodeKind::SymbolInstance { symbol: "Bent".into(), xf });
let mask = bent(&mut d, 350.0);
@@ -1603,7 +1603,7 @@ fn objects_with_plugin_effects_follow_the_plugins_installed() {
let mut d = Document::new(200.0, 100.0);
let art = square(&mut d, -20.0, -20.0, vec![]);
let art = Node::group(d.alloc_id(), vec![Arc::new(art)]);
d.symbols.push(vectorcraft_doc::Symbol { name: "Shaped".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Shaped".into(), art: Arc::new(art) });
let instance = Node::new(d.alloc_id(), NodeKind::SymbolInstance { symbol: "Shaped".into(), xf: Affine::translate((100.0, 50.0)) });
plugin_change_draws_as_new(with_art(d, vec![instance]), ID, small.clone(), big);
// Left of the view, the square is culled until the plug-in draws it in the view.
@@ -1616,7 +1616,7 @@ fn objects_with_plugin_effects_follow_the_plugins_installed() {
// effects in the art paint past the art, which the plug-in's first shape does not.
let mut d = Document::new(200.0, 100.0);
let art = square(&mut d, -20.0, -20.0, vec![]);
d.symbols.push(vectorcraft_doc::Symbol { name: "Shaped".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Shaped".into(), art: Arc::new(art) });
let instance = Node::new(d.alloc_id(), NodeKind::SymbolInstance { symbol: "Shaped".into(), xf: Affine::translate((-30.0, 50.0)) });
plugin_change_draws_as_new(with_art(d, vec![instance]), ID, Some(triangle(-15.0, -15.0, 10.0)), Some(triangle(0.0, 0.0, 150.0)));
}
@@ -1676,7 +1676,7 @@ fn effect_art_follows_the_plugins_installed() {
plugin_change_draws_as_new(group(vec![feather]), ID, small.clone(), big.clone());
let mut d = Document::new(200.0, 100.0);
let art = symbol_art(&mut d);
d.symbols.push(vectorcraft_doc::Symbol { name: "S".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "S".into(), art: Arc::new(art) });
let mut instance = Node::new(d.alloc_id(), NodeKind::SymbolInstance { symbol: "S".into(), xf: Affine::translate((100.0, 50.0)) });
instance.appearance.effects.push(plugin());
plugin_change_draws_as_new(with_art(d, vec![instance]), ID, small.clone(), big.clone());
@@ -1711,7 +1711,7 @@ fn live_objects_follow_the_plugins_installed() {
// Symbol `Shaped` is a square with the plug-in effect.
let mut d = Document::new(200.0, 100.0);
let art = black(&mut d, Rect::new(-20.0, -20.0, 20.0, 20.0));
d.symbols.push(vectorcraft_doc::Symbol { name: "Shaped".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Shaped".into(), art: Arc::new(art) });
let instance = Node::new(d.alloc_id(), NodeKind::SymbolInstance { symbol: "Shaped".into(), xf: Affine::translate((100.0, 50.0)) });
let envelope = arc_envelope(&mut d, vec![instance], true);
plugin_change_draws_as_new(with_art(d, vec![envelope]), ID, small.clone(), big.clone());
@@ -1753,7 +1753,7 @@ fn pattern_and_symbol_art_with_plugin_effects_follow_the_plugins_installed() {
// the instance's box, from (80, 30) to (120, 70).
let mut d = Document::new(200.0, 100.0);
let art = filled(&mut d, rectangle(Rect::new(-20.0, -20.0, 20.0, 20.0)), Paint::solid(Color::BLACK), vec![plugin()]);
d.symbols.push(vectorcraft_doc::Symbol { name: "Shaped".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Shaped".into(), art: Arc::new(art) });
let instance = Node::new(d.alloc_id(), NodeKind::SymbolInstance { symbol: "Shaped".into(), xf: Affine::translate((100.0, 50.0)) });
let mut group = Node::group(d.alloc_id(), vec![Arc::new(instance)]);
group.appearance.effects.push(drop_shadow());
@@ -1839,7 +1839,7 @@ fn pattern_tiles_draw_edits_to_what_their_art_shows() {
let doc = |pattern: &str, effects: Vec<vectorcraft_doc::Effect>| {
let mut d = Document::new(200.0, 100.0);
let art = symbol_art(&mut d);
d.symbols.push(vectorcraft_doc::Symbol { name: "S".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "S".into(), art: Arc::new(art) });
let shown = Node::new(d.alloc_id(), NodeKind::SymbolInstance { symbol: "S".into(), xf: Affine::translate((10.0, 10.0)) });
d.patterns.push(tile("Stamps", shown));
d.patterns.push(checks(Color::rgb(1.0, 0.0, 0.0)));
@@ -1890,10 +1890,10 @@ fn rasters_draw_symbol_brush_and_image_edits() {
// A group of an instance of symbol `A`, whose art shows symbol `S`, and a green square.
let mut d = Document::new(200.0, 100.0);
let art = symbol_art(&mut d);
d.symbols.push(vectorcraft_doc::Symbol { name: "S".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "S".into(), art: Arc::new(art) });
let shown = Node::new(d.alloc_id(), NodeKind::SymbolInstance { symbol: "S".into(), xf: Affine::IDENTITY });
let a = Node::group(d.alloc_id(), vec![Arc::new(shown)]);
d.symbols.push(vectorcraft_doc::Symbol { name: "A".into(), art: Arc::new(a) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "A".into(), art: Arc::new(a) });
let instance = Node::new(d.alloc_id(), NodeKind::SymbolInstance { symbol: "A".into(), xf: Affine::translate((50.0, 50.0)) });
let green = vectorcraft_doc::Appearance::basic(Paint::solid(Color::rgb(0.0, 0.8, 0.0)), Paint::None, 0.0);
let square = Node::path(d.alloc_id(), rectangle(Rect::new(120.0, 30.0, 160.0, 70.0)), green);
@@ -2067,7 +2067,7 @@ fn proof_doc() -> Document {
let spot = filled(&mut d, rectangle(Rect::new(110.0, 10.0, 150.0, 50.0)), spot, vec![blur]);
let yellow = filled(&mut d, rectangle(Rect::new(-10.0, -10.0, 10.0, 10.0)), Paint::solid(Color::cmyk(0.0, 0.0, 1.0, 0.0)), vec![]);
let art = Node::group(d.alloc_id(), vec![Arc::new(yellow)]);
d.symbols.push(vectorcraft_doc::Symbol { name: "S".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "S".into(), art: Arc::new(art) });
let instance = Node::new(d.alloc_id(), NodeKind::SymbolInstance { symbol: "S".into(), xf: Affine::translate((175.0, 75.0)) });
with_art(d, vec![cyan, circle, white, spot, instance])
}
@@ -2228,7 +2228,7 @@ fn symbol_instances_draw_as_groups_of_their_placed_art() {
let circle = vectorcraft_geom::shapes::ellipse(Rect::new(-6.0, -6.0, 6.0, 6.0));
let ap = vectorcraft_doc::Appearance::basic(Paint::solid(Color::rgb(1.0, 0.8, 0.0)), Paint::solid(Color::BLACK), 1.0);
let circle = Node::path(d.alloc_id(), circle, ap);
d.symbols.push(vectorcraft_doc::Symbol { name: "U".into(), art: Arc::new(circle) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "U".into(), art: Arc::new(circle) });
let id = d.alloc_id();
d.insert(Some(l), usize::MAX, Node::new(id, NodeKind::SymbolInstance { symbol: "U".into(), xf: Affine::translate((175.0, 25.0)) })).unwrap();
// `groups` holds the same art in place of the instances: groups, and the circle as a path.
@@ -2272,7 +2272,7 @@ fn inline_graphics_keep_their_placed_art_between_frames() {
use vectorcraft_doc::text::{CharStyle, InlineArt, TextObject, TextRun};
let mut d = Document::new(200.0, 100.0);
let art = symbol_art(&mut d);
d.symbols.push(vectorcraft_doc::Symbol { name: "S".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "S".into(), art: Arc::new(art) });
let style = CharStyle { size: 24.0, ..Default::default() };
let mut t = TextObject::point(vectorcraft_geom::Point::new(10.0, 50.0), "A", style.clone());
t.runs.push(TextRun::inline(InlineArt::new("S"), style.clone()));
+2 -2
View File
@@ -300,7 +300,7 @@ fn symbol_doc(effects: Vec<Effect>) -> Document {
shapes::rectangle(Rect::new(-10.0, -10.0, 10.0, 10.0)),
Appearance::basic(Paint::solid(Color::BLACK), Paint::None, 0.0),
);
d.symbols.push(Symbol { name: "Square".into(), art: Arc::new(art) });
d.symbols.push(Symbol { dynamic: false, name: "Square".into(), art: Arc::new(art) });
let mut n = Node::new(NodeId(0), NodeKind::SymbolInstance { symbol: "Square".into(), xf: Affine::translate((50.0, 50.0)) });
n.appearance.effects = effects;
doc_with(d, n)
@@ -469,7 +469,7 @@ fn a_transform_effect_on_a_group_symbol_or_envelope_moves_and_scales_the_gradien
t.id = NodeId(500);
// The type in a group, as the art of a symbol, and in an envelope that leaves it as it is.
let mut symbols = Document::new(100.0, 100.0);
symbols.symbols.push(Symbol { name: "Type".into(), art: Arc::new(t.clone()) });
symbols.symbols.push(Symbol { dynamic: false, name: "Type".into(), art: Arc::new(t.clone()) });
let holders = [
doc_with(Document::new(100.0, 100.0), Node::group(NodeId(0), vec![Arc::new(t.clone())])),
doc_with(symbols, Node::new(NodeId(0), NodeKind::SymbolInstance { symbol: "Type".into(), xf: Affine::IDENTITY })),
+5 -1
View File
@@ -281,7 +281,11 @@ fn a_ready_bitmap_draws_again_where_its_file_shows() {
let brush = vectorcraft_brush::Brush { name: "Cards".into(), kind: vectorcraft_brush::BrushKind::Scatter(scatter) };
vectorcraft_brush::store(&mut brushed, &[brush]);
assert_eq!(placed_area(&brushed, &only_a), Changed::Everything);
d.symbols.push(vectorcraft_doc::Symbol { name: "Card".into(), art: Arc::new(placed_node(65, "area-a", 40.0, 20.0, Affine::IDENTITY)) });
d.symbols.push(vectorcraft_doc::Symbol {
dynamic: false,
name: "Card".into(),
art: Arc::new(placed_node(65, "area-a", 40.0, 20.0, Affine::IDENTITY)),
});
assert_eq!(placed_area(&d, &only_a), Changed::Everything);
}
+1 -1
View File
@@ -310,7 +310,7 @@ fn rasterize_and_shadows_paint_in_stack_order() {
Appearance::basic(Paint::solid(Color::rgb(1.0, 0.0, 0.0)), Paint::None, 0.0),
);
art.appearance.effects.push(Effect { id: shadow.0.into(), params: shadow.1.clone(), visible: true });
d.symbols.push(vectorcraft_doc::Symbol { name: "Shadowed".into(), art: Arc::new(art) });
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Shadowed".into(), art: Arc::new(art) });
let mut instance = Node::new(d.alloc_id(), NodeKind::SymbolInstance { symbol: "Shadowed".into(), xf: Affine::translate((20.0, 20.0)) });
instance.appearance.effects.push(Effect { id: "rasterize".into(), params: json!({}), visible: true });
let l = d.layers[0].id;