vsvg/layer/
mod.rs

1mod flattened_layer;
2#[allow(clippy::module_inception)]
3mod layer;
4mod metadata;
5
6use crate::{IndexBuilder, PathDataTrait, PathTrait, Point, Transforms};
7
8use crate::stats::LayerStats;
9pub use flattened_layer::FlattenedLayer;
10pub use layer::Layer;
11pub use metadata::LayerMetadata;
12
13pub trait LayerTrait<P: PathTrait<D>, D: PathDataTrait>: Default + Transforms {
14    #[must_use]
15    fn new() -> Self {
16        Self::default()
17    }
18
19    #[must_use]
20    fn from_paths_and_metadata(paths: Vec<P>, metadata: LayerMetadata) -> Self;
21
22    fn paths(&self) -> &[P];
23
24    fn paths_mut(&mut self) -> &mut Vec<P>;
25
26    fn for_each<F>(&mut self, f: F)
27    where
28        F: Fn(&mut P),
29    {
30        self.paths_mut().iter_mut().for_each(f);
31    }
32
33    fn metadata(&self) -> &LayerMetadata;
34
35    fn metadata_mut(&mut self) -> &mut LayerMetadata;
36
37    fn bounds(&self) -> Option<kurbo::Rect> {
38        if self.paths().is_empty() {
39            return None;
40        }
41
42        let first = self.paths().first().expect("checked").bounds();
43        Some(
44            self.paths()
45                .iter()
46                .skip(1)
47                .fold(first, |acc, path| acc.union(path.bounds())),
48        )
49    }
50
51    fn push_path(&mut self, path: impl Into<P>) {
52        self.paths_mut().push(path.into());
53    }
54
55    /// Merge another layer into this one.
56    ///
57    /// Also merges the metadata, see [`LayerMetadata::merge`].
58    fn merge(&mut self, other: &Self) {
59        self.paths_mut().extend_from_slice(other.paths());
60        self.metadata_mut().merge(other.metadata());
61        //TODO(#4): merge default path metadata and cascade difference to paths
62    }
63
64    fn sort(&mut self, flip: bool) {
65        self.sort_with_builder(IndexBuilder::default().flip(flip));
66    }
67
68    fn sort_with_builder(&mut self, builder: IndexBuilder) {
69        if self.paths().len() <= 1 {
70            return;
71        }
72
73        let mut new_paths = Vec::with_capacity(self.paths().len());
74        let mut index = builder.build(self.paths());
75
76        let mut pos = Point::ZERO;
77        while let Some((path_item, reverse)) = index.pop_nearest(&pos) {
78            new_paths.push((*path_item.path).clone());
79            if reverse {
80                pos = path_item.start.unwrap_or(pos);
81                new_paths
82                    .last_mut()
83                    .expect("just inserted")
84                    .data_mut()
85                    .flip();
86            } else {
87                pos = path_item.end.unwrap_or(pos);
88            }
89        }
90
91        // add any remaining, unindexed paths
92        while let Some(path_item) = index.pop_first() {
93            new_paths.push((*path_item.path).clone());
94        }
95
96        *self.paths_mut() = new_paths;
97    }
98
99    fn pen_up_trajectories(&self) -> Vec<(Point, Point)> {
100        self.paths()
101            .windows(2)
102            .filter_map(|w| {
103                if let Some(ref start) = w[0].end() {
104                    #[allow(clippy::manual_map)]
105                    if let Some(ref end) = w[1].start() {
106                        Some((*start, *end))
107                    } else {
108                        None
109                    }
110                } else {
111                    None
112                }
113            })
114            .collect()
115    }
116
117    fn stats(&self) -> LayerStats {
118        LayerStats::from_layer(self)
119    }
120}