From 053066d2044bc6329cfcbe5301cf0b8c673728e6 Mon Sep 17 00:00:00 2001 From: ElnuDev Date: Mon, 14 Aug 2023 11:31:09 -0700 Subject: [PATCH] Implement unit conversions and equality --- dyesub-tool/src/svg/measure.rs | 44 ++++++++++++++++++++++++++------ dyesub-tool/src/svg/mod.rs | 3 +++ dyesub-tool/src/svg/tests.rs | 46 ++++++++++++++++++++++++++++++++++ dyesub-tool/src/svg/unit.rs | 39 +++++++++++----------------- 4 files changed, 100 insertions(+), 32 deletions(-) create mode 100644 dyesub-tool/src/svg/tests.rs diff --git a/dyesub-tool/src/svg/measure.rs b/dyesub-tool/src/svg/measure.rs index f16b4b4..4a63dd0 100644 --- a/dyesub-tool/src/svg/measure.rs +++ b/dyesub-tool/src/svg/measure.rs @@ -5,6 +5,7 @@ use std::{ ops::{Add, Div, Mul, Sub}, }; +#[derive(Debug, Clone, Copy)] pub struct SVGMeasure { pub measure: f64, pub unit: SVGUnit, @@ -14,6 +15,29 @@ impl SVGMeasure { pub fn new(measure: f64, unit: SVGUnit) -> Self { Self { measure, unit } } + + fn to_user_units(&self) -> f64 { + self.measure * self.unit.to_user_units() + } + + fn to_unit(&self, unit: SVGUnit) -> Self { + SVGMeasure { + measure: self.to_user_units() / unit.to_user_units(), + unit, + } + } +} + +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 + } + + fn ne(&self, other: &Self) -> bool { + (self.to_user_units() - other.to_user_units()).abs() > EQ_TOLERANCE + } } impl Display for SVGMeasure { @@ -25,31 +49,35 @@ impl Display for SVGMeasure { impl Add for SVGMeasure { type Output = Self; - fn add(self, other: Self) -> Self { - SVGMeasure::new(self.measure + other.measure, self.unit) + 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(self, other: Self) -> Self { - SVGMeasure::new(self.measure - other.measure, self.unit) + 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(self, scalar: f64) -> Self { - SVGMeasure::new(self.measure * scalar, self.unit) + fn mul(mut self, scalar: f64) -> Self { + self.measure *= scalar; + self } } impl Div for SVGMeasure { type Output = Self; - fn div(self, scalar: f64) -> Self { - SVGMeasure::new(self.measure / scalar, self.unit) + fn div(mut self, scalar: f64) -> Self { + self.measure /= scalar; + self } } diff --git a/dyesub-tool/src/svg/mod.rs b/dyesub-tool/src/svg/mod.rs index 2c4d0fd..979adce 100644 --- a/dyesub-tool/src/svg/mod.rs +++ b/dyesub-tool/src/svg/mod.rs @@ -3,3 +3,6 @@ pub use unit::SVGUnit; mod measure; pub use measure::SVGMeasure; + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/dyesub-tool/src/svg/tests.rs b/dyesub-tool/src/svg/tests.rs new file mode 100644 index 0000000..81ec57e --- /dev/null +++ b/dyesub-tool/src/svg/tests.rs @@ -0,0 +1,46 @@ +use crate::svg::{SVGMeasure, SVGUnit}; + +#[test] +fn add() { + let m_10cm = SVGMeasure::new(10.0, SVGUnit::Centimeter); + let m_5mm = SVGMeasure::new(5.0, SVGUnit::Millimeter); + assert_eq!(m_10cm + m_5mm, SVGMeasure::new(10.5, SVGUnit::Centimeter)); + assert_eq!(m_5mm + m_10cm, SVGMeasure::new(105.0, SVGUnit::Millimeter)); +} + +#[test] +fn sub() { + let m_10cm = SVGMeasure::new(10.0, SVGUnit::Centimeter); + let m_5mm = SVGMeasure::new(5.0, SVGUnit::Millimeter); + assert_eq!(m_10cm - m_5mm, SVGMeasure::new(9.5, SVGUnit::Centimeter)); + assert_eq!(m_5mm - m_10cm, SVGMeasure::new(-95.0, SVGUnit::Millimeter)); +} + +#[test] +fn mul() { + let m_10cm = SVGMeasure::new(10.0, SVGUnit::Centimeter); + assert_eq!(m_10cm * 5.0, SVGMeasure::new(50.0, SVGUnit::Centimeter)); +} + +#[test] +fn div() { + let m_10cm = SVGMeasure::new(10.0, SVGUnit::Centimeter); + assert_eq!(m_10cm / 5.0, SVGMeasure::new(2.0, SVGUnit::Centimeter)); +} + +#[test] +fn eq() { + let m_1in = SVGMeasure::new(1.0, SVGUnit::Inch); + assert_eq!(m_1in, m_1in); + let m_1cm = SVGMeasure::new(1.0, SVGUnit::Centimeter); + let m_10mm = SVGMeasure::new(10.0, SVGUnit::Millimeter); + assert_eq!(m_1cm, m_10mm); +} + +#[test] +fn ne() { + // Testing negative difference, there should be absolute value in PartialEq impl + let a = SVGMeasure::new(1.0, SVGUnit::Pixel); + let b = SVGMeasure::new(10.0, SVGUnit::Pixel); + assert_ne!(a, b); +} \ No newline at end of file diff --git a/dyesub-tool/src/svg/unit.rs b/dyesub-tool/src/svg/unit.rs index da85208..9c67baf 100644 --- a/dyesub-tool/src/svg/unit.rs +++ b/dyesub-tool/src/svg/unit.rs @@ -1,6 +1,7 @@ use std::fmt::{self, Display}; /// https://oreillymedia.github.io/Using_SVG/guide/units.html +#[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum SVGUnit { /// Pixel units, directly equivalent to SVG user units. Pixel, @@ -14,22 +15,20 @@ pub enum SVGUnit { Point, // Picas. Pica, - /// Em units. Equivalent to the computed font-size in effect for an element. - Em, - /// Ex units. Equivalent to the height of a lower-case letter in the font (and font-size) in effect for an element. If the font doesn’t include lower-case letters, or doesn’t include the metadata about the ex-height, then 1ex = 0.5em. - Ex, - /// Character units. Equivalent to the width of the `0` (zero) character in the font and font-size in effect for an element. - Character, - /// Root-em units. The font size of the root element in the document, regardless of any settings for the current element. - Rem, - /// Viewport width units. 1% of the viewport width. - ViewportWidth, - /// Viewport height units. 1% of the viewport height. - ViewportHeight, - /// Viewport minimum units. 1% of the viewport width or height, whichever is smaller. - ViewportMinimum, - /// Viewport maximum units. 1% of the viewport width or height, whichever is larger. - ViewportMaximum, +} + +impl SVGUnit { + pub fn to_user_units(&self) -> f64 { + use SVGUnit::*; + match self { + Pixel => 1.0, + Inch => 96.0, + Centimeter => 37.795, + Millimeter => 3.7795, + Point => 4.0 / 3.0, // 1.3333 + Pica => 16.0, + } + } } impl Display for SVGUnit { @@ -45,14 +44,6 @@ impl Display for SVGUnit { Millimeter => "mm", Point => "pt", Pica => "pc", - Em => "em", - Ex => "ex", - Character => "ch", - Rem => "rem", - ViewportWidth => "vw", - ViewportHeight => "vh", - ViewportMinimum => "vmin", - ViewportMaximum => "vmax", } ) }