use kurbo::BezPath; use kurbo::Vec2; use super::style; use super::LayerData; use serde::{Deserialize, Serialize}; use std::fmt::Write; #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] pub struct Shape { equal_sides: bool, sides: u8, } impl Shape { pub fn new(equal_sides: bool, sides: u8) -> Shape { Shape { equal_sides, sides } } } impl LayerData for Shape { fn to_kurbo_path(&mut self, transform: glam::DAffine2, _style: style::PathStyle) -> BezPath { fn unit_rotation(theta: f64) -> Vec2 { Vec2::new(-theta.sin(), theta.cos()) } let extent = Vec2::new((transform.x_axis.x + transform.x_axis.y) / 2., (transform.y_axis.x + transform.y_axis.y) / 2.); let translation = transform.translation; let mut path = kurbo::BezPath::new(); let apothem_offset_angle = std::f64::consts::PI / (self.sides as f64); let relative_points = (0..self.sides) .map(|i| apothem_offset_angle * ((i * 2 + ((self.sides + 1) % 2)) as f64)) .map(|radians| unit_rotation(radians)); let (mut min_x, mut min_y, mut max_x, mut max_y) = (f64::MAX, f64::MAX, f64::MIN, f64::MIN); relative_points.clone().for_each(|p| { min_x = min_x.min(p.x); min_y = min_y.min(p.y); max_x = max_x.max(p.x); max_y = max_y.max(p.y); }); relative_points .map(|p| { if self.equal_sides { p } else { Vec2::new((p.x - min_x) / (max_x - min_x) * 2. - 1., (p.y - min_y) / (max_y - min_y) * 2. - 1.) } }) .map(|unit| Vec2::new(-unit.x * extent.x + translation.x + extent.x, -unit.y * extent.y + translation.y + extent.y)) .map(|pos| (pos).to_point()) .enumerate() .for_each(|(i, p)| { if i == 0 { path.move_to(p); } else { path.line_to(p); } }); path.close_path(); path } fn render(&mut self, svg: &mut String, transform: glam::DAffine2, style: style::PathStyle) { let _ = write!(svg, r#""#, self.to_kurbo_path(transform, style).to_svg(), style.render()); } }