use super::SVGUnit; use derive_more::From; use lazy_static::lazy_static; use regex::Regex; use std::cmp::Ordering; use std::fmt; use std::num::ParseFloatError; use std::str::FromStr; use std::{ fmt::Display, ops::{Add, Div, Mul, Rem, Sub}, }; use strum::ParseError; #[derive(Debug, Clone, Copy)] pub struct SVGMeasure { pub measure: f64, pub unit: SVGUnit, } impl SVGMeasure { pub const fn new(measure: f64, unit: SVGUnit) -> Self { Self { measure, unit } } pub fn to_user_units(self) -> f64 { self.measure * self.unit.to_user_units() } pub fn to_unit(self, unit: SVGUnit) -> Self { SVGMeasure { measure: self.to_user_units() / unit.to_user_units(), unit, } } } #[derive(From, Debug)] pub enum SVGUnitParseError { ParseMeasure(ParseFloatError), ParseUnit(ParseError), Invalid, } impl FromStr for SVGMeasure { type Err = SVGUnitParseError; fn from_str(s: &str) -> Result { if s == "0" { return Ok(SVGMeasure::new(0.0, SVGUnit::Pixel)); } lazy_static! { static ref RE: Regex = Regex::new(r"^([\d.]+)([a-zA-Z]+)$").unwrap(); } if let Some(captures) = RE.captures(s) { let measure = captures[1].parse::()?; let unit = captures[2].parse::()?; Ok(SVGMeasure::new(measure, unit)) } else { Err(SVGUnitParseError::Invalid) } } } const EQ_TOLERANCE: f64 = 0.00001; impl PartialEq for SVGMeasure { fn eq(&self, other: &Self) -> bool { (self.to_user_units() - other.to_user_units()).abs() < EQ_TOLERANCE } } impl PartialOrd for SVGMeasure { fn partial_cmp(&self, other: &Self) -> Option { Some(if self == other { Ordering::Equal } else if self.to_user_units() > other.to_user_units() { Ordering::Greater } else { Ordering::Less }) } } impl Display for SVGMeasure { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}{}", self.measure, self.unit) } } impl Add for SVGMeasure { type Output = Self; fn add(mut self, other: Self) -> Self { self.measure += other.to_unit(self.unit).measure; self } } impl Sub for SVGMeasure { type Output = Self; fn sub(mut self, other: Self) -> Self { self.measure -= other.to_unit(self.unit).measure; self } } impl Mul for SVGMeasure { type Output = Self; fn mul(mut self, scalar: f64) -> Self { self.measure *= scalar; self } } impl Div for SVGMeasure { type Output = f64; fn div(self, other: Self) -> Self::Output { self.measure / other.to_unit(self.unit).measure } } impl Div for SVGMeasure { type Output = Self; fn div(mut self, scalar: f64) -> Self { self.measure /= scalar; self } } impl Rem for SVGMeasure { type Output = Self; fn rem(mut self, other: Self) -> Self::Output { self.measure %= other.to_unit(self.unit).measure; self } }