Symbols: an instance that only moves holds art its 9-slice guides move

A symbol whose natural size is not 20 x 20 is drawn through its 9-slice
guides by an instance that only moves, so art the guides move can draw
outside the art box moved by the instance. The visual box took that
moved art box alone, and the renderer culled the instance when only the
moved art was in view. The visual box now measures such an instance as
it does a scaled one.
This commit is contained in:
dotCooCoo
2026-11-10 23:07:27 -08:00
parent 9f44e37274
commit badcf3f63f
2 changed files with 27 additions and 1 deletions
+4 -1
View File
@@ -373,7 +373,10 @@ impl Node {
NodeKind::SymbolInstance { symbol, xf } => {
let b = match symbols.and_then(|s| Some((s, s.art(symbol)?))) {
Some((s, art)) if !s.art_strokes() => xf.transform_rect_bbox(art.geometric),
Some((_, art)) if moves_only(*xf) => xf.transform_rect_bbox(art.visual),
// A symbol drawn at other than its natural size goes through its 9-slice guides.
Some((s, art)) if moves_only(*xf) && s.document().symbol_slice_map_of(symbol, art.geometric, *xf).is_none() => {
xf.transform_rect_bbox(art.visual)
}
Some((s, art)) => {
let placed = xf.transform_rect_bbox(art.geometric);
match s.placed_visual(symbol, *xf).or_else(|| s.sliced_visual(symbol, *xf)) {
+23
View File
@@ -425,6 +425,29 @@ fn an_instance_is_culled_by_the_art_nested_9_slice_guides_move() {
assert_eq!(img.pixel(95, 50), [255, 0, 0, 255]);
}
#[test]
fn an_instance_that_only_moves_is_culled_by_the_art_its_9_slice_guides_move() {
// A symbol made in the app: art in the 20 × 20 square, a natural size of 2 × 2, and guides a
// quarter in. An instance that only moves draws it at 20 × 20, ten times its natural size. The
// bar from x −9.9 to 0.1 crosses the left guide and is placed by the middle column's map,
// x → 1.9x: moved 115 pt right, it draws from x 96.19 to 115.19 while its art box starts at
// x 105, out of view.
let mut d = Document::new(100.0, 100.0);
let bar = rect_node(&mut d, Rect::new(-9.9, -1.0, 0.1, 1.0), Paint::solid(Color::rgb(1.0, 0.0, 0.0)), Paint::None, 0.0);
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Bar".into(), art: Arc::new(bar) });
let frame = rect_node(&mut d, Rect::new(-10.0, -10.0, 10.0, 10.0), Paint::None, Paint::None, 0.0);
let inner = Node::new(d.alloc_id(), NodeKind::SymbolInstance { symbol: "Bar".into(), xf: Affine::IDENTITY });
let art = Node::group(d.alloc_id(), vec![Arc::new(frame), Arc::new(inner)]);
d.symbols.push(vectorcraft_doc::Symbol { dynamic: false, name: "Panel".into(), art: Arc::new(art) });
d.unknown.insert(vectorcraft_doc::SYMBOL_SIZES.into(), serde_json::json!({ "Panel": [2.0, 2.0] }));
d.set_symbol_nine_slice("Panel", Some(vectorcraft_doc::SymbolNineSlice::default()));
let (id, l) = (d.alloc_id(), d.layers[0].id);
let xf = Affine::translate((115.0, 50.0));
d.insert(Some(l), 0, Node::new(id, NodeKind::SymbolInstance { symbol: "Panel".into(), xf })).unwrap();
let img = Renderer::new().render(&d, 100, 100, Affine::IDENTITY, &RenderOptions::default());
assert_eq!(img.pixel(98, 50), [255, 0, 0, 255]);
}
#[test]
fn an_instance_with_a_geometry_effect_is_culled_by_its_reshaped_art() {
// The art, (0, 0) to (20, 20), drawn from (-100, 10) is out of view, and the Transform effect