use cairo::Format; use cairo::{ImageSurface, SvgSurface}; use geo::{polygon, LineString, MultiLineString, MultiPolygon, Polygon}; use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; use crate::context::command_line_options::Opt; use crate::context::palette::Palette; use crate::context::renderer::{PngRenderContainer, RenderContainer, SvgRenderContainer}; use iso8601::Date::*; pub mod command_line_options; 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, pub seed: u64, } 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), ] } /// a fall back operation, if you need a function not provided by this struct. pub fn cairo_context(&self) -> &cairo::Context { self.render_container.context() } 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.draw_line_string(polygon.exterior()); for interior in polygon.interiors() { self.draw_line_string(interior); } } pub fn draw_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(); } pub fn draw_line_strings(&self, line_strings: &Vec>) { for lines in line_strings { self.draw_line_string(&lines); } } pub fn draw_multiline_string(&self, multilinestring: &MultiLineString) { for line in multilinestring.iter() { self.draw_line_string(line); } } /// restore state which was saved before by the context pub fn restore(&self) { self.render_container.context().restore(); } /// save state to restore later again pub fn save(&self) { self.render_container.context().save(); } /// scale, but line with stays pub fn scale(&self, factor: f64) { let context = self.render_container.context(); 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.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 line_size = opt.line_size.clone(); // todo : this is not correct let mm = 2.8346456693; // 1mm = 2.8346456693pt let (mut height, mut width) = match opt.format { None => (f64::from(opt.height.clone()), f64::from(opt.width.clone())), Some(format) => match &format[..] { "a3" => (297. * mm, 420. * mm), "a4" => (210. * mm, 297. * mm), "a5" => (148. * mm, 210. * mm), "x220" => (768., 1366.), _ => (f64::from(opt.height.clone()), f64::from(opt.width.clone())), }, }; print!("height: {}\n", height); print!("width: {}\n", width); if opt.orientation == "portrait" { let cache = height; height = width; width = cache; } 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 main_color = Palette::random_color(StdRng::seed_from_u64(rng.gen())); let palette = match &opt.color_type[..] { "bright_on_dark" => Palette::bright_on_dark(main_color), "dark_on_bright" => Palette::dark_on_bright(main_color), _ => Palette::bright_on_dark(main_color), }; 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, width as i32, height as i32) .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, seed: random_seed, } } }