This repository has no description
0

Configure Feed

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

🎨 Format code

+480 -345
+4 -2
examples/dna-analysis-machine/src/main.rs
··· 1 - use shapemaker::*; 2 1 use rand; 2 + use shapemaker::*; 3 3 4 4 pub fn main() { 5 5 let mut canvas = Canvas::with_colors(ColorMapping { ··· 68 68 .add_to(hatches); 69 69 } 70 70 71 - canvas.render_to_png("dna-analysis-machine.png", 480).unwrap(); 71 + canvas 72 + .render_to_png("dna-analysis-machine.png", 480) 73 + .unwrap(); 72 74 }
+3 -12
src/geometry/region.rs
··· 188 188 self.start.0.min(other.start.0), 189 189 self.start.1.min(other.start.1), 190 190 ), 191 - end: Point( 192 - self.end.0.max(other.end.0), 193 - self.end.1.max(other.end.1), 194 - ), 191 + end: Point(self.end.0.max(other.end.0), self.end.1.max(other.end.1)), 195 192 } 196 193 } 197 194 ··· 206 203 ) 207 204 } 208 205 209 - pub fn from_bottomleft( 210 - origin: Point, 211 - size: (usize, usize), 212 - ) -> Result<Self> { 206 + pub fn from_bottomleft(origin: Point, size: (usize, usize)) -> Result<Self> { 213 207 Self::from_topleft(origin.translated(0, -(size.1 as i32 - 1)), size) 214 208 } 215 209 216 - pub fn from_bottomright( 217 - origin: Point, 218 - size: (usize, usize), 219 - ) -> Result<Self> { 210 + pub fn from_bottomright(origin: Point, size: (usize, usize)) -> Result<Self> { 220 211 Self::from_points( 221 212 origin.translated_by(Point::from(size).translated(-1, -1)), 222 213 origin,
+4 -2
src/graphics/color.rs
··· 134 134 } 135 135 136 136 pub fn from_json(content: &str) -> ColorMapping { 137 - let json: HashMap<String, String> = serde_json::from_str(content).unwrap(); 137 + let json: HashMap<String, String> = 138 + serde_json::from_str(content).unwrap(); 138 139 ColorMapping::from_hashmap(json) 139 140 } 140 141 ··· 230 231 pub fn from_json_file(path: PathBuf) -> ColorMapping { 231 232 let file = File::open(path).unwrap(); 232 233 let reader = BufReader::new(file); 233 - let json: HashMap<String, String> = serde_json::from_reader(reader).unwrap(); 234 + let json: HashMap<String, String> = 235 + serde_json::from_reader(reader).unwrap(); 234 236 ColorMapping::from_hashmap(json) 235 237 } 236 238
+11 -2
src/graphics/fill.rs
··· 51 51 ); 52 52 } 53 53 if let Fill::Dotted(color, diameter, spacing) = self { 54 - return format!("pattern-dotted-{}-{}-{}", color.name(), diameter, spacing); 54 + return format!( 55 + "pattern-dotted-{}-{}-{}", 56 + color.name(), 57 + diameter, 58 + spacing 59 + ); 55 60 } 56 61 String::from("") 57 62 } ··· 79 84 svg::node::element::Polygon::new() 80 85 .set( 81 86 "points", 82 - format!("0,0 {},0 0,{}", thickness / 2.0, thickness / 2.0), 87 + format!( 88 + "0,0 {},0 0,{}", 89 + thickness / 2.0, 90 + thickness / 2.0 91 + ), 83 92 ) 84 93 .set("fill", color.render(colormapping)), 85 94 )
+2 -1
src/graphics/filter.rs
··· 48 48 impl PartialEq for Filter { 49 49 fn eq(&self, other: &Self) -> bool { 50 50 // TODO use way less restrictive epsilon 51 - self.kind == other.kind && (self.parameter - other.parameter).abs() < f32::EPSILON 51 + self.kind == other.kind 52 + && (self.parameter - other.parameter).abs() < f32::EPSILON 52 53 } 53 54 } 54 55
+5 -3
src/graphics/layer.rs
··· 79 79 } 80 80 81 81 pub fn move_all_objects(&mut self, dx: i32, dy: i32) { 82 - self.objects.iter_mut().for_each( 83 - |(_, ColoredObject { object, .. })| object.translate(dx, dy), 84 - ); 82 + self.objects 83 + .iter_mut() 84 + .for_each(|(_, ColoredObject { object, .. })| { 85 + object.translate(dx, dy) 86 + }); 85 87 self.flush(); 86 88 } 87 89
+23 -5
src/random/objects.rs
··· 16 16 4 => Self::Dot(start), 17 17 5 => Self::CurveInward(start, region.random_end(start), line_width), 18 18 6 => Self::CurveOutward(start, region.random_end(start), line_width), 19 - 7 => Self::Line(Point::random(region), Point::random(region), line_width), 19 + 7 => Self::Line( 20 + Point::random(region), 21 + Point::random(region), 22 + line_width, 23 + ), 20 24 _ => unreachable!(), 21 25 } 22 26 } 23 27 24 - pub fn random_polygon(region: &Region, vertices_range: impl SampleRange<usize>) -> Object { 28 + pub fn random_polygon( 29 + region: &Region, 30 + vertices_range: impl SampleRange<usize>, 31 + ) -> Object { 25 32 let number_of_anchors = rand::thread_rng().gen_range(vertices_range); 26 33 let start = Point::random(region); 27 34 let mut lines: Vec<LineSegment> = vec![]; ··· 47 54 polygon_vertices_range: impl SampleRange<usize>, 48 55 ) -> Object { 49 56 let start = Point::random(region); 50 - Object::random_starting_at(start, region, line_width, polygon_vertices_range) 57 + Object::random_starting_at( 58 + start, 59 + region, 60 + line_width, 61 + polygon_vertices_range, 62 + ) 51 63 } 52 64 53 65 pub fn random_curve_within(region: &Region, line_width: f32) -> Object { 54 66 let start = region.random_point(); 55 67 match rand::thread_rng().gen_range(1..=3) { 56 68 1 => Object::CurveInward(start, region.random_end(start), line_width), 57 - 2 => Object::CurveOutward(start, region.random_end(start), line_width), 58 - 3 => Object::Line(Point::random(region), Point::random(region), line_width), 69 + 2 => { 70 + Object::CurveOutward(start, region.random_end(start), line_width) 71 + } 72 + 3 => Object::Line( 73 + Point::random(region), 74 + Point::random(region), 75 + line_width, 76 + ), 59 77 _ => unreachable!(), 60 78 } 61 79 }
+2 -1
src/random/region.rs
··· 29 29 } 30 30 31 31 // Pick a random end anchor from the possible end anchors 32 - possible_end_anchors[rand::thread_rng().gen_range(0..possible_end_anchors.len())] 32 + possible_end_anchors 33 + [rand::thread_rng().gen_range(0..possible_end_anchors.len())] 33 34 } 34 35 35 36 pub fn random(within: &Region) -> Self {
+53 -45
src/rendering/fonts.rs
··· 1 - use std::path::PathBuf; 2 - 3 - use resvg::usvg; 4 - 5 - #[derive(Default, Debug, Clone)] 6 - pub struct FontOptions { 7 - skip_system_fonts: bool, 8 - font_files: Vec<PathBuf>, 9 - font_dirs: Vec<PathBuf>, 10 - serif_family: Option<String>, 11 - sans_serif_family: Option<String>, 12 - cursive_family: Option<String>, 13 - fantasy_family: Option<String>, 14 - monospace_family: Option<String>, 15 - } 16 - 17 - pub fn load_fonts(args: &FontOptions) -> anyhow::Result<usvg::Options> { 18 - let mut usvg = usvg::Options { 19 - font_family: args.sans_serif_family.clone().unwrap_or("Arial".into()), 20 - ..Default::default() 21 - }; 22 - let fontdb = usvg.fontdb_mut(); 23 - 24 - if !args.skip_system_fonts { 25 - fontdb.load_system_fonts(); 26 - } 27 - 28 - for path in &args.font_files { 29 - if let Err(e) = fontdb.load_font_file(path) { 30 - log::warn!("Failed to load '{}' cause {}.", path.display(), e); 31 - } 32 - } 33 - 34 - for path in &args.font_dirs { 35 - fontdb.load_fonts_dir(path); 36 - } 37 - 38 - fontdb.set_serif_family(args.serif_family.as_deref().unwrap_or("Times New Roman")); 39 - fontdb.set_sans_serif_family(args.sans_serif_family.as_deref().unwrap_or("Arial")); 40 - fontdb.set_cursive_family(args.cursive_family.as_deref().unwrap_or("Comic Sans MS")); 41 - fontdb.set_fantasy_family(args.fantasy_family.as_deref().unwrap_or("Impact")); 42 - fontdb.set_monospace_family(args.monospace_family.as_deref().unwrap_or("Courier New")); 43 - 44 - Ok(usvg) 45 - } 1 + use std::path::PathBuf; 2 + 3 + use resvg::usvg; 4 + 5 + #[derive(Default, Debug, Clone)] 6 + pub struct FontOptions { 7 + skip_system_fonts: bool, 8 + font_files: Vec<PathBuf>, 9 + font_dirs: Vec<PathBuf>, 10 + serif_family: Option<String>, 11 + sans_serif_family: Option<String>, 12 + cursive_family: Option<String>, 13 + fantasy_family: Option<String>, 14 + monospace_family: Option<String>, 15 + } 16 + 17 + pub fn load_fonts(args: &FontOptions) -> anyhow::Result<usvg::Options> { 18 + let mut usvg = usvg::Options { 19 + font_family: args.sans_serif_family.clone().unwrap_or("Arial".into()), 20 + ..Default::default() 21 + }; 22 + let fontdb = usvg.fontdb_mut(); 23 + 24 + if !args.skip_system_fonts { 25 + fontdb.load_system_fonts(); 26 + } 27 + 28 + for path in &args.font_files { 29 + if let Err(e) = fontdb.load_font_file(path) { 30 + log::warn!("Failed to load '{}' cause {}.", path.display(), e); 31 + } 32 + } 33 + 34 + for path in &args.font_dirs { 35 + fontdb.load_fonts_dir(path); 36 + } 37 + 38 + fontdb.set_serif_family( 39 + args.serif_family.as_deref().unwrap_or("Times New Roman"), 40 + ); 41 + fontdb.set_sans_serif_family( 42 + args.sans_serif_family.as_deref().unwrap_or("Arial"), 43 + ); 44 + fontdb.set_cursive_family( 45 + args.cursive_family.as_deref().unwrap_or("Comic Sans MS"), 46 + ); 47 + fontdb.set_fantasy_family(args.fantasy_family.as_deref().unwrap_or("Impact")); 48 + fontdb.set_monospace_family( 49 + args.monospace_family.as_deref().unwrap_or("Courier New"), 50 + ); 51 + 52 + Ok(usvg) 53 + }
+1 -1
src/rendering/layer.rs
··· 1 - use crate::Layer; 2 1 use super::renderable::SVGRenderable; 2 + use crate::Layer; 3 3 4 4 impl SVGRenderable for Layer { 5 5 fn render_to_svg(
+2 -7
src/rendering/objects.rs
··· 5 5 ColoredObject, Object, 6 6 }; 7 7 8 - use super::{ 9 - renderable::SVGRenderable, CSSRenderable, SVGAttributesRenderable, 10 - }; 8 + use super::{renderable::SVGRenderable, CSSRenderable, SVGAttributesRenderable}; 11 9 12 10 impl SVGRenderable for ColoredObject { 13 11 fn render_to_svg( ··· 308 306 "d", 309 307 svg::node::element::path::Data::new() 310 308 .move_to(start.coords(cell_size)) 311 - .quadratic_curve_to(( 312 - control, 313 - end.coords(cell_size), 314 - )), 309 + .quadratic_curve_to((control, end.coords(cell_size))), 315 310 ) 316 311 .set("stroke-width", format!("{stroke_width}")), 317 312 );
+18 -4
src/rendering/renderable.rs
··· 32 32 pub trait CSSRenderable { 33 33 fn render_to_css_filled(&self, colormap: &ColorMapping) -> String; 34 34 fn render_to_css_stroked(&self, colormap: &ColorMapping) -> String; 35 - fn render_to_css(&self, colormap: &ColorMapping, fill_as_stroke_color: bool) -> String { 35 + fn render_to_css( 36 + &self, 37 + colormap: &ColorMapping, 38 + fill_as_stroke_color: bool, 39 + ) -> String { 36 40 if fill_as_stroke_color { 37 41 self.render_to_css_stroked(colormap) 38 42 } else { ··· 76 80 ) -> Result<HashMap<String, String>> { 77 81 let mut attrs = HashMap::<String, String>::new(); 78 82 for attrmap in self.clone().into_iter().map(|v| { 79 - v.render_to_svg_attributes(colormap.clone(), cell_size, object_sizes, id) 80 - .unwrap() 83 + v.render_to_svg_attributes( 84 + colormap.clone(), 85 + cell_size, 86 + object_sizes, 87 + id, 88 + ) 89 + .unwrap() 81 90 }) { 82 91 for (key, value) in attrmap { 83 92 if attrs.contains_key(&key) { 84 93 attrs.insert( 85 94 key.clone(), 86 - format!("{}{}{}", attrs[&key], T::MULTIPLE_VALUES_JOIN_BY, value), 95 + format!( 96 + "{}{}{}", 97 + attrs[&key], 98 + T::MULTIPLE_VALUES_JOIN_BY, 99 + value 100 + ), 87 101 ); 88 102 } else { 89 103 attrs.insert(key, value);
+3 -1
src/rendering/transform.rs
··· 17 17 match self { 18 18 Transformation::Scale(x, y) => format!("scale({} {})", x, y), 19 19 Transformation::Rotate(angle) => format!("rotate({})", angle), 20 - Transformation::Skew(x, y) => format!("skewX({}) skewY({})", x, y), 20 + Transformation::Skew(x, y) => { 21 + format!("skewX({}) skewY({})", x, y) 22 + } 21 23 Transformation::Matrix(a, b, c, d, e, f) => { 22 24 format!("matrix({}, {}, {}, {}, {}, {})", a, b, c, d, e, f) 23 25 }
+77 -56
src/synchronization/midi.rs
··· 29 29 } 30 30 31 31 fn load(&self, progressbar: Option<&ProgressBar>) -> SyncData { 32 - let (now, notes_per_instrument) = load_notes(&self.midi_path, progressbar); 32 + let (now, notes_per_instrument) = 33 + load_notes(&self.midi_path, progressbar); 33 34 34 35 SyncData { 35 36 bpm: tempo_to_bpm(now.tempo), 36 - stems: HashMap::from_iter(notes_per_instrument.iter().map(|(name, notes)| { 37 - let mut notes_per_ms = HashMap::<usize, Vec<audio::Note>>::new(); 37 + stems: HashMap::from_iter(notes_per_instrument.iter().map( 38 + |(name, notes)| { 39 + let mut notes_per_ms = 40 + HashMap::<usize, Vec<audio::Note>>::new(); 38 41 39 - if let Some(pb) = progressbar { 40 - pb.set_length(notes.len() as u64); 41 - pb.set_position(0); 42 - } 43 - progressbar.set_message(format!("Adding loaded notes for {name}")); 42 + if let Some(pb) = progressbar { 43 + pb.set_length(notes.len() as u64); 44 + pb.set_position(0); 45 + } 46 + progressbar 47 + .set_message(format!("Adding loaded notes for {name}")); 44 48 45 - for note in notes.iter() { 46 - notes_per_ms 47 - .entry(note.ms as usize) 48 - .or_default() 49 - .push(audio::Note { 50 - pitch: note.key, 51 - tick: note.tick, 52 - velocity: note.vel, 53 - }); 54 - progressbar.inc(1); 55 - } 49 + for note in notes.iter() { 50 + notes_per_ms.entry(note.ms as usize).or_default().push( 51 + audio::Note { 52 + pitch: note.key, 53 + tick: note.tick, 54 + velocity: note.vel, 55 + }, 56 + ); 57 + progressbar.inc(1); 58 + } 56 59 57 - let duration_ms = *notes_per_ms.keys().max().unwrap_or(&0); 60 + let duration_ms = *notes_per_ms.keys().max().unwrap_or(&0); 58 61 59 - if let Some(pb) = progressbar { 60 - pb.set_length(duration_ms as u64 - 1); 61 - pb.set_position(0); 62 - } 63 - progressbar.set_message(format!("Infering amplitudes for {name}")); 62 + if let Some(pb) = progressbar { 63 + pb.set_length(duration_ms as u64 - 1); 64 + pb.set_position(0); 65 + } 66 + progressbar 67 + .set_message(format!("Infering amplitudes for {name}")); 64 68 65 - let mut amplitudes = Vec::<f32>::new(); 66 - let mut last_amplitude = 0.0; 67 - for i in 0..duration_ms { 68 - if let Some(notes) = notes_per_ms.get(&i) { 69 - last_amplitude = notes 70 - .iter() 71 - .map(|n| n.velocity as f32) 72 - .collect::<Vec<f32>>() 73 - .average(); 69 + let mut amplitudes = Vec::<f32>::new(); 70 + let mut last_amplitude = 0.0; 71 + for i in 0..duration_ms { 72 + if let Some(notes) = notes_per_ms.get(&i) { 73 + last_amplitude = notes 74 + .iter() 75 + .map(|n| n.velocity as f32) 76 + .collect::<Vec<f32>>() 77 + .average(); 78 + } 79 + amplitudes.push(last_amplitude); 80 + progressbar.inc(1); 74 81 } 75 - amplitudes.push(last_amplitude); 76 - progressbar.inc(1); 77 - } 78 82 79 - ( 80 - name.clone(), 81 - Stem { 82 - amplitude_max: notes.iter().map(|n| n.vel).max().unwrap_or(0) as f32, 83 - amplitude_db: amplitudes, 84 - duration_ms, 85 - notes: notes_per_ms, 86 - name: name.clone(), 87 - }, 88 - ) 89 - })), 83 + ( 84 + name.clone(), 85 + Stem { 86 + amplitude_max: notes 87 + .iter() 88 + .map(|n| n.vel) 89 + .max() 90 + .unwrap_or(0) 91 + as f32, 92 + amplitude_db: amplitudes, 93 + duration_ms, 94 + notes: notes_per_ms, 95 + name: name.clone(), 96 + }, 97 + ) 98 + }, 99 + )), 90 100 markers: HashMap::new(), 91 101 } 92 102 } ··· 154 164 pb.set_position(0); 155 165 } 156 166 157 - let raw = std::fs::read(source) 158 - .unwrap_or_else(|_| panic!("Failed to read MIDI file {}", source.to_str().unwrap())); 167 + let raw = std::fs::read(source).unwrap_or_else(|_| { 168 + panic!("Failed to read MIDI file {}", source.to_str().unwrap()) 169 + }); 159 170 let midifile = midly::Smf::parse(&raw).unwrap(); 160 171 161 172 let mut timeline = Timeline::new(); 162 - progressbar.set_message(format!("MIDI file has {} tracks", midifile.tracks.len())); 173 + progressbar 174 + .set_message(format!("MIDI file has {} tracks", midifile.tracks.len())); 163 175 164 176 let mut now = Now { 165 177 ms: 0, 166 178 tempo: 0, 167 179 ticks_per_beat: match midifile.header.timing { 168 180 midly::Timing::Metrical(ticks_per_beat) => ticks_per_beat.as_int(), 169 - midly::Timing::Timecode(fps, subframe) => (1.0 / fps.as_f32() / subframe as f32) as u16, 181 + midly::Timing::Timecode(fps, subframe) => { 182 + (1.0 / fps.as_f32() / subframe as f32) as u16 183 + } 170 184 }, 171 185 }; 172 186 ··· 179 193 for event in track { 180 194 match event.kind { 181 195 TrackEventKind::Meta(MetaMessage::TrackName(name_bytes)) => { 182 - track_name = String::from_utf8(name_bytes.to_vec()).unwrap_or_default(); 196 + track_name = String::from_utf8(name_bytes.to_vec()) 197 + .unwrap_or_default(); 183 198 } 184 199 TrackEventKind::Meta(MetaMessage::Tempo(tempo)) => { 185 200 if now.tempo == 0 { ··· 239 254 } 240 255 241 256 if let Some(pb) = progressbar { 242 - pb.set_length(midifile.tracks.iter().map(|t| t.len() as u64).sum::<u64>()); 257 + pb.set_length( 258 + midifile.tracks.iter().map(|t| t.len() as u64).sum::<u64>(), 259 + ); 243 260 pb.set_prefix("Loading"); 244 261 pb.set_message("parsing MIDI events"); 245 262 pb.set_position(0); ··· 255 272 } = event.kind 256 273 { 257 274 match message { 258 - MidiMessage::NoteOn { key, vel } | MidiMessage::NoteOff { key, vel } => { 275 + MidiMessage::NoteOn { key, vel } 276 + | MidiMessage::NoteOff { key, vel } => { 259 277 stem_notes 260 278 .entry(absolute_tick_to_ms[tick] as u32) 261 279 .or_default() ··· 265 283 tick: *tick, 266 284 ms: absolute_tick_to_ms[tick] as u32, 267 285 key: key.as_int(), 268 - vel: if matches!(message, MidiMessage::NoteOff { .. }) { 286 + vel: if matches!( 287 + message, 288 + MidiMessage::NoteOff { .. } 289 + ) { 269 290 0 270 291 } else { 271 292 vel.as_int()
+4 -2
src/video/animation.rs
··· 3 3 use crate::{Canvas, Layer}; 4 4 5 5 /// Arguments: animation progress (from 0.0 to 1.0), canvas, current ms 6 - pub type AnimationUpdateFunction = dyn Fn(f32, &mut Canvas, usize) -> anyhow::Result<()>; 6 + pub type AnimationUpdateFunction = 7 + dyn Fn(f32, &mut Canvas, usize) -> anyhow::Result<()>; 7 8 8 9 /// An animation that only manipulates a single layer. The layer's render cache is automatically flushed at the end. See `AnimationUpdateFunction` for more information. 9 - pub type LayerAnimationUpdateFunction = dyn Fn(f32, &mut Layer, usize) -> anyhow::Result<()>; 10 + pub type LayerAnimationUpdateFunction = 11 + dyn Fn(f32, &mut Layer, usize) -> anyhow::Result<()>; 10 12 11 13 pub struct Animation { 12 14 pub name: String,
+26 -6
src/video/context.rs
··· 42 42 .notes 43 43 .get(&self.ms) 44 44 .iter() 45 - .map(|notes| notes.iter().map(|note| note.velocity).max().unwrap_or(0)) 45 + .map(|notes| { 46 + notes.iter().map(|note| note.velocity).max().unwrap_or(0) 47 + }) 46 48 .max() 47 49 .unwrap_or(0), 48 50 duration: stems[name].duration_ms, ··· 75 77 } 76 78 } 77 79 78 - pub fn later_frames(&mut self, delay: usize, render_function: &'static LaterRenderFunction) { 80 + pub fn later_frames( 81 + &mut self, 82 + delay: usize, 83 + render_function: &'static LaterRenderFunction, 84 + ) { 79 85 let current_frame = self.frame; 80 86 81 87 self.later_hooks.insert( ··· 90 96 ); 91 97 } 92 98 93 - pub fn later_ms(&mut self, delay: usize, render_function: &'static LaterRenderFunction) { 99 + pub fn later_ms( 100 + &mut self, 101 + delay: usize, 102 + render_function: &'static LaterRenderFunction, 103 + ) { 94 104 let current_ms = self.ms; 95 105 96 106 self.later_hooks.insert( 97 107 0, 98 108 LaterHook { 99 109 once: true, 100 - when: Box::new(move |_, context, _previous_beat| context.ms >= current_ms + delay), 110 + when: Box::new(move |_, context, _previous_beat| { 111 + context.ms >= current_ms + delay 112 + }), 101 113 render_function: Box::new(render_function), 102 114 }, 103 115 ); 104 116 } 105 117 106 - pub fn later_beats(&mut self, delay: f32, render_function: &'static LaterRenderFunction) { 118 + pub fn later_beats( 119 + &mut self, 120 + delay: f32, 121 + render_function: &'static LaterRenderFunction, 122 + ) { 107 123 let current_beat = self.beat; 108 124 109 125 self.later_hooks.insert( ··· 134 150 } 135 151 136 152 /// duration is in milliseconds 137 - pub fn animate(&mut self, duration: usize, f: &'static AnimationUpdateFunction) { 153 + pub fn animate( 154 + &mut self, 155 + duration: usize, 156 + f: &'static AnimationUpdateFunction, 157 + ) { 138 158 self.start_animation( 139 159 duration, 140 160 Animation::new(format!("unnamed animation {}", nanoid!()), f),
+47 -17
src/video/engine.rs
··· 17 17 pub type FrameNumber = usize; 18 18 pub type Millisecond = usize; 19 19 20 - pub type RenderFunction<C> = dyn Fn(&mut Canvas, &mut Context<C>) -> anyhow::Result<()>; 21 - pub type CommandAction<C> = dyn Fn(String, &mut Canvas, &mut Context<C>) -> anyhow::Result<()>; 20 + pub type RenderFunction<C> = 21 + dyn Fn(&mut Canvas, &mut Context<C>) -> anyhow::Result<()>; 22 + pub type CommandAction<C> = 23 + dyn Fn(String, &mut Canvas, &mut Context<C>) -> anyhow::Result<()>; 22 24 23 25 /// Arguments: canvas, context, previous rendered beat, previous rendered frame 24 - pub type HookCondition<C> = dyn Fn(&Canvas, &Context<C>, BeatNumber, FrameNumber) -> bool; 26 + pub type HookCondition<C> = 27 + dyn Fn(&Canvas, &Context<C>, BeatNumber, FrameNumber) -> bool; 25 28 26 29 /// Arguments: canvas, context, current milliseconds timestamp 27 - pub type LaterRenderFunction = dyn Fn(&mut Canvas, Millisecond) -> anyhow::Result<()>; 30 + pub type LaterRenderFunction = 31 + dyn Fn(&mut Canvas, Millisecond) -> anyhow::Result<()>; 28 32 29 33 /// Arguments: canvas, context, previous rendered beat 30 34 pub type LaterHookCondition<C> = dyn Fn(&Canvas, &Context<C>, BeatNumber) -> bool; ··· 137 141 Self { hooks, ..self } 138 142 } 139 143 140 - pub fn init(self, render_function: &'static RenderFunction<AdditionalContext>) -> Self { 144 + pub fn init( 145 + self, 146 + render_function: &'static RenderFunction<AdditionalContext>, 147 + ) -> Self { 141 148 self.with_hook(Hook { 142 149 when: Box::new(move |_, context, _, _| context.frame == 0), 143 150 render_function: Box::new(render_function), ··· 150 157 render_function: &'static RenderFunction<AdditionalContext>, 151 158 ) -> Self { 152 159 self.with_hook(Hook { 153 - when: Box::new(move |_, context, _, _| context.marker() == marker_text), 160 + when: Box::new(move |_, context, _, _| { 161 + context.marker() == marker_text 162 + }), 154 163 render_function: Box::new(render_function), 155 164 }) 156 165 } 157 166 158 - pub fn each_beat(self, render_function: &'static RenderFunction<AdditionalContext>) -> Self { 167 + pub fn each_beat( 168 + self, 169 + render_function: &'static RenderFunction<AdditionalContext>, 170 + ) -> Self { 159 171 self.with_hook(Hook { 160 172 when: Box::new( 161 - move |_, context, previous_rendered_beat, previous_rendered_frame| { 173 + move |_, 174 + context, 175 + previous_rendered_beat, 176 + previous_rendered_frame| { 162 177 previous_rendered_frame != context.frame 163 - && (context.ms == 0 || previous_rendered_beat != context.beat) 178 + && (context.ms == 0 179 + || previous_rendered_beat != context.beat) 164 180 }, 165 181 ), 166 182 render_function: Box::new(render_function), ··· 183 199 }; 184 200 185 201 self.with_hook(Hook { 186 - when: Box::new(move |_, context, _, _| context.beat_fractional % beats < 0.01), 202 + when: Box::new(move |_, context, _, _| { 203 + context.beat_fractional % beats < 0.01 204 + }), 187 205 render_function: Box::new(render_function), 188 206 }) 189 207 } 190 208 191 - pub fn each_frame(self, render_function: &'static RenderFunction<AdditionalContext>) -> Self { 209 + pub fn each_frame( 210 + self, 211 + render_function: &'static RenderFunction<AdditionalContext>, 212 + ) -> Self { 192 213 let hook = Hook { 193 214 when: Box::new(move |_, context, _, previous_rendered_frame| { 194 215 context.frame != previous_rendered_frame ··· 281 302 ) -> Self { 282 303 self.with_hook(Hook { 283 304 when: Box::new(move |_, ctx, _, _| { 284 - stems 285 - .split(',') 286 - .any(|stem_name| ctx.stem(stem_name).notes.iter().any(|note| note.is_on())) 305 + stems.split(',').any(|stem_name| { 306 + ctx.stem(stem_name).notes.iter().any(|note| note.is_on()) 307 + }) 287 308 }), 288 309 render_function: Box::new(move |canvas, ctx| { 289 310 let object = create_object(canvas, ctx)?; ··· 295 316 when: Box::new(move |_, ctx, _, _| { 296 317 stems.split(',').any(|stem_name| { 297 318 ctx.stem(stem_name).amplitude_relative() < cutoff_amplitude 298 - || ctx.stem(stem_name).notes.iter().any(|note| note.is_off()) 319 + || ctx 320 + .stem(stem_name) 321 + .notes 322 + .iter() 323 + .any(|note| note.is_off()) 299 324 }) 300 325 }), 301 326 render_function: Box::new(move |canvas, _| { ··· 370 395 match precision { 371 396 "milliseconds" => { 372 397 let current_time: NaiveDateTime = 373 - NaiveDateTime::parse_from_str(timestamp, "%H:%M:%S%.3f").unwrap(); 398 + NaiveDateTime::parse_from_str( 399 + timestamp, 400 + "%H:%M:%S%.3f", 401 + ) 402 + .unwrap(); 374 403 current_time == criteria_time 375 404 } 376 405 "seconds" => { 377 406 let current_time: NaiveDateTime = 378 - NaiveDateTime::parse_from_str(timestamp, "%H:%M:%S").unwrap(); 407 + NaiveDateTime::parse_from_str(timestamp, "%H:%M:%S") 408 + .unwrap(); 379 409 current_time == criteria_time 380 410 } 381 411 _ => panic!("Unknown precision"),
+127 -115
src/vst/beacon.rs
··· 1 - extern crate env_logger; 2 - extern crate ws; 3 - 4 - use super::Probe; 5 - use anyhow::Result; 6 - use once_cell::sync::Lazy; 7 - use std::sync::{Mutex, MutexGuard}; 8 - 9 - const BEACON_PORT: u16 = 8080; 10 - 11 - #[inline] 12 - pub fn beacon_url() -> String { 13 - return format!("ws://localhost:{BEACON_PORT}"); 14 - } 15 - 16 - pub fn connect_to_beacon<T: FnMut(&ws::Sender) -> ()>(mut action: T) -> Result<()> { 17 - ws::connect(beacon_url(), |out| { 18 - action(&out); 19 - move |_msg| out.close(ws::CloseCode::Normal) 20 - })?; 21 - Ok(()) 22 - } 23 - 24 - pub fn register_probe(probe: Probe) -> Result<()> { 25 - connect_to_beacon(|beacon| { 26 - beacon 27 - .send(format!( 28 - "+ probe {}", 29 - serde_json::to_string(&probe).expect("Failed to serialize probe") 30 - )) 31 - .expect("Failed to send register probe message"); 32 - })?; 33 - Ok(()) 34 - } 35 - 36 - pub fn unregister_probe(id: u32) -> Result<()> { 37 - connect_to_beacon(|beacon| { 38 - beacon 39 - .send(format!("- probe {}", id)) 40 - .expect("Failed to send unregister probe message"); 41 - })?; 42 - Ok(()) 43 - } 44 - 45 - #[derive(Default)] 46 - pub struct Beacon { 47 - pub probes: Vec<Probe>, 48 - } 49 - 50 - static BEACON: Lazy<Mutex<Beacon>> = Lazy::new(|| Mutex::new(Beacon::new())); 51 - 52 - pub fn get_beacon() -> MutexGuard<'static, Beacon> { 53 - return BEACON.lock().unwrap(); 54 - } 55 - 56 - impl Beacon { 57 - pub fn new() -> Self { 58 - return Self::default(); 59 - } 60 - 61 - pub fn start() -> Result<()> { 62 - ws::listen(format!("127.0.0.1:{BEACON_PORT}"), |out| { 63 - println!("Opening beacon connection with a probe..."); 64 - move |msg| match msg { 65 - ws::Message::Text(text) => match split3(&text) { 66 - ("+", "probe", probe_json) => match serde_json::from_str::<Probe>(probe_json) { 67 - Ok(probe) => { 68 - let mut beacon = get_beacon(); 69 - beacon.probes.push(probe); 70 - out.send("^ probe added") 71 - } 72 - Err(_) => out.send("! probe invalid JSON"), 73 - }, 74 - ("-", "probe", id_str) => { 75 - let id = id_str.parse::<u32>().unwrap(); 76 - let mut beacon = get_beacon(); 77 - let probe_index = beacon.probes.iter().position(|probe| probe.id == id); 78 - match probe_index { 79 - Some(probe_index) => { 80 - let removed_probe = beacon.probes.remove(probe_index); 81 - out.send(format!("^ probe {} removed", removed_probe.id)) 82 - } 83 - None => out.send(format!("! probe {id} not found")), 84 - } 85 - } 86 - ("=", "probe", "*") => { 87 - let beacon = get_beacon(); 88 - let body = serde_json::to_string(&beacon.probes).unwrap(); 89 - out.send(body) 90 - } 91 - ("=", "probe", id_str) => { 92 - let id = id_str.parse::<u32>().unwrap(); 93 - let beacon = get_beacon(); 94 - let probe = beacon.probes.iter().find(|probe| probe.id == id); 95 - match probe { 96 - Some(probe) => out.send(serde_json::to_string(probe).unwrap()), 97 - None => out.send(format!("! probe {id} not found")), 98 - } 99 - } 100 - _ => out.send("! invalid command"), 101 - }, 102 - ws::Message::Binary(_) => todo!(), 103 - } 104 - })?; 105 - Ok(()) 106 - } 107 - } 108 - 109 - fn split3(subject: &str) -> (&str, &str, &str) { 110 - let mut parts = subject.splitn(3, ' '); 111 - let first = parts.next().unwrap_or_default(); 112 - let second = parts.next().unwrap_or_default(); 113 - let third = parts.next().unwrap_or_default(); 114 - return (first, second, third); 115 - } 1 + extern crate env_logger; 2 + extern crate ws; 3 + 4 + use super::Probe; 5 + use anyhow::Result; 6 + use once_cell::sync::Lazy; 7 + use std::sync::{Mutex, MutexGuard}; 8 + 9 + const BEACON_PORT: u16 = 8080; 10 + 11 + #[inline] 12 + pub fn beacon_url() -> String { 13 + return format!("ws://localhost:{BEACON_PORT}"); 14 + } 15 + 16 + pub fn connect_to_beacon<T: FnMut(&ws::Sender) -> ()>( 17 + mut action: T, 18 + ) -> Result<()> { 19 + ws::connect(beacon_url(), |out| { 20 + action(&out); 21 + move |_msg| out.close(ws::CloseCode::Normal) 22 + })?; 23 + Ok(()) 24 + } 25 + 26 + pub fn register_probe(probe: Probe) -> Result<()> { 27 + connect_to_beacon(|beacon| { 28 + beacon 29 + .send(format!( 30 + "+ probe {}", 31 + serde_json::to_string(&probe).expect("Failed to serialize probe") 32 + )) 33 + .expect("Failed to send register probe message"); 34 + })?; 35 + Ok(()) 36 + } 37 + 38 + pub fn unregister_probe(id: u32) -> Result<()> { 39 + connect_to_beacon(|beacon| { 40 + beacon 41 + .send(format!("- probe {}", id)) 42 + .expect("Failed to send unregister probe message"); 43 + })?; 44 + Ok(()) 45 + } 46 + 47 + #[derive(Default)] 48 + pub struct Beacon { 49 + pub probes: Vec<Probe>, 50 + } 51 + 52 + static BEACON: Lazy<Mutex<Beacon>> = Lazy::new(|| Mutex::new(Beacon::new())); 53 + 54 + pub fn get_beacon() -> MutexGuard<'static, Beacon> { 55 + return BEACON.lock().unwrap(); 56 + } 57 + 58 + impl Beacon { 59 + pub fn new() -> Self { 60 + return Self::default(); 61 + } 62 + 63 + pub fn start() -> Result<()> { 64 + ws::listen(format!("127.0.0.1:{BEACON_PORT}"), |out| { 65 + println!("Opening beacon connection with a probe..."); 66 + move |msg| match msg { 67 + ws::Message::Text(text) => match split3(&text) { 68 + ("+", "probe", probe_json) => { 69 + match serde_json::from_str::<Probe>(probe_json) { 70 + Ok(probe) => { 71 + let mut beacon = get_beacon(); 72 + beacon.probes.push(probe); 73 + out.send("^ probe added") 74 + } 75 + Err(_) => out.send("! probe invalid JSON"), 76 + } 77 + } 78 + ("-", "probe", id_str) => { 79 + let id = id_str.parse::<u32>().unwrap(); 80 + let mut beacon = get_beacon(); 81 + let probe_index = 82 + beacon.probes.iter().position(|probe| probe.id == id); 83 + match probe_index { 84 + Some(probe_index) => { 85 + let removed_probe = 86 + beacon.probes.remove(probe_index); 87 + out.send(format!( 88 + "^ probe {} removed", 89 + removed_probe.id 90 + )) 91 + } 92 + None => out.send(format!("! probe {id} not found")), 93 + } 94 + } 95 + ("=", "probe", "*") => { 96 + let beacon = get_beacon(); 97 + let body = serde_json::to_string(&beacon.probes).unwrap(); 98 + out.send(body) 99 + } 100 + ("=", "probe", id_str) => { 101 + let id = id_str.parse::<u32>().unwrap(); 102 + let beacon = get_beacon(); 103 + let probe = 104 + beacon.probes.iter().find(|probe| probe.id == id); 105 + match probe { 106 + Some(probe) => { 107 + out.send(serde_json::to_string(probe).unwrap()) 108 + } 109 + None => out.send(format!("! probe {id} not found")), 110 + } 111 + } 112 + _ => out.send("! invalid command"), 113 + }, 114 + ws::Message::Binary(_) => todo!(), 115 + } 116 + })?; 117 + Ok(()) 118 + } 119 + } 120 + 121 + fn split3(subject: &str) -> (&str, &str, &str) { 122 + let mut parts = subject.splitn(3, ' '); 123 + let first = parts.next().unwrap_or_default(); 124 + let second = parts.next().unwrap_or_default(); 125 + let third = parts.next().unwrap_or_default(); 126 + return (first, second, third); 127 + }
+10 -10
src/vst/mod.rs
··· 1 - pub mod beacon; 2 - pub mod vst; 3 - pub mod probe; 4 - 5 - use nih_plug::{nih_export_clap, nih_export_vst3}; 6 - pub use probe::Probe; 7 - pub use vst::*; 8 - 9 - nih_export_clap!(ShapemakerVST); 10 - nih_export_vst3!(ShapemakerVST); 1 + pub mod beacon; 2 + pub mod probe; 3 + pub mod vst; 4 + 5 + use nih_plug::{nih_export_clap, nih_export_vst3}; 6 + pub use probe::Probe; 7 + pub use vst::*; 8 + 9 + nih_export_clap!(ShapemakerVST); 10 + nih_export_vst3!(ShapemakerVST);
+44 -44
src/vst/probe.rs
··· 1 - use std::fmt::Display; 2 - use serde::{Deserialize, Serialize}; 3 - 4 - #[derive(Serialize, Deserialize, Clone)] 5 - pub struct Probe { 6 - pub id: u32, 7 - pub added_at: String, 8 - pub automation_name: String, 9 - pub midi_name: String, 10 - pub audio_name: String, 11 - } 12 - 13 - impl Probe { 14 - /// Returns a new probe with the `added_at` field set to the current time. 15 - pub fn with_added_at_now(&self) -> Self { 16 - return Self { 17 - added_at: chrono::Utc::now().to_rfc3339(), 18 - ..self.clone() 19 - }; 20 - } 21 - } 22 - 23 - impl Display for Probe { 24 - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 25 - write!(f, "probe {} [", self.id)?; 26 - if !self.automation_name.is_empty() { 27 - write!(f, "automation \"{}\"", self.automation_name)?; 28 - if !self.midi_name.is_empty() || !self.audio_name.is_empty() { 29 - write!(f, " ")?; 30 - } 31 - } 32 - if !self.midi_name.is_empty() { 33 - write!(f, "midi \"{}\"", self.midi_name)?; 34 - if !self.audio_name.is_empty() { 35 - write!(f, " ")?; 36 - } 37 - } 38 - if !self.audio_name.is_empty() { 39 - write!(f, "audio \"{}\"", self.audio_name)?; 40 - } 41 - write!(f, "]")?; 42 - return Ok(()); 43 - } 44 - } 1 + use serde::{Deserialize, Serialize}; 2 + use std::fmt::Display; 3 + 4 + #[derive(Serialize, Deserialize, Clone)] 5 + pub struct Probe { 6 + pub id: u32, 7 + pub added_at: String, 8 + pub automation_name: String, 9 + pub midi_name: String, 10 + pub audio_name: String, 11 + } 12 + 13 + impl Probe { 14 + /// Returns a new probe with the `added_at` field set to the current time. 15 + pub fn with_added_at_now(&self) -> Self { 16 + return Self { 17 + added_at: chrono::Utc::now().to_rfc3339(), 18 + ..self.clone() 19 + }; 20 + } 21 + } 22 + 23 + impl Display for Probe { 24 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 25 + write!(f, "probe {} [", self.id)?; 26 + if !self.automation_name.is_empty() { 27 + write!(f, "automation \"{}\"", self.automation_name)?; 28 + if !self.midi_name.is_empty() || !self.audio_name.is_empty() { 29 + write!(f, " ")?; 30 + } 31 + } 32 + if !self.midi_name.is_empty() { 33 + write!(f, "midi \"{}\"", self.midi_name)?; 34 + if !self.audio_name.is_empty() { 35 + write!(f, " ")?; 36 + } 37 + } 38 + if !self.audio_name.is_empty() { 39 + write!(f, "audio \"{}\"", self.audio_name)?; 40 + } 41 + write!(f, "]")?; 42 + return Ok(()); 43 + } 44 + }
+4 -3
src/vst/vst.rs
··· 1 + use super::beacon; 2 + use super::probe::Probe; 1 3 use nih_plug::prelude::*; 2 4 use rand::Rng; 3 5 use std::sync::Arc; 4 - use super::beacon; 5 - use super::probe::Probe; 6 6 7 7 pub struct ShapemakerVST { 8 8 params: Arc<ShapemakerVSTParams>, ··· 146 146 const CLAP_SUPPORT_URL: Option<&'static str> = None; 147 147 148 148 // Don't forget to change these features 149 - const CLAP_FEATURES: &'static [ClapFeature] = &[ClapFeature::AudioEffect, ClapFeature::Stereo]; 149 + const CLAP_FEATURES: &'static [ClapFeature] = 150 + &[ClapFeature::AudioEffect, ClapFeature::Stereo]; 150 151 } 151 152 152 153 impl Vst3Plugin for ShapemakerVST {
+10 -6
src/wasm/transform.rs
··· 11 11 impl From<TransformationWASM> for Transformation { 12 12 fn from(transformation: TransformationWASM) -> Self { 13 13 match transformation.kind { 14 - TransformationType::Scale => { 15 - Transformation::Scale(transformation.parameters[0], transformation.parameters[1]) 16 - } 17 - TransformationType::Rotate => Transformation::Rotate(transformation.parameters[0]), 18 - TransformationType::Skew => { 19 - Transformation::Skew(transformation.parameters[0], transformation.parameters[1]) 14 + TransformationType::Scale => Transformation::Scale( 15 + transformation.parameters[0], 16 + transformation.parameters[1], 17 + ), 18 + TransformationType::Rotate => { 19 + Transformation::Rotate(transformation.parameters[0]) 20 20 } 21 + TransformationType::Skew => Transformation::Skew( 22 + transformation.parameters[0], 23 + transformation.parameters[1], 24 + ), 21 25 TransformationType::Matrix => Transformation::Matrix( 22 26 transformation.parameters[0], 23 27 transformation.parameters[1],