this repo has no description
3
fork

Configure Feed

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

:eyes:

authored by

Gwenn Le Bihan and committed by
Ewen Le Bihan
dc6c78bc 7a0325a6

+105 -32
+1
.gitignore
··· 16 16 preview.html 17 17 out.png 18 18 stems_data/ 19 + street
+1 -1
Justfile
··· 18 18 ./shapemaker video --colors colorschemes/palenight.css {{out}} --sync-with fixtures/schedule-hell.midi --audio fixtures/schedule-hell.flac --grid-size 16x10 --resolution 1920 {{args}} 19 19 20 20 example-image out="out.png" args='': 21 - ./shapemaker image --colors colorschemes/palenight.css --resolution 3000 {{out}} {{args}} 21 + ./shapemaker image --colors colorschemes/snazzy-light.json --resolution 3000 {{out}} {{args}}
+14 -11
src/canvas.rs
··· 219 219 let hatchable = object.hatchable(); 220 220 objects.insert( 221 221 format!("{}#{}", name, i), 222 - ColoredObject::from(( 223 - object, 224 - if rand::thread_rng().gen_bool(0.5) { 225 - Some(self.random_fill(hatchable)) 226 - } else { 227 - None 228 - }, 229 - )), 222 + object.color(self.random_fill(hatchable)), 230 223 ); 231 224 } 232 225 Layer { ··· 380 373 region.start == (0, 0) && region.end == self.grid_size 381 374 } 382 375 376 + pub fn random_region(&self) -> Region { 377 + let start = self.random_point(&self.world_region); 378 + let end = self.random_end_anchor(start, &self.world_region); 379 + Region::from(if start.0 > end.0 { 380 + (end, start) 381 + } else { 382 + (start, end) 383 + }) 384 + } 385 + 383 386 pub fn random_point(&self, region: &Region) -> Point { 384 387 region.ensure_nonempty().unwrap(); 385 388 Point( ··· 391 394 pub fn random_fill(&self, hatchable: bool) -> Fill { 392 395 if hatchable { 393 396 match rand::thread_rng().gen_range(1..=2) { 394 - 1 => Fill::Solid(random_color()), 397 + 1 => Fill::Solid(random_color(self.background)), 395 398 2 => { 396 399 let hatch_size = rand::thread_rng().gen_range(5..=100) as f32 * 1e-2; 397 400 Fill::Hatched( 398 - random_color(), 401 + random_color(self.background), 399 402 Angle(rand::thread_rng().gen_range(0.0..360.0)), 400 403 hatch_size, 401 404 // under a certain hatch size, we can't see the hatching if the ratio is not ½ ··· 409 412 _ => unreachable!(), 410 413 } 411 414 } else { 412 - Fill::Solid(random_color()) 415 + Fill::Solid(random_color(self.background)) 413 416 } 414 417 } 415 418
+24 -16
src/color.rs
··· 27 27 } 28 28 29 29 #[wasm_bindgen] 30 - pub fn random_color() -> Color { 31 - match rand::thread_rng().gen_range(1..=12) { 32 - 1 => Color::Black, 33 - 2 => Color::White, 34 - 3 => Color::Red, 35 - 4 => Color::Green, 36 - 5 => Color::Blue, 37 - 6 => Color::Yellow, 38 - 7 => Color::Orange, 39 - 8 => Color::Purple, 40 - 9 => Color::Brown, 41 - 10 => Color::Pink, 42 - 11 => Color::Gray, 43 - 12 => Color::Cyan, 44 - _ => unreachable!(), 45 - } 30 + pub fn random_color(except: Option<Color>) -> Color { 31 + let all = [ 32 + Color::Black, 33 + Color::White, 34 + Color::Red, 35 + Color::Green, 36 + Color::Blue, 37 + Color::Yellow, 38 + Color::Orange, 39 + Color::Purple, 40 + Color::Brown, 41 + Color::Cyan, 42 + Color::Pink, 43 + Color::Gray, 44 + ]; 45 + let candidates = all 46 + .iter() 47 + .filter(|c| match except { 48 + None => true, 49 + Some(color) => &&color != c, 50 + }) 51 + .collect::<Vec<_>>(); 52 + 53 + *candidates[rand::thread_rng().gen_range(0..candidates.len())] 46 54 } 47 55 48 56 impl Default for Color {
+29 -1
src/main.rs
··· 1 + use std::env; 2 + 1 3 use anyhow::Result; 4 + use itertools::Itertools; 5 + use rand::Rng; 2 6 use shapemaker::{ 3 7 cli::{canvas_from_cli, cli_args}, 4 8 *, ··· 11 15 pub fn run(args: cli::Args) -> Result<()> { 12 16 let mut canvas = canvas_from_cli(&args); 13 17 18 + let qrname = env::var("QRCODE_NAME").unwrap(); 19 + 14 20 if args.cmd_image && !args.cmd_video { 15 - canvas = examples::dna_analysis_machine(); 21 + canvas.set_grid_size(3, 3); 22 + canvas.add_or_replace_layer(canvas.random_layer("root")); 23 + canvas.new_layer("qr"); 24 + let qrcode = Object::Image( 25 + vec![ 26 + canvas.world_region.topleft(), 27 + canvas.world_region.topright(), 28 + canvas.world_region.bottomright(), 29 + canvas.world_region.bottomleft(), 30 + ][rand::thread_rng().gen_range(0..4)] 31 + .region(), 32 + format!("./{qrname}-qr.png"), 33 + ); 34 + canvas.root().remove_all_objects_in(&qrcode.region()); 35 + canvas.set_background(Color::White); 36 + canvas.add_object("qr", "qr", qrcode, None).unwrap(); 37 + canvas.put_layer_on_top("qr"); 38 + canvas.root().objects.values_mut().for_each(|o| { 39 + if !o.object.fillable() { 40 + o.fill = Some(Fill::Solid(Color::Black)); 41 + } 42 + }); 43 + 16 44 let rendered = canvas.render(true)?; 17 45 if args.arg_file.ends_with(".svg") { 18 46 std::fs::write(args.arg_file, rendered).unwrap();
+21 -1
src/objects.rs
··· 1 - use std::collections::HashMap; 1 + use std::{cell, collections::HashMap}; 2 2 3 3 use crate::{ColorMapping, Fill, Filter, Point, Region, Transformation}; 4 4 use itertools::Itertools; ··· 24 24 CenteredText(Point, String, f32), 25 25 // FittedText(Region, String), 26 26 Rectangle(Point, Point), 27 + Image(Region, String), 27 28 RawSVG(Box<dyn svg::Node>), 28 29 } 29 30 ··· 252 253 | Object::Dot(anchor) 253 254 | Object::SmallCircle(anchor) => anchor.translate(dx, dy), 254 255 Object::BigCircle(center) => center.translate(dx, dy), 256 + Object::Image(region, ..) => region.translate(dx, dy), 255 257 Object::RawSVG(_) => { 256 258 unimplemented!() 257 259 } ··· 297 299 | Object::Dot(anchor) 298 300 | Object::SmallCircle(anchor) => anchor.region(), 299 301 Object::BigCircle(center) => center.region(), 302 + Object::Image(region, ..) => *region, 300 303 Object::RawSVG(_) => { 301 304 unimplemented!() 302 305 } ··· 333 336 Object::SmallCircle(..) => self.render_small_circle(cell_size, object_sizes), 334 337 Object::Dot(..) => self.render_dot(cell_size, object_sizes), 335 338 Object::BigCircle(..) => self.render_big_circle(cell_size), 339 + Object::Image(..) => self.render_image(cell_size), 336 340 Object::RawSVG(..) => self.render_raw_svg(), 337 341 }; 338 342 339 343 group.set("data-object", id).add(rendered) 344 + } 345 + 346 + fn render_image(&self, cell_size: usize) -> Box<dyn svg::node::Node> { 347 + if let Object::Image(region, path) = self { 348 + let (x, y) = region.start.coords(cell_size); 349 + return Box::new( 350 + svg::node::element::Image::new() 351 + .set("x", x) 352 + .set("y", y) 353 + .set("width", region.width() * cell_size) 354 + .set("height", region.height() * cell_size) 355 + .set("href", path.clone()), 356 + ); 357 + } 358 + 359 + panic!("Expected Image, got {:?}", self); 340 360 } 341 361 342 362 fn render_raw_svg(&self) -> Box<dyn svg::node::Node> {
+2 -2
src/region.rs
··· 208 208 209 209 // panics if the region is invalid 210 210 pub fn ensure_valid(self) -> Result<Self> { 211 - if self.start.0 >= self.end.0 || self.start.1 >= self.end.1 { 211 + if self.start.0 > self.end.0 || self.start.1 > self.end.1 { 212 212 return Err(format_err!( 213 - "Invalid region: start ({:?}) >= end ({:?})", 213 + "Invalid region: start ({:?}) > end ({:?})", 214 214 self.start, 215 215 self.end 216 216 ));
+13
street.fish
··· 1 + while true 2 + set id (nanoid -s 10) 3 + qrencode "https://shapemaker.ewen.works/found/$id" -o street/$id-qr.png 4 + QRCODE_NAME=street/$id just example-image out.svg "--objects-count 5..15" 5 + if test (read || echo "n") = "y" 6 + cp out.svg street/$id.svg 7 + resvg --width 2000 out.svg street/$id.png 8 + echo resvg --width 2000 street/$id.svg street/$id.png 9 + echo saved street/$id.svg \| street/$id.png 10 + else 11 + rm street/$id-qr.png 12 + end 13 + end