experimental SVG-based video rendering engine made for music videos. React to MIDI or arbitrary signals from your DAW through "probe" VSTs
at main 211 lines 5.1 kB view raw
1use super::shapes::Shape; 2use crate::{Angle, Fill, Filter, Point, Region, Transformation}; 3use itertools::Itertools; 4use std::fmt::Display; 5#[cfg(feature = "web")] 6use wasm_bindgen::prelude::*; 7 8use super::{Color, fill::FillOperations}; 9 10impl Shape { 11 pub fn filled(self, fill: Fill) -> Object { 12 Object::from((self, Some(fill))) 13 } 14 15 pub fn colored(self, color: Color) -> Object { 16 Object::from((self, None)).colored(color) 17 } 18 19 pub fn filtered(self, filter: Filter) -> Object { 20 Object::from((self, None)).filtered(filter) 21 } 22 23 pub fn transform(self, transformation: Transformation) -> Object { 24 Object::from((self, None)).transformed(transformation) 25 } 26} 27 28#[derive(Debug, Clone)] 29pub struct Object { 30 pub shape: Shape, 31 pub fill: Option<Fill>, 32 pub filters: Vec<Filter>, 33 pub transformations: Vec<Transformation>, 34 pub tags: Vec<String>, 35 pub clip_to: Option<Region>, 36} 37 38impl Object { 39 pub fn filtered(mut self, filter: Filter) -> Self { 40 self.filters.push(filter); 41 self 42 } 43 44 pub fn transformed(mut self, transformation: Transformation) -> Self { 45 self.transformations.push(transformation); 46 self 47 } 48 49 pub fn filled(mut self, fill: Fill) -> Self { 50 self.fill = Some(fill); 51 self 52 } 53 54 pub fn colored(mut self, color: Color) -> Self { 55 self.fill = Some(Fill::Solid(color)); 56 self 57 } 58 59 pub fn opacified(mut self, opacity: f32) -> Self { 60 if let Some(fill) = &mut self.fill { 61 *fill = fill.opacify(opacity); 62 } 63 self 64 } 65 66 pub fn clipped_to(mut self, region: impl Into<Region>) -> Self { 67 self.clip_to = Some(region.into()); 68 self 69 } 70 71 pub fn clear_filters(&mut self) { 72 self.filters.clear(); 73 } 74 75 pub fn refill(&mut self, fill: Fill) { 76 self.fill = Some(fill); 77 } 78 79 pub fn recolor(&mut self, color: Color) { 80 self.fill = Some(Fill::Solid(color)) 81 } 82 83 pub fn filter(&mut self, filter: Filter) { 84 self.filters.push(filter) 85 } 86 87 pub fn rotate(&mut self, angle: Angle) { 88 self.transformations 89 .push(Transformation::Rotate(angle.degrees())) 90 } 91 92 pub fn set_rotation(&mut self, angle: Angle) { 93 self.transformations 94 .retain(|t| !matches!(t, Transformation::Rotate(_))); 95 self.transformations 96 .push(Transformation::Rotate(angle.degrees())) 97 } 98 99 pub fn region(&self) -> Region { 100 self.shape.region() 101 } 102 103 pub fn position(&self) -> Point { 104 self.shape.position() 105 } 106 107 pub fn tag(&mut self, tag: impl Display) -> String { 108 let tag = tag.to_string(); 109 110 self.tags.push(tag.clone()); 111 tag 112 } 113 114 pub fn remove_tag(&mut self, tag: impl Display) { 115 let tag_str = format!("{tag}"); 116 self.tags.retain(|t| t != &tag_str); 117 } 118 119 pub fn tagged(mut self, tag: impl Display) -> Self { 120 self.tags.push(format!("{tag}")); 121 self 122 } 123 124 pub fn has_tag(&self, tag: impl Display) -> bool { 125 let tag_str = format!("{tag}"); 126 self.tags.iter().any(|t| t == &tag_str) 127 } 128} 129 130impl std::fmt::Display for Object { 131 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 132 let Object { 133 shape: object, 134 fill, 135 filters, 136 transformations, 137 tags, 138 clip_to, 139 } = self; 140 141 if fill.is_some() { 142 write!(f, "{:?} {:?}", fill.unwrap(), object)?; 143 } else { 144 write!(f, "transparent {:?}", object)?; 145 } 146 147 if !filters.is_empty() { 148 write!(f, " with filters {:?}", filters)?; 149 } 150 151 if !transformations.is_empty() { 152 write!(f, " with transformations {:?}", transformations)?; 153 } 154 155 if !tags.is_empty() { 156 write!(f, "{}", tags.iter().map(|t| format!("#{t}")).join(" "))?; 157 } 158 159 if let Some(clip_to) = clip_to { 160 write!(f, " (clipped to {:?})", clip_to)?; 161 } 162 163 Ok(()) 164 } 165} 166 167impl From<Shape> for Object { 168 fn from(value: Shape) -> Self { 169 Object { 170 shape: value, 171 fill: None, 172 filters: vec![], 173 transformations: vec![], 174 tags: vec![], 175 clip_to: None, 176 } 177 } 178} 179 180impl From<(Shape, Option<Fill>)> for Object { 181 fn from((object, fill): (Shape, Option<Fill>)) -> Self { 182 Object { 183 shape: object, 184 fill, 185 filters: vec![], 186 transformations: vec![], 187 tags: vec![], 188 clip_to: None, 189 } 190 } 191} 192 193#[cfg_attr(feature = "web", wasm_bindgen)] 194#[derive(Debug, Clone, Copy)] 195pub struct ObjectSizes { 196 pub empty_shape_stroke_width: f32, 197 pub small_circle_radius: f32, 198 pub dot_radius: f32, 199 pub default_line_width: f32, 200} 201 202impl Default for ObjectSizes { 203 fn default() -> Self { 204 Self { 205 empty_shape_stroke_width: 0.5, 206 small_circle_radius: 5.0, 207 dot_radius: 2.0, 208 default_line_width: 2.0, 209 } 210 } 211}