This repository has no description
1use itertools::Itertools;
2
3use crate::{Fill, Filter, Object, ObjectSizes, Point, Region, Toggleable};
4use std::{collections::HashMap, fmt::Display};
5
6#[derive(Debug, Clone, Default)]
7// #[wasm_bindgen(getter_with_clone)]
8pub struct Layer {
9 pub object_sizes: ObjectSizes,
10 pub objects: HashMap<String, Object>,
11 pub name: String,
12 pub hidden: bool,
13}
14
15impl Layer {
16 pub fn new(name: impl Display) -> Self {
17 Layer {
18 object_sizes: ObjectSizes::default(),
19 objects: HashMap::new(),
20 name: format!("{}", name),
21 hidden: false,
22 }
23 }
24
25 pub fn hide(&mut self) {
26 self.hidden = true;
27 }
28
29 pub fn show(&mut self) {
30 self.hidden = false;
31 }
32
33 pub fn toggle(&mut self) {
34 self.hidden.toggle();
35 }
36
37 pub fn object(&mut self, name: &str) -> &mut Object {
38 self.safe_object(name).unwrap()
39 }
40
41 pub fn safe_object(&mut self, name: &str) -> Option<&mut Object> {
42 self.objects.get_mut(name)
43 }
44
45 pub fn objects_sorted_owned(
46 &self,
47 ) -> impl Iterator<Item = (String, Object)> + '_ {
48 self.objects
49 .iter()
50 .sorted_by_cached_key(|&(id, _)| id.clone())
51 .map(|(id, obj)| (id.clone(), obj.clone()))
52 }
53
54 // Useful to be able to guarantee a stable order when rendering.
55 pub fn objects_sorted(&self) -> impl Iterator<Item = (&String, &Object)> {
56 self.objects
57 .iter()
58 .sorted_by_cached_key(|&(id, _)| id.clone())
59 }
60
61 // Useful to be able to guarantee a stable order when rendering.
62 pub fn objects_sorted_mut(
63 &mut self,
64 ) -> impl Iterator<Item = (&String, &mut Object)> {
65 self.objects
66 .iter_mut()
67 .sorted_by_cached_key(|&(id, _)| id.clone())
68 }
69
70 pub fn objects_in(
71 &mut self,
72 region: Region,
73 ) -> impl Iterator<Item = (&String, &mut Object)> {
74 self.objects
75 .iter_mut()
76 .filter(move |(_, obj)| obj.shape.region().within(®ion))
77 }
78
79 pub fn object_at(&mut self, point: Point) -> Option<&mut Object> {
80 self.find_object_mut(|o| o.position() == point.as_corner())
81 }
82
83 pub fn has_object_that(&self, pred: impl Fn(&Object) -> bool) -> bool {
84 self.objects.values().any(|obj| pred(obj))
85 }
86
87 pub fn find_object_mut(
88 &mut self,
89 pred: impl Fn(&Object) -> bool,
90 ) -> Option<&mut Object> {
91 self.objects.values_mut().find(|obj| pred(obj))
92 }
93
94 pub fn find_object(&self, pred: impl Fn(&Object) -> bool) -> Option<&Object> {
95 self.objects.values().find(|obj| pred(obj))
96 }
97
98 // Remove all objects.
99 pub fn clear(&mut self) {
100 self.objects.clear();
101 }
102
103 pub fn replace(&mut self, with: Layer) {
104 self.objects.clone_from(&with.objects);
105 }
106
107 pub fn remove_all_objects_in(&mut self, region: &Region) {
108 self.objects.retain(|_, Object { shape: object, .. }| {
109 !object.region().within(region)
110 })
111 }
112
113 pub fn paint_all_objects(&mut self, fill: Fill) {
114 for obj in self.objects.values_mut() {
115 obj.fill = Some(fill);
116 }
117 }
118
119 pub fn filter_all_objects(&mut self, filter: Filter) {
120 for obj in self.objects.values_mut() {
121 obj.filters.push(filter)
122 }
123 }
124
125 pub fn move_all_objects(&mut self, dx: i32, dy: i32) {
126 self.objects
127 .iter_mut()
128 .for_each(|(_, Object { shape: object, .. })| {
129 object.translate(dx, dy)
130 });
131 }
132
133 pub fn add(&mut self, name: impl Display, object: impl Into<Object>) {
134 let name_str = format!("{}", name);
135
136 if self.objects.contains_key(&name_str) {
137 panic!("object {} already exists in layer {}", name_str, self.name);
138 }
139
140 self.set(name_str, object);
141 }
142
143 pub fn add_anon(&mut self, object: impl Into<Object>) {
144 self.add(format!("anon-{}", self.objects.len()), object);
145 }
146
147 pub fn add_many(
148 &mut self,
149 objects: impl IntoIterator<Item = (impl Display, Object)>,
150 ) {
151 for (name, obj) in objects {
152 self.add(name, obj);
153 }
154 }
155
156 pub fn add_many_anon(&mut self, objects: impl IntoIterator<Item = Object>) {
157 for obj in objects {
158 self.add_anon(obj);
159 }
160 }
161
162 pub fn set(&mut self, name: impl Display, object: impl Into<Object>) {
163 let name_str = format!("{}", name);
164
165 self.objects.insert(name_str, object.into());
166 }
167
168 pub fn filter_object(
169 &mut self,
170 name: &str,
171 filter: Filter,
172 ) -> Result<(), String> {
173 self.objects
174 .get_mut(name)
175 .ok_or(format!("Object '{}' not found", name))?
176 .filters
177 .push(filter);
178
179 Ok(())
180 }
181
182 pub fn remove_object(&mut self, name: &str) {
183 self.objects.remove(name);
184 }
185
186 pub fn replace_object(&mut self, name: &str, object: Object) {
187 self.remove_object(name);
188 self.add(name, object);
189 }
190
191 pub fn add_objects(&mut self, objects: impl IntoIterator<Item = Object>) {
192 for obj in objects {
193 self.add_anon(obj);
194 }
195 }
196
197 pub fn objects_with_tag(
198 &mut self,
199 tag: impl Display,
200 ) -> impl Iterator<Item = (&String, &mut Object)> {
201 let tag_str = format!("{}", tag);
202 self.objects
203 .iter_mut()
204 .filter(move |(_, obj)| obj.has_tag(&tag_str))
205 }
206
207 pub fn tag_objects(
208 &mut self,
209 tag: impl Display,
210 objects: impl Fn(&String, &Object) -> bool,
211 ) {
212 let tag_str = format!("{}", tag);
213 for (_, obj) in
214 self.objects.iter_mut().filter(|(id, obj)| objects(id, obj))
215 {
216 obj.tag(&tag_str);
217 }
218 }
219
220 /// Returns the effective region the layer occupies, by merging all its objects' regions.
221 pub fn region(&self) -> Region {
222 self.objects
223 .values()
224 .map(|object| object.region())
225 .fold(Region::default(), |acc, region| acc.merge(®ion))
226 }
227}
228
229impl Object {
230 pub fn add_to(self, layer: &mut Layer) {
231 layer.add_anon(self);
232 }
233
234 pub fn set_in(self, layer: &mut Layer, name: impl Display) {
235 layer.set(name, self);
236 }
237}