mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-15 00:09:41 +00:00
Merge pull request #7449 from dotCooCoo/fix/links-stored-brush-art
Links: placed images in stored brush art get Links rows
This commit is contained in:
@@ -79,6 +79,7 @@ impl Document {
|
||||
index: _,
|
||||
guide_cache: _,
|
||||
inline_cycles: _,
|
||||
mounted_art: _,
|
||||
} = self;
|
||||
*version == other.version
|
||||
&& *title == other.title
|
||||
@@ -264,6 +265,7 @@ impl Document {
|
||||
index: Default::default(),
|
||||
guide_cache: Default::default(),
|
||||
inline_cycles: Default::default(),
|
||||
mounted_art: vec![],
|
||||
};
|
||||
let id = d.alloc_id();
|
||||
d.layers.push(Arc::new(Node::layer(id, "Layer 1", LayerColor::Preset(0))));
|
||||
|
||||
@@ -128,8 +128,8 @@ impl Document {
|
||||
}
|
||||
|
||||
/// The roots of every tree of the document's art: the layers, symbol definitions, graph designs,
|
||||
/// pattern swatches and the original of the pattern being edited. Opacity masks hang off nodes
|
||||
/// in these trees.
|
||||
/// pattern swatches, the original of the pattern being edited and [`Self::mounted_art`].
|
||||
/// Opacity masks hang off nodes in these trees.
|
||||
pub fn art_roots(&self) -> impl Iterator<Item = &Arc<Node>> {
|
||||
self.layers
|
||||
.iter()
|
||||
@@ -137,6 +137,7 @@ impl Document {
|
||||
.chain(self.graph_designs.iter().map(|g| &g.art))
|
||||
.chain(self.patterns.iter().flat_map(|p| p.art.iter()))
|
||||
.chain(self.pattern_edit.iter().filter_map(|pe| pe.original.as_ref()).flat_map(|p| p.art.iter()))
|
||||
.chain(self.mounted_art.iter().map(|(n, _)| n))
|
||||
}
|
||||
|
||||
/// [`Self::art_roots`], to change.
|
||||
@@ -147,6 +148,7 @@ impl Document {
|
||||
.chain(self.graph_designs.iter_mut().map(|g| &mut g.art))
|
||||
.chain(self.patterns.iter_mut().flat_map(|p| p.art.iter_mut()))
|
||||
.chain(self.pattern_edit.iter_mut().filter_map(|pe| pe.original.as_mut()).flat_map(|p| p.art.iter_mut()))
|
||||
.chain(self.mounted_art.iter_mut().map(|(n, _)| n))
|
||||
}
|
||||
|
||||
/// Does `wants` hold for a node anywhere in the document's art ([`Self::art_roots`]) or its
|
||||
|
||||
@@ -367,6 +367,12 @@ pub struct Document {
|
||||
/// saved.
|
||||
#[serde(skip)]
|
||||
inline_cycles: inline::InlineCycles,
|
||||
/// Art kept elsewhere in the document's data (the art of the stored brushes, which
|
||||
/// `vectorcraft-brush` reads from [`Self::unknown`]) while it is set here, each root with the
|
||||
/// `owner` the Links panel names for it, so the stored-art lookups ([`StoredRoot::Mounted`])
|
||||
/// find its objects too. Empty otherwise; never saved.
|
||||
#[serde(skip)]
|
||||
pub mounted_art: Vec<(Arc<Node>, serde_json::Value)>,
|
||||
}
|
||||
|
||||
fn ppi72() -> f64 {
|
||||
|
||||
@@ -248,14 +248,15 @@ fn walk_all<'a>(n: &'a Node, f: &mut impl FnMut(&'a Node)) {
|
||||
|
||||
impl Document {
|
||||
/// Visit every image object: in the layers, then in symbol definitions and pattern swatches,
|
||||
/// opacity-mask art included.
|
||||
/// then in [`Document::mounted_art`], opacity-mask art included.
|
||||
pub fn visit_images<'a>(&'a self, mut f: impl FnMut(&'a Node, &'a ImageObject)) {
|
||||
let roots = self
|
||||
.layers
|
||||
.iter()
|
||||
.chain(self.symbols.iter().map(|s| &s.art))
|
||||
.chain(self.graph_designs.iter().map(|g| &g.art))
|
||||
.chain(self.patterns.iter().flat_map(|p| &p.art));
|
||||
.chain(self.patterns.iter().flat_map(|p| &p.art))
|
||||
.chain(self.mounted_art.iter().map(|(n, _)| n));
|
||||
for root in roots {
|
||||
walk_all(root, &mut |n| {
|
||||
if let NodeKind::Image(im) = &n.kind {
|
||||
@@ -384,7 +385,8 @@ impl Align {
|
||||
|
||||
impl Document {
|
||||
/// Rewrite linked file locations in both raster images and placed VectorCraft documents,
|
||||
/// including those inside symbols, patterns and opacity masks. Used to make packages portable.
|
||||
/// including those inside symbols, patterns, opacity masks and [`Document::mounted_art`]. Used to
|
||||
/// make packages portable.
|
||||
pub fn update_links(&mut self, mut f: impl FnMut(&mut LinkInfo)) {
|
||||
fn walk(n: &mut Arc<Node>, f: &mut dyn FnMut(&mut LinkInfo)) {
|
||||
let n = Arc::make_mut(n);
|
||||
@@ -409,7 +411,8 @@ impl Document {
|
||||
.iter_mut()
|
||||
.chain(self.symbols.iter_mut().map(|s| &mut s.art))
|
||||
.chain(self.graph_designs.iter_mut().map(|g| &mut g.art))
|
||||
.chain(self.patterns.iter_mut().flat_map(|p| &mut p.art));
|
||||
.chain(self.patterns.iter_mut().flat_map(|p| &mut p.art))
|
||||
.chain(self.mounted_art.iter_mut().map(|(n, _)| n));
|
||||
for root in roots {
|
||||
walk(root, &mut f);
|
||||
}
|
||||
@@ -471,7 +474,8 @@ impl Document {
|
||||
.iter_mut()
|
||||
.chain(self.symbols.iter_mut().map(|s| &mut s.art))
|
||||
.chain(self.graph_designs.iter_mut().map(|g| &mut g.art))
|
||||
.chain(self.patterns.iter_mut().flat_map(|p| &mut p.art));
|
||||
.chain(self.patterns.iter_mut().flat_map(|p| &mut p.art))
|
||||
.chain(self.mounted_art.iter_mut().map(|(n, _)| n));
|
||||
for root in roots {
|
||||
update(root, &pick, &mut f);
|
||||
}
|
||||
|
||||
@@ -166,13 +166,15 @@ fn walk_all<'a>(n: &'a Node, f: &mut impl FnMut(&'a Node)) {
|
||||
}
|
||||
}
|
||||
|
||||
/// The roots of `d`'s art: layers, symbol definitions and pattern swatches.
|
||||
/// The roots of `d`'s art: layers, symbol definitions, pattern swatches and
|
||||
/// [`Document::mounted_art`].
|
||||
fn roots(d: &Document) -> impl Iterator<Item = &Arc<Node>> {
|
||||
d.layers
|
||||
.iter()
|
||||
.chain(d.symbols.iter().map(|s| &s.art))
|
||||
.chain(d.graph_designs.iter().map(|g| &g.art))
|
||||
.chain(d.patterns.iter().flat_map(|p| &p.art))
|
||||
.chain(d.mounted_art.iter().map(|(n, _)| n))
|
||||
}
|
||||
|
||||
fn roots_mut(d: &mut Document) -> impl Iterator<Item = &mut Arc<Node>> {
|
||||
@@ -181,6 +183,7 @@ fn roots_mut(d: &mut Document) -> impl Iterator<Item = &mut Arc<Node>> {
|
||||
.chain(d.symbols.iter_mut().map(|s| &mut s.art))
|
||||
.chain(d.graph_designs.iter_mut().map(|g| &mut g.art))
|
||||
.chain(d.patterns.iter_mut().flat_map(|p| &mut p.art))
|
||||
.chain(d.mounted_art.iter_mut().map(|(n, _)| n))
|
||||
}
|
||||
|
||||
impl Document {
|
||||
|
||||
@@ -20,6 +20,8 @@ pub enum StoredRoot {
|
||||
GraphDesign(usize),
|
||||
/// Object `art` of pattern swatch `pattern` of [`Document::patterns`].
|
||||
Pattern { pattern: usize, art: usize },
|
||||
/// Root `index` of [`Document::mounted_art`].
|
||||
Mounted(usize),
|
||||
}
|
||||
|
||||
/// Where a stored object is: its root and, inside opacity-mask art, the object that mask belongs
|
||||
@@ -170,10 +172,12 @@ impl Document {
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(p, def)| def.art.iter().enumerate().map(move |(a, n)| (n, StoredRoot::Pattern { pattern: p, art: a })));
|
||||
symbols.chain(designs).chain(patterns)
|
||||
let mounted = self.mounted_art.iter().enumerate().map(|(i, (n, _))| (n, StoredRoot::Mounted(i)));
|
||||
symbols.chain(designs).chain(patterns).chain(mounted)
|
||||
}
|
||||
|
||||
/// Every root of stored art: the layers, then the art of symbols, graph designs and patterns.
|
||||
/// Every root of stored art: the layers, then the art of symbols, graph designs and patterns,
|
||||
/// then [`Document::mounted_art`].
|
||||
fn stored_roots(&self) -> impl Iterator<Item = (&Arc<Node>, StoredRoot)> {
|
||||
self.layers.iter().enumerate().map(|(i, l)| (l, StoredRoot::Layer(i))).chain(self.definition_roots())
|
||||
}
|
||||
@@ -185,12 +189,13 @@ impl Document {
|
||||
StoredRoot::Symbol(i) => self.symbols.get_mut(i).map(|s| &mut s.art),
|
||||
StoredRoot::GraphDesign(i) => self.graph_designs.get_mut(i).map(|g| &mut g.art),
|
||||
StoredRoot::Pattern { pattern, art } => self.patterns.get_mut(pattern)?.art.get_mut(art),
|
||||
StoredRoot::Mounted(i) => self.mounted_art.get_mut(i).map(|(n, _)| n),
|
||||
}
|
||||
}
|
||||
|
||||
/// Visit every stored object with its place: the layer tree with the opacity-mask art of its
|
||||
/// objects, then the art of symbols, graph designs and pattern swatches (their definitions,
|
||||
/// once each, never the copies instances and tiles draw).
|
||||
/// once each, never the copies instances and tiles draw), then [`Document::mounted_art`].
|
||||
pub fn visit_stored<'a>(&'a self, mut f: impl FnMut(&'a Node, StoredPlace)) {
|
||||
self.visit_stored_arcs(|n, place| f(n, place));
|
||||
}
|
||||
@@ -238,6 +243,29 @@ impl Document {
|
||||
/// visited before them (the layer tree's own objects first) a new id, so every stored object
|
||||
/// can be named by its id. Returns how many changed; the id counter then stands above them.
|
||||
pub(crate) fn renumber_stored_duplicates(&mut self) -> usize {
|
||||
self.renumber_duplicates_in(|_| true)
|
||||
}
|
||||
|
||||
/// Raises the id counter above the ids of [`Document::mounted_art`]'s objects, then gives those
|
||||
/// objects whose id is 0, past the id range, or already taken by an object visited before them
|
||||
/// (every other stored object first) a new id, as [`Self::renumber_stored_duplicates`] gives
|
||||
/// them. Returns how many changed.
|
||||
pub fn number_mounted_art(&mut self) -> usize {
|
||||
let mut max = 0;
|
||||
for (i, (root, _)) in self.mounted_art.iter().enumerate() {
|
||||
visit(root, StoredPlace { root: StoredRoot::Mounted(i), mask_of: None }, &mut |n, _| {
|
||||
if n.id.0 < MAX_NEXT_ID {
|
||||
max = max.max(n.id.0);
|
||||
}
|
||||
});
|
||||
}
|
||||
self.raise_next_id(max.saturating_add(1));
|
||||
self.renumber_duplicates_in(|r| matches!(r, StoredRoot::Mounted(_)))
|
||||
}
|
||||
|
||||
/// [`Self::renumber_stored_duplicates`] for the roots `pick` holds for only; the objects of the
|
||||
/// others still count as taken.
|
||||
fn renumber_duplicates_in(&mut self, pick: impl Fn(StoredRoot) -> bool) -> usize {
|
||||
let mut seen: HashSet<NodeId> = HashSet::new();
|
||||
for l in &self.layers {
|
||||
l.walk(&mut |n| {
|
||||
@@ -250,9 +278,9 @@ impl Document {
|
||||
// Per root, the visiting indices of the nodes that take a new id.
|
||||
let mut fresh: Vec<(StoredRoot, HashSet<usize>)> = vec![];
|
||||
for (root, r) in self.stored_roots() {
|
||||
let (mut i, mut here) = (0, HashSet::new());
|
||||
let (mut i, mut here, take) = (0, HashSet::new(), pick(r));
|
||||
visit(root, StoredPlace { root: r, mask_of: None }, &mut |n, place| {
|
||||
if !place.in_layers() && (n.id.0 == 0 || n.id.0 >= MAX_NEXT_ID || !seen.insert(n.id)) {
|
||||
if !place.in_layers() && (n.id.0 == 0 || n.id.0 >= MAX_NEXT_ID || !seen.insert(n.id)) && take {
|
||||
here.insert(i);
|
||||
}
|
||||
i += 1;
|
||||
@@ -355,6 +383,30 @@ mod tests {
|
||||
assert_eq!(d, again);
|
||||
}
|
||||
|
||||
/// Mounted art is found and changed by id after the other stored art, and numbering it gives
|
||||
/// its objects that repeat another stored object's id, or have none, new ids above the counter,
|
||||
/// leaving every other object's id and its own unique ones as they are.
|
||||
#[test]
|
||||
fn mounted_art_is_found_and_numbered_after_the_rest() {
|
||||
let mut d = doc([12, 13, 14]);
|
||||
let owner = serde_json::json!({ "kind": "brush" });
|
||||
d.mounted_art = vec![(Arc::new(masked(10, rect(0))), owner.clone()), (Arc::new(rect(15)), owner)];
|
||||
assert_eq!(d.stored_node(NodeId(15)).map(|(_, p)| p.root), Some(StoredRoot::Mounted(1)));
|
||||
let before = d.clone();
|
||||
assert_eq!(d.number_mounted_art(), 2);
|
||||
let mut ids = vec![];
|
||||
d.visit_stored(|n, _| ids.push(n.id.0));
|
||||
let unique: HashSet<u64> = ids.iter().copied().collect();
|
||||
assert_eq!(unique.len(), ids.len(), "{ids:?}");
|
||||
assert_eq!(&ids[..6], &[d.layers[0].id.0, 10, 11, 12, 13, 14], "the other art keeps its ids");
|
||||
assert_eq!(ids[8], 15, "a unique id is kept");
|
||||
assert!(ids.iter().all(|id| *id < d.peek_next_id()), "{ids:?} {}", d.peek_next_id());
|
||||
assert!(Arc::ptr_eq(&d.symbols[0].art, &before.symbols[0].art) && Arc::ptr_eq(&d.mounted_art[1].0, &before.mounted_art[1].0));
|
||||
d.stored_node_mut(NodeId(15)).unwrap().name = Some("changed".into());
|
||||
assert_eq!(d.mounted_art[1].0.name.as_deref(), Some("changed"));
|
||||
assert_eq!(d.number_mounted_art(), 0, "a second pass changes nothing");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn renumbered_objects_on_path_keep_their_path_and_members() {
|
||||
// A symbol's Objects on Path group whose path repeats the layer's object 10 and whose
|
||||
|
||||
Reference in New Issue
Block a user