generated from ElnuDev/rust-project
Move svg-units and dyesub into separate crates
This commit is contained in:
parent
18c1e84b79
commit
fb10870a3b
14 changed files with 59 additions and 13 deletions
13
svg-units/Cargo.toml
Normal file
13
svg-units/Cargo.toml
Normal 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
8
svg-units/src/lib.rs
Normal 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
141
svg-units/src/measure.rs
Normal 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
66
svg-units/src/tests.rs
Normal 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
38
svg-units/src/unit.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue