195 lines
6.3 KiB
Rust
195 lines
6.3 KiB
Rust
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<dyn RenderContainer>,
|
|
pub height: f64,
|
|
pub width: f64,
|
|
pub line_size: f64,
|
|
pub pseudo_random_number_generator: Box<StdRng>,
|
|
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<f64> {
|
|
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<f64>) {
|
|
for polygon in multi_polygon.iter() {
|
|
self.draw_polygon(polygon);
|
|
}
|
|
}
|
|
|
|
pub fn draw_polygon(&self, polygon: &Polygon<f64>) {
|
|
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<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();
|
|
}
|
|
|
|
pub fn draw_line_strings(&self, line_strings: &Vec<LineString<f64>>) {
|
|
for lines in line_strings {
|
|
self.draw_line_string(&lines);
|
|
}
|
|
}
|
|
|
|
pub fn draw_multiline_string(&self, multilinestring: &MultiLineString<f64>) {
|
|
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<dyn RenderContainer> = 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<dyn RenderContainer>
|
|
} 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<dyn RenderContainer>
|
|
} 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,
|
|
}
|
|
}
|
|
}
|