experimental SVG-based video rendering engine made for music videos. React to MIDI or arbitrary signals from your DAW through "probe" VSTs
1use super::svg;
2use crate::{ColorMapping, graphics::objects::ObjectSizes};
3use anyhow::Result;
4use itertools::Itertools;
5use std::collections::HashMap;
6
7/// Struct can be rendered as a SVG element
8pub trait SVGRenderable {
9 fn render_to_svg(
10 &self,
11 colormap: ColorMapping,
12 cell_size: usize,
13 object_sizes: ObjectSizes,
14 id: &str,
15 ) -> Result<svg::Node>;
16}
17
18/// Struct can be rendered as attributes of a SVG element
19pub trait SVGAttributesRenderable {
20 /// When merging multiple SVGAttributesRenderable, this string is used to join multiple values for the same key
21 const MULTIPLE_VALUES_JOIN_BY: &'static str = ", ";
22
23 fn render_to_svg_attributes(
24 &self,
25 colormap: ColorMapping,
26 cell_size: usize,
27 object_sizes: ObjectSizes,
28 id: &str,
29 ) -> Result<HashMap<String, String>>;
30}
31
32/// Struct can be rendered as a CSS ruleset (e.g. no selectors)
33pub trait CSSRenderable {
34 fn render_to_css_filled(&self, colormap: &ColorMapping) -> String;
35 fn render_to_css_stroked(&self, colormap: &ColorMapping) -> String;
36 fn render_to_css(
37 &self,
38 colormap: &ColorMapping,
39 fill_as_stroke_color: bool,
40 ) -> String {
41 if fill_as_stroke_color {
42 self.render_to_css_stroked(colormap)
43 } else {
44 self.render_to_css_filled(colormap)
45 }
46 }
47}
48
49impl<T: CSSRenderable, V: Clone + IntoIterator<Item = T>> CSSRenderable for V {
50 fn render_to_css_filled(&self, colormap: &ColorMapping) -> String {
51 self.clone()
52 .into_iter()
53 .map(|v| v.render_to_css_filled(colormap))
54 .join("\n")
55 }
56
57 fn render_to_css_stroked(&self, colormap: &ColorMapping) -> String {
58 self.clone()
59 .into_iter()
60 .map(|v| v.render_to_css_stroked(colormap))
61 .join("\n")
62 }
63}
64
65// We get the Option<T> implementation for free since Option<T> implements IntoIterator, and it works:
66// None => empty iterator => empty hashmap,
67// Some(T) => iterator with one element => one hashmap, no merging.
68// I love Rust <3.
69
70impl<T, V> SVGAttributesRenderable for V
71where
72 T: SVGAttributesRenderable,
73 V: Clone + IntoIterator<Item = T>,
74{
75 fn render_to_svg_attributes(
76 &self,
77 colormap: ColorMapping,
78 cell_size: usize,
79 object_sizes: ObjectSizes,
80 id: &str,
81 ) -> Result<HashMap<String, String>> {
82 let mut attrs = HashMap::<String, String>::new();
83 for attrmap in self.clone().into_iter().map(|v| {
84 v.render_to_svg_attributes(
85 colormap.clone(),
86 cell_size,
87 object_sizes,
88 id,
89 )
90 .unwrap()
91 }) {
92 for (key, value) in attrmap {
93 if attrs.contains_key(&key) {
94 attrs.insert(
95 key.clone(),
96 format!(
97 "{}{}{}",
98 attrs[&key],
99 T::MULTIPLE_VALUES_JOIN_BY,
100 value
101 ),
102 );
103 } else {
104 attrs.insert(key, value);
105 }
106 }
107 }
108 Ok(attrs)
109 }
110}