Implement unit conversions and equality

main
Elnu 1 year ago
parent 63f6b61f91
commit 053066d204

@ -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<f64> 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<f64> 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
}
}

@ -3,3 +3,6 @@ pub use unit::SVGUnit;
mod measure;
pub use measure::SVGMeasure;
#[cfg(test)]
mod tests;

@ -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);
}

@ -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 doesnt include lower-case letters, or doesnt 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",
}
)
}

Loading…
Cancel
Save