mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-11 23:32:17 +00:00
Vector import: split a document at its images in one traversal (#3514)
This commit is contained in:
@@ -477,35 +477,51 @@ pub fn split_at_images(doc: &Doc) -> Vec<Doc> {
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
/// Keep the shapes between images `seg − 1` and `seg`; `seen` counts images passed.
|
||||
fn prune(g: &effectcraft_svg::Group, seg: usize, seen: &mut usize) -> effectcraft_svg::Group {
|
||||
let mut out = g.clone();
|
||||
out.children = vec![];
|
||||
/// `g` without its children, holding `children` instead (nothing below `g` is copied).
|
||||
fn shell(g: &effectcraft_svg::Group, children: Vec<Node>) -> effectcraft_svg::Group {
|
||||
effectcraft_svg::Group {
|
||||
name: g.name.clone(),
|
||||
transform: g.transform,
|
||||
opacity: g.opacity,
|
||||
children,
|
||||
clip: g.clip.clone(),
|
||||
blend: g.blend,
|
||||
mask: g.mask.clone(),
|
||||
isolated: g.isolated,
|
||||
knockout: g.knockout,
|
||||
}
|
||||
}
|
||||
/// One traversal for every segment: the pieces of `g` as (segment, group), by ascending
|
||||
/// segment; `seen` counts images passed. Each shape is copied once, into its own segment.
|
||||
fn split(g: &effectcraft_svg::Group, seen: &mut usize) -> Vec<(usize, effectcraft_svg::Group)> {
|
||||
let mut by: Vec<(usize, Vec<Node>)> = vec![];
|
||||
let mut put = |seg: usize, node: Node| match by.last_mut() {
|
||||
Some(last) if last.0 == seg => last.1.push(node),
|
||||
_ => by.push((seg, vec![node])),
|
||||
};
|
||||
for c in &g.children {
|
||||
match c {
|
||||
Node::Image(_) => *seen += 1,
|
||||
Node::Shape(_) => {
|
||||
if *seen == seg {
|
||||
out.children.push(c.clone());
|
||||
}
|
||||
}
|
||||
Node::Shape(_) => put(*seen, c.clone()),
|
||||
Node::Group(s) => {
|
||||
let p = prune(s, seg, seen);
|
||||
if !p.children.is_empty() {
|
||||
out.children.push(Node::Group(p));
|
||||
for (seg, part) in split(s, seen) {
|
||||
put(seg, Node::Group(part));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
out
|
||||
by.into_iter().map(|(seg, children)| (seg, shell(g, children))).collect()
|
||||
}
|
||||
let n = count(&doc.root);
|
||||
(0..=n)
|
||||
.map(|seg| {
|
||||
let mut d = doc.clone();
|
||||
d.root = prune(&doc.root, seg, &mut 0);
|
||||
d
|
||||
})
|
||||
let mut roots: Vec<Option<effectcraft_svg::Group>> = vec![None; n + 1];
|
||||
for (seg, part) in split(&doc.root, &mut 0) {
|
||||
if let Some(slot) = roots.get_mut(seg) {
|
||||
*slot = Some(part);
|
||||
}
|
||||
}
|
||||
roots
|
||||
.into_iter()
|
||||
.map(|root| Doc { width: doc.width, height: doc.height, root: root.unwrap_or_else(|| shell(&doc.root, vec![])), skipped: doc.skipped.clone() })
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -655,3 +671,73 @@ pub fn masks_layer(proj: &mut Project, comp: &Comp, name: &str, outlines: Vec<Sh
|
||||
}
|
||||
l
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use effectcraft_svg::{Geom, Group, Image, Shape};
|
||||
|
||||
fn shape(name: &str) -> Node {
|
||||
let geom = Geom::Rect { x: 0.0, y: 0.0, w: 1.0, h: 1.0, rx: 0.0, ry: 0.0 };
|
||||
Node::Shape(Shape {
|
||||
name: name.into(),
|
||||
geom,
|
||||
transform: Affine::IDENTITY,
|
||||
fill: None,
|
||||
fill_opacity: 1.0,
|
||||
fill_rule: effectcraft_svg::FillRule::NonZero,
|
||||
stroke: None,
|
||||
opacity: 1.0,
|
||||
})
|
||||
}
|
||||
fn image() -> Node {
|
||||
Node::Image(Image { name: "i".into(), transform: Affine::IDENTITY, width: 1, height: 1, rgba: std::sync::Arc::new(vec![0; 4]), opacity: 1.0 })
|
||||
}
|
||||
fn group(name: &str, children: Vec<Node>) -> Node {
|
||||
let mut g = Group::new(name);
|
||||
g.children = children;
|
||||
Node::Group(g)
|
||||
}
|
||||
fn names(g: &Group, out: &mut Vec<String>) {
|
||||
for c in &g.children {
|
||||
match c {
|
||||
Node::Shape(s) => out.push(s.name.clone()),
|
||||
Node::Group(s) => {
|
||||
out.push(format!("<{}", s.name));
|
||||
names(s, out);
|
||||
out.push(format!("{}>", s.name));
|
||||
}
|
||||
Node::Image(_) => out.push("IMG".into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Segment `k` keeps the shapes between image `k - 1` and `k`, with the groups that hold them.
|
||||
#[test]
|
||||
fn split_keeps_each_segments_shapes_and_their_groups() {
|
||||
let mut root = Group::new("root");
|
||||
root.children = vec![
|
||||
shape("a"),
|
||||
image(),
|
||||
group("g1", vec![shape("b"), image(), shape("c"), group("g2", vec![shape("d"), image(), shape("e")])]),
|
||||
image(),
|
||||
group("empty", vec![image()]),
|
||||
shape("f"),
|
||||
];
|
||||
let doc = Doc { width: 10.0, height: 10.0, root, skipped: vec![] };
|
||||
let segs = split_at_images(&doc);
|
||||
assert_eq!(segs.len(), 6);
|
||||
let got: Vec<Vec<String>> = segs
|
||||
.iter()
|
||||
.map(|d| {
|
||||
let mut v = vec![];
|
||||
names(&d.root, &mut v);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
let want: Vec<Vec<&str>> =
|
||||
vec![vec!["a"], vec!["<g1", "b", "g1>"], vec!["<g1", "c", "<g2", "d", "g2>", "g1>"], vec!["<g1", "<g2", "e", "g2>", "g1>"], vec![], vec!["f"]];
|
||||
assert_eq!(got, want);
|
||||
assert!(segs.iter().all(|d| d.root.name == "root" && d.width == 10.0));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user