use crate::color::Color; use serde::{Deserialize, Serialize}; const OPACITY_PERCISION: usize = 3; fn format_opacity(name: &str, opacity: f32) -> String { if (opacity - 1.).abs() > 10f32.powi(-(OPACITY_PERCISION as i32)) { format!(r#" {}-opacity="{:.percision$}""#, name, opacity, percision = OPACITY_PERCISION) } else { String::new() } } #[repr(C)] #[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)] pub struct Fill { color: Option, } impl Fill { pub fn new(color: Color) -> Self { Self { color: Some(color) } } pub fn none() -> Self { Self { color: None } } pub fn render(&self) -> String { match self.color { Some(c) => format!(r##" fill="#{}"{}"##, c.rgb_hex(), format_opacity("fill", c.a())), None => r#" fill="none""#.to_string(), } } } #[repr(C)] #[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)] pub struct Stroke { color: Color, width: f32, } impl Stroke { pub fn new(color: Color, width: f32) -> Self { Self { color, width } } pub fn render(&self) -> String { format!(r##" stroke="#{}"{} stroke-width="{}""##, self.color.rgb_hex(), format_opacity("stroke", self.color.a()), self.width) } } #[repr(C)] #[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)] pub struct PathStyle { stroke: Option, fill: Option, } impl PathStyle { pub fn new(stroke: Option, fill: Option) -> Self { Self { stroke, fill } } pub fn render(&self) -> String { format!( "{}{}", match self.fill { Some(fill) => fill.render(), None => String::new(), }, match self.stroke { Some(stroke) => stroke.render(), None => String::new(), }, ) } }