use cairo::Format; use cairo::{ImageSurface, SvgSurface}; use geo::{polygon, LineString, MultiPolygon, Polygon}; use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; use crate::context::commandline::Opt; use crate::context::palette::Palette; use crate::context::renderer::{PngRenderContainer, RenderContainer, SvgRenderContainer}; use iso8601::Date::*; pub mod commandline; pub mod palette; pub mod renderer; pub struct Context { render_container: Box, pub height: f64, pub width: f64, pub line_size: f64, pub pseudo_random_number_generator: Box, } impl Context { /// generate a frame for the image pub fn frame(&mut self, left: f64, bottom: f64, right: f64, top: f64) -> Polygon { polygon![ (x: left, y: top), (x: self.width - right, y: top), (x: self.width - right, y: self.height - bottom), (x: left , y: self.height - bottom), ] } pub fn draw_multipolygon(&self, multi_polygon: &MultiPolygon) { for polygon in multi_polygon.iter() { self.draw_polygon(polygon); } } pub fn draw_polygon(&self, polygon: &Polygon) { self.plot_line_string(polygon.exterior()); for interior in polygon.interiors() { self.plot_line_string(interior); } } fn plot_line_string(&self, line_string: &LineString) { let context = self.render_container.context(); let mut init = true; for point in line_string.clone().into_points() { if init { context.move_to(point.x(), point.y()); init = false; } else { context.line_to(point.x(), point.y()); } } context.stroke(); } /// restore state which was saved before by the context pub fn restore(&self) { self.render_container.context().restore(); } /// scale, but line with stays pub fn scale(&self, factor: f64) { let context = self.render_container.context(); context.save(); context.scale(factor, factor); context.set_line_width(1. / factor); } /// translate to the center of the image pub fn translate_center(&self) { self.translate(self.width / 2., self.height / 2.); } /// translate to a position pub fn translate(&self, tx: f64, ty: f64) { let context = self.render_container.context(); context.save(); context.translate(tx, ty); } pub fn render(self) { self.render_container.render(); } pub fn gen_rng(&mut self) -> StdRng { StdRng::seed_from_u64(self.pseudo_random_number_generator.gen()) } pub fn create() -> Context { let opt = Opt::get_command_line_arguments(); let height = f64::from(opt.height.clone()); let width = f64::from(opt.width.clone()); let line_size = opt.line_size.clone(); let random_seed = match opt.random_seed { Some(random_seed) => random_seed, None => match opt.random_seed_date { Some(YMD { year, month, day }) => { ((10000 + year) as u32 * 365 + month * 12 + day) as u64 } Some(Week { year, ww, d }) => ((10000 + year) as u32 * 365 + ww * 7 + d) as u64, Some(Ordinal { year, ddd }) => ((10000 + year) as u32 * 365 + ddd) as u64, None => rand::random(), }, }; println!("random seed {}", random_seed); let mut rng = StdRng::seed_from_u64(random_seed); // color let y: f32 = rng.gen(); // generates a float between 0 and 1 let hue: f32 = y * 360.0; let saturation: f32 = f32::max(rng.gen(), 0.6); let value: f32 = f32::max(rng.gen(), 0.6); let palette = Palette::dark_on_bright(Palette::color(hue, saturation, value)); let render_container: Box = if opt.output_type == "svg" { let svg_surface = SvgSurface::new( f64::from(width), f64::from(height), Some(opt.output.as_path()), ) .expect("Can't svg surface"); Box::new(SvgRenderContainer::new(svg_surface, palette)) as Box } else if opt.output_type == "png" { let png_surface = ImageSurface::create(Format::Rgb24, opt.width.clone(), opt.height.clone()) .expect("Can't create png surface"); Box::new(PngRenderContainer::new( opt.output.to_string_lossy().to_string(), png_surface, palette, )) as Box } else { panic!("output type unknown"); }; render_container.init(width as f64, height as f64, line_size); Context { height, width, line_size, pseudo_random_number_generator: Box::new(rng), render_container, } } }