polygon-art/src/context.rs

147 lines
4.5 KiB
Rust

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<dyn RenderContainer>,
pub height: f64,
pub width: f64,
pub line_size: f64,
pub pseudo_random_number_generator: Box<StdRng>,
}
impl Context {
pub fn draw_multipolgon(&self, multi_polygon: &MultiPolygon<f64>) {
for polygon in multi_polygon.iter() {
self.draw_polygon(polygon);
}
}
pub fn draw_polygon(&self, polygon: &Polygon<f64>) {
self.plot_line_string(polygon.exterior());
for interior in polygon.interiors() {
self.plot_line_string(interior);
}
}
fn plot_line_string(&self, line_string: &LineString<f64>) {
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<dyn RenderContainer> = 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<dyn RenderContainer>
} 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<dyn RenderContainer>
};
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,
}
}
}