#![feature(generic_const_exprs)] #![feature(let_chains)] use std::fs::File; use std::path::PathBuf; use anyhow::Context; use crate::cli::cli::OutputFormat; use crate::system::model::HistoryLine; use crate::system::spaces::square_grid::Grid2D; use clap::Parser; use colorous::Color; use itertools::Itertools; use num_traits::real::Real; use num_traits::Signed; use serde::de::DeserializeOwned; use serde::Deserialize; use svg::Node; use svg::node::element::{Circle, Polygon, Rectangle}; use crate::{RenderCli, Space}; use crate::system::{GriddedPosition, Position}; use crate::system::spaces::continuous_2d::P2; use crate::system::spaces::hexagonal::HexPosition; use crate::tools::read; #[derive(Debug, Parser)] struct Args { format: OutputFormat, path: PathBuf, output: PathBuf, } trait ToSvg { fn to_svg(&self, colour: Color) -> Box; } impl ToSvg for Grid2D { fn to_svg(&self, colour: Color) -> Box { Box::new(Rectangle::new() .set("fill", format!("rgb({}, {}, {})", colour.r, colour.g, colour.b)) .set("width", 1) .set("height", 1) .set("x", self.x) .set("y", self.y)) } } impl ToSvg for P2 { fn to_svg(&self, colour: Color) -> Box { Box::new(Circle::new() .set("fill", format!("rgb({}, {}, {})", colour.r, colour.g, colour.b)) .set("r", 1) .set("cx", self.x) .set("cy", self.y)) } } impl ToSvg for HexPosition { fn to_svg(&self, colour: Color) -> Box { let points = [ [25.045, 128.0], [256.0, 0.0], [486.955, 128.0], [486.955, 384.0], [256.0, 512.0], [25.045, 384.0] ]; let a = 256.0; // let a = 400.0; let b = points.map(|x| [ (x[0] / a), (x[1] / a)] ); let c = b.map(|p| format!("{},{}", p[0], p[1])).join(" "); let cartesian = self.to_cartesian(); Box::new(Polygon::new() .set("fill", format!("rgb({}, {}, {})", colour.r, colour.g, colour.b)) .set("points", c) .set("transform", format!("translate({}, {})", cartesian[0], cartesian[1]))) } } pub fn compute_max_size(positions: &[P]) -> f32 { let mut positions = positions.iter().map(P::to_cartesian); let mut maximums: Vec = positions.next().unwrap(); for cartesian in positions { for i in 0..maximums.len() { maximums[i] = maximums[i].abs().max(cartesian[i].abs()); } } maximums.into_iter() .fold(0f32, |r, c| r.abs().max(c.abs())) } fn render(args: &RenderCli) where P: DeserializeOwned + ToSvg { let positions = read::

(&args.path, args.format); let max_size = compute_max_size(&positions) + 10.0; let mut svg = svg::Document::new() .set("width", args.image_size) .set("height", args.image_size) .set("viewBox", format!("{} {} {} {}", -max_size, -max_size, max_size * 2.0, max_size * 2.0)); svg.append(Rectangle::new() .set("fill", "white") .set("width", max_size * 2.0) .set("height", max_size * 2.0) .set("x", -max_size) .set("y", -max_size) ); for (n, position) in positions.iter().enumerate() { let colour = if args.colour { colorous::VIRIDIS.eval_rational(n, positions.len()) } else { Color::default() }; svg.append(position.to_svg(colour)); } svg::write(File::create(&args.output).unwrap(), &svg).unwrap(); } pub(crate) fn main(args: &RenderCli) { match args.space { Space::Grid2D => render::(args), Space::Continuous2D => render::(args), Space::Hex => render::(args), } }