this repo has no description
3
fork

Configure Feed

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

๐Ÿ“ Bring in Schedule Hell as an example

+258 -2
+1
.gitignore
··· 20 20 !paper/street 21 21 !paper/street/frames 22 22 timings.log 23 + examples/schedule-hell/schedule-hell.mp4
+20
Cargo.lock
··· 2843 2843 ] 2844 2844 2845 2845 [[package]] 2846 + name = "itertools" 2847 + version = "0.14.0" 2848 + source = "registry+https://github.com/rust-lang/crates.io-index" 2849 + checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" 2850 + dependencies = [ 2851 + "either", 2852 + ] 2853 + 2854 + [[package]] 2846 2855 name = "itoa" 2847 2856 version = "1.0.11" 2848 2857 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4298 4307 checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" 4299 4308 dependencies = [ 4300 4309 "windows-sys 0.59.0", 4310 + ] 4311 + 4312 + [[package]] 4313 + name = "schedule-hell" 4314 + version = "0.1.0" 4315 + dependencies = [ 4316 + "anyhow", 4317 + "itertools 0.14.0", 4318 + "pico-args", 4319 + "rand 0.9.0", 4320 + "shapemaker", 4301 4321 ] 4302 4322 4303 4323 [[package]]
+1 -1
Cargo.toml
··· 10 10 description = "An experimental WASM-capable, generative SVG-based video rendering engine that reacts to MIDI or audio data" 11 11 12 12 [workspace] 13 - members = [ "examples/dna-analysis-machine", "examples/specimen","xtask"] 13 + members = [ "examples/dna-analysis-machine", "examples/schedule-hell", "examples/specimen","xtask"] 14 14 15 15 [lib] 16 16 crate-type = ["cdylib", "lib"]
+1 -1
Justfile
··· 27 27 cp shapemaker {{install_at}} 28 28 29 29 example-video out="out.mp4" args='': 30 - RUST_BACKTRACE=full ./shapemaker video --colors examples/colorschemes/palenight.css {{out}} --sync-with examples/schedule-hell.midi --audio examples/schedule-hell.flac --grid-size 16x10 --resolution 480 {{args}} 30 + RUST_BACKTRACE=full ./shapemaker test-video --colors examples/colorschemes/palenight.css {{out}} --sync-with examples/schedule-hell/schedule-hell.midi --audio examples/schedule-hell/schedule-hell.flac --grid-size 16x10 --resolution 480 {{args}} 31 31 32 32 [working-directory: 'paper'] 33 33 paper:
+11
examples/schedule-hell/Cargo.toml
··· 1 + [package] 2 + name = "schedule-hell" 3 + version = "0.1.0" 4 + edition = "2024" 5 + 6 + [dependencies] 7 + anyhow = "1.0.97" 8 + itertools = "0.14.0" 9 + pico-args = { version = "0.5.0", features = ["combined-flags", "eq-separator"] } 10 + rand = "0.9.0" 11 + shapemaker = { path = "../..", features = ["mp4"] }
+224
examples/schedule-hell/src/main.rs
··· 1 + use std::path::PathBuf; 2 + 3 + use anyhow::Result; 4 + use itertools::Itertools; 5 + use shapemaker::{graphics::fill::FillOperations, *}; 6 + 7 + struct State { 8 + bass_pattern_at: Region, 9 + kick_color: Color, 10 + } 11 + 12 + impl Default for State { 13 + fn default() -> Self { 14 + Self { 15 + bass_pattern_at: Region::from_topleft(Point(1, 1), (2, 2)).unwrap(), 16 + kick_color: Color::White, 17 + } 18 + } 19 + } 20 + 21 + pub fn main() -> Result<()> { 22 + let mut canvas = Canvas::new(vec![]); 23 + 24 + canvas.set_grid_size(16, 9); 25 + canvas.colormap = ColorMapping { 26 + black: "#000000".into(), 27 + white: "#ffffff".into(), 28 + red: "#cf0a2b".into(), 29 + green: "#22e753".into(), 30 + blue: "#2734e6".into(), 31 + yellow: "#f8e21e".into(), 32 + orange: "#f05811".into(), 33 + purple: "#6a24ec".into(), 34 + brown: "#a05634".into(), 35 + pink: "#e92e76".into(), 36 + gray: "#81a0a8".into(), 37 + cyan: "#4fecec".into(), 38 + }; 39 + 40 + let mut video = Video::<State>::new(canvas); 41 + let mut args = pico_args::Arguments::from_env(); 42 + 43 + video.duration_override = args 44 + .value_from_str("--duration") 45 + .ok() 46 + .map(|seconds: usize| seconds * 1000); 47 + 48 + if video.duration_override == Some(0) { 49 + video.duration_override = None; 50 + } 51 + 52 + video.start_rendering_at = args 53 + .value_from_str("--start") 54 + .ok() 55 + .map(|seconds: usize| seconds * 1000) 56 + .unwrap_or_default(); 57 + 58 + video.resolution = args.value_from_str("--resolution").ok().unwrap_or(480); 59 + video.fps = args.value_from_str("--fps").ok().unwrap_or(30); 60 + 61 + video.audiofile = PathBuf::from("schedule-hell.flac"); 62 + video = video 63 + .sync_audio_with("schedule-hell.midi") 64 + .init(&|canvas, _| { 65 + canvas.set_background(Color::Black); 66 + 67 + let mut kicks = Layer::new("anchor kick"); 68 + 69 + let circle_at = |x: usize, y: usize| Object::SmallCircle(Point(x, y)); 70 + 71 + let (end_x, end_y) = { 72 + let Point(x, y) = canvas.world_region.end; 73 + (x - 2, y - 2) 74 + }; 75 + kicks.set_object("top left", circle_at(1, 1)); 76 + kicks.set_object("top right", circle_at(end_x, 1)); 77 + kicks.set_object("bottom left", circle_at(1, end_y)); 78 + kicks.set_object("bottom right", circle_at(end_x, end_y)); 79 + canvas.add_or_replace_layer(kicks); 80 + 81 + let mut ch = Layer::new("ch"); 82 + ch.set_object("0", Object::Dot(Point(0, 0))); 83 + canvas.add_or_replace_layer(ch); 84 + 85 + Ok(()) 86 + }) 87 + .on_note("anchor kick", &|canvas, ctx| { 88 + canvas 89 + .layer("anchor kick") 90 + .paint_all_objects(Fill::Translucent(ctx.extra.kick_color, 1.0)); 91 + 92 + ctx.animate_layer("anchor kick", 200, &|t, layer, _| { 93 + layer.objects.values_mut().for_each( 94 + |ColoredObject { fill, .. }| { 95 + *fill = fill.opacify(1.0 - t); 96 + }, 97 + ); 98 + Ok(()) 99 + }); 100 + 101 + Ok(()) 102 + }) 103 + .on_note("bass", &|canvas, ctx| { 104 + let mut new_layer = 105 + canvas.random_layer_within("bass", &ctx.extra.bass_pattern_at); 106 + new_layer.paint_all_objects(Fill::Solid(Color::White)); 107 + canvas.add_or_replace_layer(new_layer); 108 + Ok(()) 109 + }) 110 + .on_note("powerful clap hit, clap, perclap", &|canvas, ctx| { 111 + let mut new_layer = canvas.random_layer_within( 112 + "claps", 113 + &ctx.extra.bass_pattern_at.translated(2, 0), 114 + ); 115 + new_layer.paint_all_objects(Fill::Solid(Color::Red)); 116 + canvas.add_or_replace_layer(new_layer); 117 + Ok(()) 118 + }) 119 + .on_note( 120 + "rimshot, glitchy percs, hitting percs, glitchy percs", 121 + &|canvas, ctx| { 122 + let mut new_layer = canvas.random_layer_within( 123 + "percs", 124 + &ctx.extra.bass_pattern_at.translated(2, 0), 125 + ); 126 + new_layer.paint_all_objects(Fill::Translucent(Color::Red, 0.5)); 127 + canvas.add_or_replace_layer(new_layer); 128 + Ok(()) 129 + }, 130 + ) 131 + .on_note("qanda", &|canvas, ctx| { 132 + let mut new_layer = canvas.random_curves_within( 133 + "qanda", 134 + &ctx.extra.bass_pattern_at.translated(-1, -1).enlarged(1, 1), 135 + 3..=5, 136 + ); 137 + new_layer.paint_all_objects(Fill::Solid(Color::Orange)); 138 + new_layer.object_sizes.default_line_width = 139 + canvas.object_sizes.default_line_width 140 + * 4.0 141 + * ctx.stem("qanda").velocity_relative(); 142 + canvas.add_or_replace_layer(new_layer); 143 + Ok(()) 144 + }) 145 + .on_note("brokenup", &|canvas, ctx| { 146 + let mut new_layer = canvas.random_curves_within( 147 + "brokenup", 148 + &ctx.extra.bass_pattern_at.translated(0, -2), 149 + 3..=5, 150 + ); 151 + new_layer.paint_all_objects(Fill::Solid(Color::Yellow)); 152 + new_layer.object_sizes.default_line_width = 153 + canvas.object_sizes.default_line_width 154 + * 4.0 155 + * ctx.stem("brokenup").velocity_relative(); 156 + canvas.add_or_replace_layer(new_layer); 157 + Ok(()) 158 + }) 159 + .on_note("goup", &|canvas, ctx| { 160 + let mut new_layer = canvas.random_curves_within( 161 + "goup", 162 + &ctx.extra.bass_pattern_at.translated(0, 2), 163 + 3..=5, 164 + ); 165 + new_layer.paint_all_objects(Fill::Solid(Color::Green)); 166 + new_layer.object_sizes.default_line_width = 167 + canvas.object_sizes.default_line_width 168 + * 4.0 169 + * ctx.stem("goup").velocity_relative(); 170 + canvas.add_or_replace_layer(new_layer); 171 + Ok(()) 172 + }) 173 + .on_note("ch", &|canvas, ctx| { 174 + let world = canvas.world_region.clone(); 175 + 176 + // keep only the last 2 dots 177 + let dots_to_keep = canvas 178 + .layer("ch") 179 + .objects 180 + .iter() 181 + .sorted_by_key(|(name, _)| name.parse::<usize>().unwrap()) 182 + .rev() 183 + .take(2) 184 + .map(|(name, _)| (name.clone())) 185 + .collect::<Vec<_>>(); 186 + 187 + let layer = canvas.layer("ch"); 188 + layer.object_sizes.empty_shape_stroke_width = 2.0; 189 + layer.objects.retain(|name, _| dots_to_keep.contains(name)); 190 + 191 + let object_name = format!("{}", ctx.ms); 192 + layer.set_object( 193 + &object_name, 194 + Object::Dot(world.resized(-1, -1).random_point()) 195 + .colored(Color::Cyan), 196 + ); 197 + 198 + canvas.put_layer_on_top("ch"); 199 + canvas.layer("ch").flush(); 200 + Ok(()) 201 + }) 202 + .when_remaining(10, &|canvas, _| { 203 + let world = canvas.world_region; 204 + canvas.root().set_object( 205 + "credits text", 206 + Object::Text( 207 + world.start.translated(2, 2), 208 + "by ewen-lbh".into(), 209 + 12.0, 210 + ) 211 + .colored(Color::White), 212 + ); 213 + Ok(()) 214 + }) 215 + .command("remove", &|argumentsline, canvas, _| { 216 + let args = argumentsline.splitn(3, ' ').collect::<Vec<_>>(); 217 + canvas.remove_object(args[0]); 218 + Ok(()) 219 + }); 220 + 221 + video.render("schedule-hell.mp4".into())?; 222 + 223 + Ok(()) 224 + }