use crate::context::palette::Palette; use cairo::Context; use cairo::{ImageSurface, Surface, SvgSurface}; use std::fs::File; /// container that holds everything to render surface pub(crate) trait RenderContainer { /// render command fn render(&self); /// get render context just in case fn context(&self) -> &Context; /// surface to create cairo context fn surface(&self) -> &Surface; /// initialize RenderContainer fn init(&self, width: f64, height: f64, line_size: f64); /// plot a path fn plot_path(&self, path: &Vec<[f64; 2]>, fill: bool); fn put_path_on_stack(&self, path: &Vec<[f64; 2]>) { let context = self.context(); let mut init = true; for &[x, y] in path.iter() { if init { context.move_to(x, y); init = false; } else { context.line_to(x, y); } } context.close_path(); } } /// container to render svg images pub struct SvgRenderContainer { surface: SvgSurface, context: Context, } impl SvgRenderContainer { pub fn new(surface: SvgSurface, _: Palette) -> Self { let context = Context::new(&surface); SvgRenderContainer { surface, context } } } impl RenderContainer for SvgRenderContainer { fn render(&self) { // nothing to do } fn context(&self) -> &Context { &self.context } fn surface(&self) -> &Surface { &self.surface as &Surface } fn init(&self, _width: f64, _height: f64, line_size: f64) { self.context.set_line_width(line_size); } /// plot a path fn plot_path(&self, path: &Vec<[f64; 2]>, fill: bool) { self.put_path_on_stack(path); let context = self.context(); if fill { context.fill() } else { context.stroke(); } } } /// container to render png images pub struct PngRenderContainer { /// output path of the png path: String, surface: ImageSurface, context: Context, palette: Palette, } impl PngRenderContainer { pub fn new(path: String, surface: ImageSurface, palette: Palette) -> Self { let context = Context::new(&surface); PngRenderContainer { path, surface, context, palette, } } } impl RenderContainer for PngRenderContainer { fn render(&self) { let mut file = File::create(&self.path).expect("Couldn't create 'file.png'"); match self.surface.write_to_png(&mut file) { Ok(_) => println!("{}, created", self.path), Err(_) => println!("Error create file.png"), } } fn context(&self) -> &Context { &self.context } fn surface(&self) -> &Surface { &self.surface as &Surface } fn init(&self, width: f64, height: f64, line_size: f64) { // set background color self.context.set_source_rgb( self.palette.background_color.red as f64, self.palette.background_color.green as f64, self.palette.background_color.blue as f64, ); self.context.rectangle(0., 0., width, height); self.context.fill(); // configure line self.context.set_source_rgb( self.palette.fill_color.red as f64, self.palette.fill_color.green as f64, self.palette.fill_color.blue as f64, ); self.context.set_line_width(line_size); } /// plot a path fn plot_path(&self, path: &Vec<[f64; 2]>, fill: bool) { if fill { self.put_path_on_stack(path); self.context.fill() } else { // fill with background self.context.set_source_rgb( self.palette.background_color.red as f64, self.palette.background_color.green as f64, self.palette.background_color.blue as f64, ); self.put_path_on_stack(path); self.context.fill(); // set drawing color back to normal self.context.set_source_rgb( self.palette.fill_color.red as f64, self.palette.fill_color.green as f64, self.palette.fill_color.blue as f64, ); self.put_path_on_stack(path); self.context.stroke(); } } }