experimental SVG-based video rendering engine made for music videos. React to MIDI or arbitrary signals from your DAW through "probe" VSTs
1use itertools::Itertools;
2use measure_time::debug_time;
3
4use crate::{Fill, Object, Shape};
5
6use super::{
7 CSSRenderable, SVGAttributesRenderable, renderable::SVGRenderable, svg,
8};
9
10impl SVGRenderable for Object {
11 fn render_to_svg(
12 &self,
13 colormap: crate::ColorMapping,
14 cell_size: usize,
15 object_sizes: crate::graphics::objects::ObjectSizes,
16 id: &str,
17 ) -> anyhow::Result<svg::Node> {
18 debug_time!("render_to_svg/colored_object");
19
20 let plain_obj = match &self.shape {
21 Shape::RawSVG { .. } => self.render_raw_svg(&colormap),
22 _ => self.shape.render_to_svg(
23 colormap.clone(),
24 cell_size,
25 object_sizes,
26 id,
27 )?,
28 };
29
30 let mut css = self
31 .fill
32 .render_to_css(&colormap.clone(), !self.shape.fillable());
33
34 let object_svg = if !self.transformations.is_empty()
35 || !self.filters.is_empty()
36 {
37 // transform-box is not supported by resvg yet
38 // css += "transform-box: fill-box; transform-origin: 50% 50%;";
39
40 let (center_x, center_y) =
41 self.shape.region().center_coords(cell_size);
42
43 css += &format!("transform-origin: {center_x}px {center_y}px;");
44
45 css += self
46 .filters
47 .iter()
48 .map(|f| f.render_to_css_filled(&colormap))
49 .join(" ")
50 .as_ref();
51
52 svg::tag("g")
53 .dataset("object", id)
54 .with_attributes(self.transformations.render_to_svg_attributes(
55 colormap,
56 cell_size,
57 object_sizes,
58 id,
59 )?)
60 .wrapping(vec![plain_obj])
61 .attr("style", &css)
62 .into()
63 } else {
64 match plain_obj {
65 svg::Node::Element(el) => el.attr("style", &css).into(),
66 _ => plain_obj,
67 }
68 };
69
70 if let Some(region) = &self.clip_to {
71 Ok(svg::tag("g")
72 .attr("clip-path", region.clip_path_id())
73 .child(object_svg)
74 .into())
75 } else {
76 Ok(object_svg)
77 }
78 }
79}
80
81impl Object {
82 fn render_raw_svg(&self, colormap: &crate::ColorMapping) -> svg::Node {
83 if let Shape::RawSVG { content, color } = &self.shape {
84 let filled_svg = match &self.fill {
85 Some(Fill::Solid(fill_color)) => {
86 content.replace(color, &fill_color.render(colormap))
87 }
88 _ => content.clone(),
89 };
90
91 svg::Node::SVG(filled_svg)
92 } else {
93 panic!("Called render_raw_svg on a non-RawSVG shape");
94 }
95 }
96}