This repository has no description
1use anyhow::{Result, anyhow};
2use core::panic;
3use resvg::usvg;
4use std::{collections::HashMap, ops::Range, sync::Arc};
5
6use itertools::Itertools as _;
7use measure_time::debug_time;
8
9use crate::{
10 Color, ColorMapping, Fill, Filter, Layer, ObjectSizes, Point, Region, Shape,
11 fonts::{FontOptions, load_fonts},
12};
13
14use super::Object;
15
16#[derive(Debug, Clone)]
17pub struct Canvas {
18 pub grid_size: (usize, usize),
19 pub cell_size: usize,
20 pub objects_count_range: Range<usize>,
21 pub polygon_vertices_range: Range<usize>,
22 pub outer_padding: usize,
23 pub object_sizes: ObjectSizes,
24 pub font_options: FontOptions,
25 pub colormap: ColorMapping,
26 pub name: String,
27
28 /// The layers are in order of top to bottom: the first layer will be rendered on top of the second, etc.
29 pub layers: Vec<Layer>,
30 pub background: Option<Color>,
31
32 pub world_region: Region,
33
34 /// Render cache for the SVG string. Prevents having to re-calculate a pixmap when the SVG hasn't changed.
35 pub(crate) fontdb: Option<Arc<usvg::fontdb::Database>>,
36}
37
38impl Canvas {
39 pub fn new(width: usize, height: usize) -> Self {
40 let mut canvas = Self::with_layers(vec![]);
41 canvas.set_grid_size(width, height);
42 canvas
43 }
44
45 pub fn default_settings() -> Self {
46 Self {
47 name: String::new(),
48 grid_size: (3, 3),
49 cell_size: 50,
50 objects_count_range: 3..7,
51 polygon_vertices_range: 2..7,
52 outer_padding: 10,
53 object_sizes: ObjectSizes::default(),
54 font_options: FontOptions::default(),
55 colormap: ColorMapping::default(),
56 layers: vec![Layer::new("root")],
57 world_region: Region::new((0, 0), (3, 3)).unwrap(),
58 background: None,
59 fontdb: None,
60 }
61 }
62
63 /// Create a new canvas.
64 /// The layers are in order of top to bottom: the first layer will be rendered on top of the second, etc.
65 /// A layer named "root" will be added below all layers if you don't add it yourself.
66 pub fn with_layers(layer_names: Vec<&str>) -> Self {
67 let mut canvas = Self::default_settings();
68 canvas.load_fonts().unwrap();
69 canvas.init_layers(layer_names);
70 canvas
71 }
72
73 pub fn with_colors(colormap: ColorMapping) -> Self {
74 Self {
75 colormap,
76 ..Self::default_settings()
77 }
78 }
79
80 pub fn init_layers(&mut self, names: Vec<&str>) {
81 self.layers = names
82 .iter()
83 .map(|name| Layer {
84 object_sizes: ObjectSizes::default(),
85 objects: HashMap::new(),
86 name: name.to_string(),
87 hidden: false,
88 })
89 .collect();
90 if !self.layer_exists("root") {
91 self.layers.push(Layer::new("root"));
92 }
93 }
94
95 pub fn set_grid_size(&mut self, new_width: usize, new_height: usize) {
96 self.grid_size = (new_width, new_height);
97 self.world_region = Region {
98 start: Point::Corner(0, 0),
99 end: Point::from(self.grid_size).translated(-1, -1),
100 };
101 }
102
103 pub fn layer(&mut self, name: &str) -> Result<&mut Layer> {
104 self.layers
105 .iter_mut()
106 .find(|layer| layer.name == name)
107 .ok_or(anyhow!("Layer {name} does not exist"))
108 }
109
110 pub fn layer_unchecked(&mut self, name: &str) -> &mut Layer {
111 self.layer(name).unwrap()
112 }
113
114 pub fn new_layer(&mut self, name: &str) -> &mut Layer {
115 if self.layer_exists(name) {
116 panic!("Layer {} already exists", name);
117 }
118
119 let mut layer = Layer::new(name);
120 layer.object_sizes = self.object_sizes;
121 self.layers.push(layer);
122 self.layers.last_mut().unwrap()
123 }
124
125 pub fn add_layer(&mut self, layer: Layer) -> &mut Layer {
126 if self.layer_exists(&layer.name) {
127 panic!("Layer {} already exists", layer.name);
128 }
129
130 self.layers.push(layer);
131 self.layers.last_mut().unwrap()
132 }
133
134 pub fn layer_or_empty(&mut self, name: &str) -> &mut Layer {
135 if self.layer_exists(name) {
136 return self.layer_unchecked(name);
137 }
138
139 self.new_layer(name)
140 }
141
142 pub fn layer_exists(&self, name: &str) -> bool {
143 self.layers.iter().any(|layer| layer.name == name)
144 }
145
146 pub fn ensure_layer_exists(&self, name: &str) {
147 if !self.layer_exists(name) {
148 panic!("Layer {} does not exist", name);
149 }
150 }
151
152 /// puts this layer on top, and the others below, without changing their order
153 pub fn put_layer_on_top(&mut self, name: &str) {
154 self.ensure_layer_exists(name);
155 let target_index =
156 self.layers.iter().position(|l| l.name == name).unwrap();
157 self.layers.swap(0, target_index)
158 }
159
160 /// puts this layer on bottom, and the others above, without changing their order
161 pub fn put_layer_on_bottom(&mut self, name: &str) {
162 self.ensure_layer_exists(name);
163 let target_index =
164 self.layers.iter().position(|l| l.name == name).unwrap();
165 let last_index = self.layers.len() - 1;
166 self.layers.swap(last_index, target_index)
167 }
168
169 /// re-order layers. The first layer in the list will be on top, the last at the bottom
170 pub fn reorder_layers(&mut self, new_order: Vec<&str>) {
171 let current_order = self
172 .layers
173 .iter()
174 .map(|l| l.name.clone())
175 .collect::<Vec<_>>();
176
177 // make sure the new order is well-formed
178 // assert_eq!(self.layers.len(), new_order.len());
179 assert!(new_order.iter().all(|name| self.layer_exists(name)));
180
181 self.layers.sort_by_key(|o| {
182 new_order.iter().position(|n| *n == o.name).unwrap_or(
183 current_order.iter().position(|n| *n == o.name).unwrap(),
184 )
185 });
186 }
187
188 pub fn remove_layer(&mut self, name: &str) {
189 self.layers.retain(|layer| layer.name != name);
190 }
191
192 pub fn root(&mut self) -> &mut Layer {
193 self.layer("root")
194 .expect("Layer 'root' should always exist in a canvas")
195 }
196
197 pub fn add_object(
198 &mut self,
199 layer: &str,
200 name: &str,
201 shape: Shape,
202 fill: Option<Fill>,
203 ) -> Result<()> {
204 self.layer(layer)?.set(name, Object::from((shape, fill)));
205
206 Ok(())
207 }
208
209 pub fn remove_object(&mut self, name: &str) {
210 for layer in self.layers.iter_mut() {
211 layer.remove_object(name);
212 }
213 }
214
215 pub fn set_background(&mut self, color: Color) {
216 self.background = Some(color);
217 }
218
219 pub fn remove_background(&mut self) {
220 self.background = None;
221 }
222
223 pub fn fonts_loaded(&self) -> bool {
224 self.fontdb.is_some()
225 }
226
227 pub fn load_fonts(&mut self) -> Result<()> {
228 if self.fonts_loaded() {
229 return Ok(());
230 }
231
232 debug_time!("load_fonts");
233 let usvg = load_fonts(&self.font_options)?;
234 self.fontdb = Some(usvg.fontdb);
235 Ok(())
236 }
237
238 pub fn add_or_replace_layer(&mut self, layer: Layer) {
239 if let Ok(existing_layer) = self.layer(&layer.name) {
240 existing_layer.replace(layer);
241 } else {
242 self.layers.push(layer);
243 }
244 }
245
246 pub fn region_is_whole_grid(&self, region: &Region) -> bool {
247 region.start == (0, 0) && region.end == self.grid_size
248 }
249 pub fn clear(&mut self) {
250 self.layers.clear();
251 self.remove_background();
252 self.layers.push(Layer::new("root"));
253 }
254
255 pub fn resolution_to_size_even(&self, resolution: u32) -> (u32, u32) {
256 let (width, height) = self.resolution_to_size(resolution);
257 (width + width % 2, height + height % 2)
258 }
259
260 pub fn resolution_to_size(&self, resolution: u32) -> (u32, u32) {
261 let aspect_ratio = self.aspect_ratio();
262 if aspect_ratio > 1.0 {
263 // landscape: resolution is width
264 (resolution, (resolution as f32 / aspect_ratio) as u32)
265 } else {
266 // portrait: resolution is height
267 ((resolution as f32 * aspect_ratio) as u32, resolution)
268 }
269 }
270
271 pub fn width(&self) -> usize {
272 self.cell_size * self.world_region.width() + 2 * self.outer_padding
273 }
274
275 pub fn height(&self) -> usize {
276 self.cell_size * self.world_region.height() + 2 * self.outer_padding
277 }
278
279 pub fn aspect_ratio(&self) -> f32 {
280 self.width() as f32 / self.height() as f32
281 }
282
283 pub fn remove_all_objects_in(&mut self, region: &Region) {
284 self.layers
285 .iter_mut()
286 .for_each(|layer| layer.remove_all_objects_in(region));
287 }
288
289 /// returns a list of all unique filters used throughout the canvas
290 /// used to only generate one definition per filter
291 ///
292 pub fn unique_filters(&self) -> Vec<Filter> {
293 let mut filters: Vec<Filter> = self
294 .layers
295 .iter()
296 .flat_map(|layer| {
297 layer.objects.iter().flat_map(|(_, o)| o.filters.clone())
298 })
299 .unique()
300 .collect();
301 filters.sort_by_key(|f| format!("{:?}", f));
302 filters
303 }
304
305 pub fn unique_pattern_fills(&self) -> Vec<Fill> {
306 let mut fills: Vec<Fill> = self
307 .layers
308 .iter()
309 .flat_map(|layer| layer.objects.iter().flat_map(|(_, o)| o.fill))
310 .filter(|fill| matches!(fill, Fill::Hatches(..) | Fill::Dotted(..)))
311 .unique_by(|fill| fill.pattern_id())
312 .collect();
313 fills.sort_by_key(|f| f.pattern_id());
314 fills
315 }
316
317 pub fn debug_region(&mut self, region: &Region, color: Color) {
318 let layer = self.layer_or_empty("debug plane");
319
320 layer.set(
321 format!("{}_corner_sw", region).as_str(),
322 Shape::Dot(region.topleft()).colored(color),
323 );
324 layer.set(
325 format!("{}_corner_se", region).as_str(),
326 Shape::Dot(region.topright().translated(1, 0)).colored(color),
327 );
328 layer.set(
329 format!("{}_corner_ne", region).as_str(),
330 Shape::Dot(region.bottomright().translated(1, 1)).colored(color),
331 );
332 layer.set(
333 format!("{}_corner_nw", region).as_str(),
334 Shape::Dot(region.bottomleft().translated(0, 1)).colored(color),
335 );
336 layer.set(
337 format!("{}_region", region).as_str(),
338 Shape::Rectangle(region.start, region.end)
339 .filled(Fill::Translucent(color, 0.25)),
340 )
341 }
342
343 pub fn debug_grid(&mut self, color: Color) {
344 let world = self.world_region.clone();
345 let layer = self.layer_or_empty("debug_plane");
346
347 let ymax = world.end.x() + 1;
348 let xmax = world.end.y() + 1;
349
350 // Vertical lines
351 for point in world.iter() {
352 let (x, y) = point.xy();
353
354 layer.set(
355 format!("grid_vertical_{x}"),
356 Shape::Line(Point::Corner(x, 0), Point::Corner(x, ymax), 1.0)
357 .colored(color)
358 .opacified(0.25),
359 );
360
361 layer.set(
362 format!("grid_horizontal_{y}"),
363 Shape::Line(Point::Corner(0, y), Point::Corner(xmax, y), 1.0)
364 .colored(color)
365 .opacified(0.25),
366 );
367 }
368 }
369
370 pub fn dimensions<T: From<usize>>(&self) -> (T, T) {
371 (self.width().into(), self.height().into())
372 }
373}