···11+pub mod angle;
22+pub mod point;
33+pub mod region;
44+55+pub use angle::Angle;
66+pub use point::Point;
77+pub use region::{Containable, Region};
···11+pub mod canvas;
22+pub mod color;
33+pub mod fill;
44+pub mod filter;
55+pub mod layer;
66+pub mod objects;
77+pub mod transform;
88+99+pub use color::{Color, ColorMapping};
1010+pub use fill::Fill;
1111+pub use filter::{Filter, FilterType};
1212+pub use layer::Layer;
1313+pub use objects::{ColoredObject, LineSegment, Object, ObjectSizes};
1414+pub use transform::{Transformation, TransformationType};
1515+pub use canvas::Canvas;
···11+use super::random_color;
22+use crate::{Angle, Color, Fill};
33+use rand::Rng;
44+55+impl Fill {
66+ pub fn random_solid(except: Option<Color>) -> Self {
77+ Fill::Solid(random_color(except))
88+ }
99+1010+ pub fn random_hatches(except: Option<Color>) -> Self {
1111+ let hatch_size = rand::thread_rng().gen_range(5..=100) as f32 * 1e-2;
1212+ Fill::Hatched(
1313+ random_color(except),
1414+ Angle(rand::thread_rng().gen_range(0.0..360.0)),
1515+ hatch_size,
1616+ // under a certain hatch size, we can't see the hatching if the ratio is not ½
1717+ if hatch_size < 8.0 {
1818+ 0.5
1919+ } else {
2020+ rand::thread_rng().gen_range(1..=4) as f32 / 4.0
2121+ },
2222+ )
2323+ }
2424+}
src/random/layer.rs
This is a binary file and will not be displayed.
+13
src/random/mod.rs
···11+pub mod canvas;
22+pub mod color;
33+pub mod fill;
44+pub mod layer;
55+pub mod objects;
66+pub mod region;
77+88+pub use canvas::*;
99+pub use color::*;
1010+pub use fill::*;
1111+pub use layer::*;
1212+pub use objects::*;
1313+pub use region::*;
src/random/objects.rs
This is a binary file and will not be displayed.
src/random/region.rs
This is a binary file and will not be displayed.
src/region.rs
src/geometry/region.rs
+202
src/rendering/canvas.rs
···11+use super::renderable::SVGRenderable;
22+use crate::graphics::canvas::Canvas;
33+use measure_time::{debug_time, info_time};
44+use rayon::{
55+ iter::{IndexedParallelIterator, ParallelIterator},
66+ slice::ParallelSliceMut,
77+};
88+use resvg::usvg;
99+use std::sync::Arc;
1010+1111+impl SVGRenderable for Canvas {
1212+ fn render_to_svg(
1313+ &self,
1414+ _colormap: crate::ColorMapping,
1515+ _cell_size: usize,
1616+ _object_sizes: crate::graphics::objects::ObjectSizes,
1717+ _id: &str,
1818+ ) -> anyhow::Result<svg::node::element::Element> {
1919+ debug_time!("render_to_svg");
2020+ let background_color = self.background.unwrap_or_default();
2121+ let mut svg = svg::Document::new();
2222+ svg = svg.add(
2323+ svg::node::element::Rectangle::new()
2424+ .set("x", -(self.canvas_outter_padding as i32))
2525+ .set("y", -(self.canvas_outter_padding as i32))
2626+ .set("width", self.width())
2727+ .set("height", self.height())
2828+ .set("fill", background_color.render(&self.colormap)),
2929+ );
3030+3131+ for layer in self.layers.iter().filter(|layer| !layer.hidden).rev() {
3232+ svg = svg.add(layer.render_to_svg(
3333+ self.colormap.clone(),
3434+ self.cell_size,
3535+ layer.object_sizes,
3636+ "",
3737+ )?);
3838+ }
3939+4040+ let mut defs = svg::node::element::Definitions::new();
4141+ for filter in self.unique_filters() {
4242+ defs = defs.add(filter.render_to_svg(
4343+ self.colormap.clone(),
4444+ self.cell_size,
4545+ self.object_sizes,
4646+ "",
4747+ )?);
4848+ }
4949+5050+ for pattern_fill in self.unique_pattern_fills() {
5151+ if let Some(patterndef) = pattern_fill.pattern_definition(&self.colormap) {
5252+ defs = defs.add(patterndef)
5353+ }
5454+ }
5555+5656+ Ok(svg
5757+ .add(defs)
5858+ .set(
5959+ "viewBox",
6060+ format!(
6161+ "{0} {0} {1} {2}",
6262+ -(self.canvas_outter_padding as i32),
6363+ self.width(),
6464+ self.height()
6565+ ),
6666+ )
6767+ .set("width", self.width())
6868+ .set("height", self.height())
6969+ .into())
7070+ }
7171+}
7272+7373+impl Canvas {
7474+ pub fn svg_to_pixmap(
7575+ &self,
7676+ width: u32,
7777+ height: u32,
7878+ contents: &str,
7979+ ) -> anyhow::Result<tiny_skia::Pixmap> {
8080+ info_time!("svg_to_pixmap");
8181+8282+ let mut pixmap = self.create_pixmap(width, height);
8383+8484+ let parsed_svg = &svg_to_usvg_tree(contents, &self.fontdb)?;
8585+8686+ self.usvg_tree_to_pixmap(width, height, pixmap.as_mut(), parsed_svg);
8787+8888+ Ok(pixmap)
8989+ }
9090+9191+ pub fn render_to_pixmap_no_cache(
9292+ &mut self,
9393+ width: u32,
9494+ height: u32,
9595+ ) -> anyhow::Result<tiny_skia::Pixmap> {
9696+ let svg_contents = self
9797+ .render_to_svg(self.colormap.clone(), self.cell_size, self.object_sizes, "")?
9898+ .to_string();
9999+ self.svg_to_pixmap(width, height, &svg_contents)
100100+ }
101101+102102+ // Returns None if we had a render cache hit -- pixmap is in self.png_render_cache in that case
103103+ pub fn render_to_pixmap(
104104+ &mut self,
105105+ width: u32,
106106+ height: u32,
107107+ ) -> anyhow::Result<Option<tiny_skia::Pixmap>> {
108108+ info_time!("render_to_pixmap");
109109+110110+ self.load_fonts()?;
111111+112112+ let new_svg_contents = self
113113+ .render_to_svg(self.colormap.clone(), self.cell_size, self.object_sizes, "")?
114114+ .to_string();
115115+ if let Some(cached_svg) = &self.png_render_cache {
116116+ if *cached_svg == new_svg_contents {
117117+ // TODO find a way to avoid .cloneing the pixmap
118118+ return Ok(None);
119119+ }
120120+ }
121121+122122+ let pixmap = self.svg_to_pixmap(width, height, &new_svg_contents)?;
123123+124124+ self.png_render_cache = Some(new_svg_contents);
125125+126126+ Ok(Some(pixmap))
127127+ }
128128+129129+ pub fn pixmap_to_hwc_frame(
130130+ &self,
131131+ resolution: u32,
132132+ pixmap: &tiny_skia::Pixmap,
133133+ ) -> anyhow::Result<video_rs::Frame> {
134134+ info_time!("pixmap_to_hwc_frame");
135135+ let (width, height) = self.resolution_to_size(resolution);
136136+ let (width, height) = (width as usize, height as usize);
137137+ let mut data = vec![0u8; height * width * 3];
138138+139139+ data.par_chunks_exact_mut(3)
140140+ .enumerate()
141141+ .for_each(|(index, chunk)| {
142142+ let x = index % width;
143143+ let y = index / width;
144144+145145+ let pixel = pixmap
146146+ .pixel(x as u32, y as u32)
147147+ .unwrap_or_else(|| panic!("No pixel found at x, y = {x}, {y}"));
148148+149149+ chunk[0] = pixel.red();
150150+ chunk[1] = pixel.green();
151151+ chunk[2] = pixel.blue();
152152+ });
153153+154154+ Ok(video_rs::Frame::from_shape_vec([height, width, 3], data)?)
155155+ }
156156+157157+ pub fn render_to_hwc_frame(&mut self, resolution: u32) -> anyhow::Result<video_rs::Frame> {
158158+ let (width, height) = self.resolution_to_size(resolution);
159159+ let pixmap = self.render_to_pixmap_no_cache(width, height)?;
160160+ self.pixmap_to_hwc_frame(resolution, &pixmap)
161161+ }
162162+163163+ fn usvg_tree_to_pixmap(
164164+ &self,
165165+ width: u32,
166166+ height: u32,
167167+ mut pixmap_mut: tiny_skia::PixmapMut<'_>,
168168+ parsed_svg: &resvg::usvg::Tree,
169169+ ) {
170170+ info_time!("usvg_tree_to_pixmap");
171171+ resvg::render(
172172+ parsed_svg,
173173+ tiny_skia::Transform::from_scale(
174174+ width as f32 / self.width() as f32,
175175+ height as f32 / self.height() as f32,
176176+ ),
177177+ &mut pixmap_mut,
178178+ );
179179+ }
180180+181181+ fn create_pixmap(&self, width: u32, height: u32) -> tiny_skia::Pixmap {
182182+ info_time!("create_pixmap");
183183+ tiny_skia::Pixmap::new(width, height).expect("Failed to create pixmap")
184184+ }
185185+}
186186+187187+fn svg_to_usvg_tree(
188188+ svg: &str,
189189+ fontdb: &Option<Arc<usvg::fontdb::Database>>,
190190+) -> anyhow::Result<resvg::usvg::Tree> {
191191+ info_time!("svg_to_usvg_tree");
192192+ Ok(resvg::usvg::Tree::from_str(
193193+ svg,
194194+ &match fontdb {
195195+ Some(fontdb) => resvg::usvg::Options {
196196+ fontdb: fontdb.clone(),
197197+ ..Default::default()
198198+ },
199199+ None => resvg::usvg::Options::default(),
200200+ },
201201+ )?)
202202+}
···11+pub mod canvas;
22+pub mod fill;
33+pub mod filter;
44+pub mod fonts;
55+pub mod layer;
66+pub mod objects;
77+pub mod renderable;
88+pub mod transform;
99+1010+pub use renderable::{CSSRenderable, SVGAttributesRenderable, SVGRenderable};