This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

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}