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 fn merge(&mut self, other: &Self) {
59 self.paths_mut().extend_from_slice(other.paths());
60 self.metadata_mut().merge(other.metadata());
61 }
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 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}