Move svg-units and dyesub into separate crates

This commit is contained in:
Elnu 2023-08-22 14:56:18 -07:00
parent 18c1e84b79
commit fb10870a3b
14 changed files with 59 additions and 13 deletions

13
svg-units/Cargo.toml Normal file
View file

@ -0,0 +1,13 @@
[package]
name = "svg-units"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
derive_more = "0.99.17"
lazy_static = "1.4.0"
regex = "1.9.3"
strum = "0.25.0"
strum_macros = "0.25.2"

8
svg-units/src/lib.rs Normal file
View file

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

141
svg-units/src/measure.rs Normal file
View file

@ -0,0 +1,141 @@
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<Self, Self::Err> {
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::<f64>()?;
let unit = captures[2].parse::<SVGUnit>()?;
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<Ordering> {
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<f64> 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<f64> 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
}
}

66
svg-units/src/tests.rs Normal file
View file

@ -0,0 +1,66 @@
use std::str::FromStr;
use crate::{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);
}
#[test]
fn cmp() {
let m_10cm = SVGMeasure::new(10.0, SVGUnit::Centimeter);
let m_5mm = SVGMeasure::new(5.0, SVGUnit::Millimeter);
assert!(m_10cm > m_5mm);
assert!(m_5mm < m_10cm);
}
#[test]
fn from_str() {
let m_10cm = SVGMeasure::new(10.0, SVGUnit::Centimeter);
let m_5mm = SVGMeasure::new(5.0, SVGUnit::Millimeter);
let m_0 = SVGMeasure::new(0.0, SVGUnit::Pixel);
assert_eq!(m_10cm, SVGMeasure::from_str("10cm").unwrap());
assert_eq!(m_5mm, SVGMeasure::from_str("5mm").unwrap());
assert_eq!(m_0, SVGMeasure::from_str("0").unwrap());
}

38
svg-units/src/unit.rs Normal file
View file

@ -0,0 +1,38 @@
use strum_macros::{Display, EnumString, IntoStaticStr};
/// https://oreillymedia.github.io/Using_SVG/guide/units.html
#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, EnumString, IntoStaticStr)]
pub enum SVGUnit {
/// Pixel units, directly equivalent to SVG user units.
#[strum(serialize = "px")]
Pixel,
/// Inches.
#[strum(serialize = "in")]
Inch,
/// Centimeters.
#[strum(serialize = "cm")]
Centimeter,
/// Millimeters.
#[strum(serialize = "mm")]
Millimeter,
/// Points.
#[strum(serialize = "pt")]
Point,
// Picas.
#[strum(serialize = "pc")]
Pica,
}
impl SVGUnit {
pub const fn to_user_units(&self) -> f64 {
use SVGUnit::*;
match self {
Pixel => 1.0,
Inch => 96.0,
Centimeter => 37.795,
Millimeter => 3.7795,
Point => 1.3333,
Pica => 16.0,
}
}
}