use cairo::Format; use cairo::{ImageSurface, SvgSurface}; use geo_types::{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}; 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 { pub fn draw_multipolgon(&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(); } } // cairo facades impl Context { /// 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); } /// plot a path // pub fn plot_object(&self, object: &Object) { // self.render_container.plot_object(object); // } /// plot a path // pub fn plot_polygon(&self, polygon: &OldPolynom) { // self.plot_path(&polygon.path, polygon.filled); // } /// plot a path // pub fn plot_path(&self, path: &Vec<[f64; 2]>, fill: bool) { // self.render_container.plot_path(path, fill); // } pub fn render(self) { self.render_container.render(); } 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 => 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.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 { 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 }; 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, } } }