mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-08 04:50:18 +00:00
Symbols: the instance a symbol is edited from is not drawn in the view while it is edited
Entering symbol editing mode from an instance puts a copy of the symbol's art on the editing layer in the instance's place, and the instance stays in the document. The view drew the instance dimmed under the copy, so art with transparency showed darker, and its edges showed a dimmed fringe. The renderer now leaves that instance out of the screen view while the editing layer is isolated, with its masks and effects, and in Outline view inside a clip group. Exports and thumbnails draw it as before.
This commit is contained in:
@@ -118,11 +118,19 @@ impl Renderer {
|
||||
self.path_of(f.symbols, &owner)
|
||||
}
|
||||
|
||||
/// Whether `a` is left out of this frame: hidden, a skipped template or guide, or culled.
|
||||
/// Symbol editing mode on screen: `id` is the instance editing was entered from, which is not
|
||||
/// drawn (with its masks and effects), since the editing layer holds a copy of its art in its
|
||||
/// place.
|
||||
fn edited_instance(f: &Frame, id: NodeId) -> bool {
|
||||
f.opts.isolated.is_some() && f.doc.symbol_edit.as_ref().is_some_and(|e| e.instance == Some(id))
|
||||
}
|
||||
|
||||
/// Whether `a` is left out of this frame: hidden, a skipped template or guide, the instance a
|
||||
/// symbol is edited from, or culled.
|
||||
fn skipped(&mut self, f: &Frame, a: &Arc<Node>) -> bool {
|
||||
let skipped_view_item = (f.opts.skip_templates || f.opts.hide_templates) && matches!(a.kind, NodeKind::Layer { template: true, .. })
|
||||
|| (f.opts.skip_templates || f.opts.hide_guides) && matches!(a.kind, NodeKind::Path { guide: true, .. });
|
||||
if !a.visible || skipped_view_item || f.opts.hidden.contains(&a.id) {
|
||||
if !a.visible || skipped_view_item || f.opts.hidden.contains(&a.id) || Self::edited_instance(f, a.id) {
|
||||
return true;
|
||||
}
|
||||
match self.bounds_of(f.symbols, a) {
|
||||
@@ -551,7 +559,11 @@ impl Renderer {
|
||||
/// `fx_owner`, so its path, its live-effect geometry and, through [`Self::draw_kept_node`], a
|
||||
/// live object's evaluation come from the caches.
|
||||
fn draw_outline_member(&mut self, ctx: &mut RenderContext, f: &Frame, c: &Arc<Node>) {
|
||||
if !c.visible || f.opts.hidden.contains(&c.id) || f.opts.hide_guides && matches!(c.kind, NodeKind::Path { guide: true, .. }) {
|
||||
if !c.visible
|
||||
|| f.opts.hidden.contains(&c.id)
|
||||
|| f.opts.hide_guides && matches!(c.kind, NodeKind::Path { guide: true, .. })
|
||||
|| Self::edited_instance(f, c.id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
match self.bounds_of(f.symbols, c) {
|
||||
|
||||
@@ -417,6 +417,65 @@ fn isolation_mode_dims_the_art_around_the_isolated_group() {
|
||||
assert_eq!(isolated(NodeId(u64::MAX)).pixels, plain.pixels);
|
||||
}
|
||||
|
||||
/// Symbol editing mode entered from an instance of art at 50% opacity, plain, with a drop shadow,
|
||||
/// or in a clip group in Outline view: the screen view draws the copy on the editing layer and not
|
||||
/// the instance, so the art shows once, as it does with the instance removed. Without the editing
|
||||
/// layer isolated (an export), the instance draws.
|
||||
#[test]
|
||||
fn symbol_editing_does_not_draw_the_instance_it_was_entered_from() {
|
||||
use vectorcraft_doc::{Effect, Symbol, SymbolEdit};
|
||||
let editing = |effects: &[Effect], clipped: bool, with_instance: bool| {
|
||||
let mut d = Document::new(100.0, 100.0);
|
||||
let mut art = rect(&mut d, Rect::new(20.0, 20.0, 80.0, 80.0), S);
|
||||
art.opacity = 0.5;
|
||||
d.symbols.push(Symbol { dynamic: false, name: "Square".into(), art: Arc::new(art.clone()) });
|
||||
let mut inst = Node::new(d.alloc_id(), NodeKind::SymbolInstance { symbol: "Square".into(), xf: Affine::IDENTITY });
|
||||
inst.appearance.effects = effects.to_vec();
|
||||
let id = inst.id;
|
||||
let l = d.layers[0].id;
|
||||
let members: Vec<Node> = with_instance.then_some(inst).into_iter().collect();
|
||||
if clipped {
|
||||
let clip = clip_path(&mut d, Rect::new(10.0, 10.0, 90.0, 90.0));
|
||||
let children = std::iter::once(clip).chain(members).map(Arc::new).collect();
|
||||
let g = Node::new(d.alloc_id(), NodeKind::Group { children, clip: true });
|
||||
d.insert(Some(l), usize::MAX, g).unwrap();
|
||||
} else {
|
||||
for n in members {
|
||||
d.insert(Some(l), usize::MAX, n).unwrap();
|
||||
}
|
||||
}
|
||||
let layer = d.add_layer(Some("Square"));
|
||||
let copy = d.reid(&art);
|
||||
d.insert(Some(layer), usize::MAX, copy).unwrap();
|
||||
d.symbol_edit = Some(SymbolEdit {
|
||||
symbol: "Square".into(),
|
||||
layer,
|
||||
instance: Some(id),
|
||||
xf: Affine::IDENTITY,
|
||||
group: false,
|
||||
changed: false,
|
||||
back_isolation: None,
|
||||
back_layer: None,
|
||||
});
|
||||
(d, layer)
|
||||
};
|
||||
let params = serde_json::json!({"x": 4, "y": 4, "blur": 1, "opacity": 100});
|
||||
let shadow = Effect { id: "stylize.dropShadow".into(), params, visible: true };
|
||||
for (effects, clipped) in [(vec![], false), (vec![shadow], false), (vec![], true)] {
|
||||
let what = format!("{} effects, clipped {clipped}", effects.len());
|
||||
let (d, layer) = editing(&effects, clipped, true);
|
||||
let (alone, alone_layer) = editing(&effects, clipped, false);
|
||||
let opts = RenderOptions { outline: clipped, ..Default::default() };
|
||||
let screen = |d: &Document, layer| render_with(&mut Renderer::new(), d, &RenderOptions { isolated: Some(layer), ..opts.clone() });
|
||||
let shown = screen(&d, layer);
|
||||
assert_eq!(shown.pixel(50, 50), screen(&alone, alone_layer).pixel(50, 50), "{what}: the art shows once");
|
||||
assert!(shown.pixels == screen(&alone, alone_layer).pixels, "{what}: the view draws as without the instance");
|
||||
// An export draws the instance and the copy.
|
||||
let export = |d: &Document| render_with(&mut Renderer::new(), d, &opts).pixels;
|
||||
assert!(export(&d) != export(&alone), "{what}: an export draws the instance");
|
||||
}
|
||||
}
|
||||
|
||||
/// Deeply nested isolated blending groups (#3581): each level is drawn offscreen only while the
|
||||
/// offscreen pixels alive stay within a few frames' worth; deeper levels are drawn as layers.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user