diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-04-07 18:04:29 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-04-07 18:04:29 +0200 |
| commit | 4bb6240b401605ef6d905273db07545e14f9a21f (patch) | |
| tree | b01163a5fce3fe62d16abcbdabf37bc373617ff1 /src/geom | |
| parent | 1192132dc0a9e991953fd29e93f87c8437a53ea0 (diff) | |
Make `Relative` generic
Diffstat (limited to 'src/geom')
| -rw-r--r-- | src/geom/angle.rs | 45 | ||||
| -rw-r--r-- | src/geom/em.rs | 17 | ||||
| -rw-r--r-- | src/geom/fraction.rs | 15 | ||||
| -rw-r--r-- | src/geom/length.rs | 61 | ||||
| -rw-r--r-- | src/geom/mod.rs | 24 | ||||
| -rw-r--r-- | src/geom/paint.rs | 36 | ||||
| -rw-r--r-- | src/geom/point.rs | 21 | ||||
| -rw-r--r-- | src/geom/ratio.rs | 16 | ||||
| -rw-r--r-- | src/geom/relative.rs | 158 | ||||
| -rw-r--r-- | src/geom/scalar.rs | 10 | ||||
| -rw-r--r-- | src/geom/sides.rs | 2 | ||||
| -rw-r--r-- | src/geom/spec.rs | 25 | ||||
| -rw-r--r-- | src/geom/transform.rs | 10 |
13 files changed, 230 insertions, 210 deletions
diff --git a/src/geom/angle.rs b/src/geom/angle.rs index b64ec77e..a0900ce5 100644 --- a/src/geom/angle.rs +++ b/src/geom/angle.rs @@ -10,6 +10,16 @@ impl Angle { Self(Scalar(0.0)) } + /// Create an angle from a number of raw units. + pub const fn raw(raw: f64) -> Self { + Self(Scalar(raw)) + } + + /// Create an angle from a value in a unit. + pub fn with_unit(val: f64, unit: AngularUnit) -> Self { + Self(Scalar(val * unit.raw_scale())) + } + /// Create an angle from a number of radians. pub fn rad(rad: f64) -> Self { Self::with_unit(rad, AngularUnit::Rad) @@ -20,9 +30,14 @@ impl Angle { Self::with_unit(deg, AngularUnit::Deg) } - /// Create an angle from a number of raw units. - pub const fn raw(raw: f64) -> Self { - Self(Scalar(raw)) + /// Get the value of this angle in raw units. + pub const fn to_raw(self) -> f64 { + (self.0).0 + } + + /// Get the value of this length in unit. + pub fn to_unit(self, unit: AngularUnit) -> f64 { + self.to_raw() / unit.raw_scale() } /// Convert this to a number of radians. @@ -35,9 +50,9 @@ impl Angle { self.to_unit(AngularUnit::Deg) } - /// Get the value of this angle in raw units. - pub const fn to_raw(self) -> f64 { - (self.0).0 + /// The absolute value of the this angle. + pub fn abs(self) -> Self { + Self::raw(self.to_raw().abs()) } /// Get the sine of this angle in radians. @@ -49,20 +64,15 @@ impl Angle { pub fn cos(self) -> f64 { self.to_rad().cos() } +} - /// Create an angle from a value in a unit. - pub fn with_unit(val: f64, unit: AngularUnit) -> Self { - Self(Scalar(val * unit.raw_scale())) - } - - /// Get the value of this length in unit. - pub fn to_unit(self, unit: AngularUnit) -> f64 { - self.to_raw() / unit.raw_scale() +impl Numeric for Angle { + fn zero() -> Self { + Self::zero() } - /// The absolute value of the this angle. - pub fn abs(self) -> Self { - Self::raw(self.to_raw().abs()) + fn is_finite(self) -> bool { + self.0.is_finite() } } @@ -132,6 +142,7 @@ impl Sum for Angle { Self(iter.map(|s| s.0).sum()) } } + /// Different units of angular measurement. #[derive(Copy, Clone, Eq, PartialEq, Hash)] pub enum AngularUnit { diff --git a/src/geom/em.rs b/src/geom/em.rs index d1e90e04..3c772d96 100644 --- a/src/geom/em.rs +++ b/src/geom/em.rs @@ -32,19 +32,24 @@ impl Em { Self(Scalar(length / font_size)) } + /// The number of em units. + pub const fn get(self) -> f64 { + (self.0).0 + } + /// Convert to a length at the given font size. pub fn resolve(self, font_size: Length) -> Length { self.get() * font_size } +} - /// The number of em units. - pub const fn get(self) -> f64 { - (self.0).0 +impl Numeric for Em { + fn zero() -> Self { + Self::zero() } - /// Whether the length is zero. - pub fn is_zero(self) -> bool { - self.0 == 0.0 + fn is_finite(self) -> bool { + self.0.is_finite() } } diff --git a/src/geom/fraction.rs b/src/geom/fraction.rs index 7da85779..2f33a134 100644 --- a/src/geom/fraction.rs +++ b/src/geom/fraction.rs @@ -25,11 +25,6 @@ impl Fraction { (self.0).0 } - /// Whether the fraction is zero. - pub fn is_zero(self) -> bool { - self.0 == 0.0 - } - /// The absolute value of the this fraction. pub fn abs(self) -> Self { Self::new(self.get().abs()) @@ -46,6 +41,16 @@ impl Fraction { } } +impl Numeric for Fraction { + fn zero() -> Self { + Self::zero() + } + + fn is_finite(self) -> bool { + self.0.is_finite() + } +} + impl Debug for Fraction { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "{}fr", round_2(self.get())) diff --git a/src/geom/length.rs b/src/geom/length.rs index c5fbfd76..838d33c0 100644 --- a/src/geom/length.rs +++ b/src/geom/length.rs @@ -15,6 +15,16 @@ impl Length { Self(Scalar(f64::INFINITY)) } + /// Create a length from a number of raw units. + pub const fn raw(raw: f64) -> Self { + Self(Scalar(raw)) + } + + /// Create a length from a value in a unit. + pub fn with_unit(val: f64, unit: LengthUnit) -> Self { + Self(Scalar(val * unit.raw_scale())) + } + /// Create a length from a number of points. pub fn pt(pt: f64) -> Self { Self::with_unit(pt, LengthUnit::Pt) @@ -35,9 +45,14 @@ impl Length { Self::with_unit(inches, LengthUnit::In) } - /// Create a length from a number of raw units. - pub const fn raw(raw: f64) -> Self { - Self(Scalar(raw)) + /// Get the value of this length in raw units. + pub const fn to_raw(self) -> f64 { + (self.0).0 + } + + /// Get the value of this length in unit. + pub fn to_unit(self, unit: LengthUnit) -> f64 { + self.to_raw() / unit.raw_scale() } /// Convert this to a number of points. @@ -60,36 +75,6 @@ impl Length { self.to_unit(LengthUnit::In) } - /// Get the value of this length in raw units. - pub const fn to_raw(self) -> f64 { - (self.0).0 - } - - /// Create a length from a value in a unit. - pub fn with_unit(val: f64, unit: LengthUnit) -> Self { - Self(Scalar(val * unit.raw_scale())) - } - - /// Get the value of this length in unit. - pub fn to_unit(self, unit: LengthUnit) -> f64 { - self.to_raw() / unit.raw_scale() - } - - /// Whether the length is zero. - pub fn is_zero(self) -> bool { - self.to_raw() == 0.0 - } - - /// Whether the length is finite. - pub fn is_finite(self) -> bool { - self.to_raw().is_finite() - } - - /// Whether the length is infinite. - pub fn is_infinite(self) -> bool { - self.to_raw().is_infinite() - } - /// The absolute value of the this length. pub fn abs(self) -> Self { Self::raw(self.to_raw().abs()) @@ -137,6 +122,16 @@ impl Length { } } +impl Numeric for Length { + fn zero() -> Self { + Self::zero() + } + + fn is_finite(self) -> bool { + self.0.is_finite() + } +} + impl Debug for Length { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "{}pt", round_2(self.to_pt())) diff --git a/src/geom/mod.rs b/src/geom/mod.rs index 8d7759b4..bfb450a1 100644 --- a/src/geom/mod.rs +++ b/src/geom/mod.rs @@ -60,6 +60,30 @@ pub trait Get<Index> { } } +/// A numeric type. +pub trait Numeric: + Sized + + Debug + + Copy + + PartialEq + + Neg<Output = Self> + + Add<Output = Self> + + Sub<Output = Self> + + Mul<f64, Output = Self> + + Div<f64, Output = Self> +{ + /// The identity element. + fn zero() -> Self; + + /// Whether `self` is the identity element. + fn is_zero(self) -> bool { + self == Self::zero() + } + + /// Whether `self` contains only finite parts. + fn is_finite(self) -> bool; +} + /// Round a float to two decimal places. fn round_2(value: f64) -> f64 { (value * 100.0).round() / 100.0 diff --git a/src/geom/paint.rs b/src/geom/paint.rs index 8dee363c..3660d528 100644 --- a/src/geom/paint.rs +++ b/src/geom/paint.rs @@ -1,4 +1,3 @@ -use std::fmt::Display; use std::str::FromStr; use syntect::highlighting::Color as SynColor; @@ -103,7 +102,7 @@ impl RgbaColor { } impl FromStr for RgbaColor { - type Err = RgbaError; + type Err = &'static str; /// Constructs a new color from hex strings like the following: /// - `#aef` (shorthand, with leading hashtag), @@ -113,8 +112,8 @@ impl FromStr for RgbaColor { /// The hashtag is optional and both lower and upper case are fine. fn from_str(hex_str: &str) -> Result<Self, Self::Err> { let hex_str = hex_str.strip_prefix('#').unwrap_or(hex_str); - if !hex_str.is_ascii() { - return Err(RgbaError); + if hex_str.chars().any(|c| !c.is_ascii_hexdigit()) { + return Err("string contains non-hexadecimal letters"); } let len = hex_str.len(); @@ -123,7 +122,7 @@ impl FromStr for RgbaColor { let alpha = len == 4 || len == 8; if !long && !short { - return Err(RgbaError); + return Err("string has wrong length"); } let mut values: [u8; 4] = [255; 4]; @@ -133,7 +132,7 @@ impl FromStr for RgbaColor { let pos = elem * item_len; let item = &hex_str[pos .. (pos + item_len)]; - values[elem] = u8::from_str_radix(item, 16).map_err(|_| RgbaError)?; + values[elem] = u8::from_str_radix(item, 16).unwrap(); if short { // Duplicate number for shorthand notation, i.e. `a` -> `aa` @@ -169,18 +168,6 @@ impl Debug for RgbaColor { } } -/// The error when parsing an [`RgbaColor`] from a string fails. -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct RgbaError; - -impl Display for RgbaError { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.pad("invalid hex string") - } -} - -impl std::error::Error for RgbaError {} - /// An 8-bit CMYK color. #[derive(Copy, Clone, Eq, PartialEq, Hash)] pub struct CmykColor { @@ -268,13 +255,14 @@ mod tests { #[test] fn test_parse_invalid_colors() { #[track_caller] - fn test(hex: &str) { - assert_eq!(RgbaColor::from_str(hex), Err(RgbaError)); + fn test(hex: &str, message: &str) { + assert_eq!(RgbaColor::from_str(hex), Err(message)); } - test("12345"); - test("a5"); - test("14B2AH"); - test("f075ff011"); + test("a5", "string has wrong length"); + test("12345", "string has wrong length"); + test("f075ff011", "string has wrong length"); + test("hmmm", "string contains non-hexadecimal letters"); + test("14B2AH", "string contains non-hexadecimal letters"); } } diff --git a/src/geom/point.rs b/src/geom/point.rs index 6d77507b..afce68ba 100644 --- a/src/geom/point.rs +++ b/src/geom/point.rs @@ -35,20 +35,25 @@ impl Point { Self { x: Length::zero(), y } } - /// Whether both components are zero. - pub fn is_zero(self) -> bool { - self.x.is_zero() && self.y.is_zero() - } - /// Transform the point with the given transformation. - pub fn transform(self, transform: Transform) -> Self { + pub fn transform(self, ts: Transform) -> Self { Self::new( - transform.sx.resolve(self.x) + transform.kx.resolve(self.y) + transform.tx, - transform.ky.resolve(self.x) + transform.sy.resolve(self.y) + transform.ty, + ts.sx.resolve(self.x) + ts.kx.resolve(self.y) + ts.tx, + ts.ky.resolve(self.x) + ts.sy.resolve(self.y) + ts.ty, ) } } +impl Numeric for Point { + fn zero() -> Self { + Self::zero() + } + + fn is_finite(self) -> bool { + self.x.is_finite() && self.y.is_finite() + } +} + impl Get<SpecAxis> for Point { type Component = Length; diff --git a/src/geom/ratio.rs b/src/geom/ratio.rs index d035c33e..7dca53c2 100644 --- a/src/geom/ratio.rs +++ b/src/geom/ratio.rs @@ -28,16 +28,6 @@ impl Ratio { (self.0).0 } - /// Resolve this relative to the given `length`. - pub fn resolve(self, length: Length) -> Length { - // We don't want NaNs. - if length.is_infinite() { - Length::zero() - } else { - self.get() * length - } - } - /// Whether the ratio is zero. pub fn is_zero(self) -> bool { self.0 == 0.0 @@ -52,6 +42,12 @@ impl Ratio { pub fn abs(self) -> Self { Self::new(self.get().abs()) } + + /// Resolve this relative to the given `whole`. + pub fn resolve<T: Numeric>(self, whole: T) -> T { + let resolved = whole * self.get(); + if resolved.is_finite() { resolved } else { T::zero() } + } } impl Debug for Ratio { diff --git a/src/geom/relative.rs b/src/geom/relative.rs index fa2ec7fc..8e8897e7 100644 --- a/src/geom/relative.rs +++ b/src/geom/relative.rs @@ -1,65 +1,60 @@ use super::*; -/// A relative length. +/// A value that is composed of a relative and an absolute part. #[derive(Default, Copy, Clone, Eq, PartialEq, Hash)] -pub struct Relative { +pub struct Relative<T: Numeric> { /// The relative part. pub rel: Ratio, /// The absolute part. - pub abs: Length, + pub abs: T, } -impl Relative { - /// The zero relative length. - pub const fn zero() -> Self { - Self { rel: Ratio::zero(), abs: Length::zero() } +impl<T: Numeric> Relative<T> { + /// The zero relative. + pub fn zero() -> Self { + Self { rel: Ratio::zero(), abs: T::zero() } } - /// A relative length with a ratio of `100%` and no absolute part. - pub const fn one() -> Self { - Self { rel: Ratio::one(), abs: Length::zero() } + /// A relative with a ratio of `100%` and no absolute part. + pub fn one() -> Self { + Self { rel: Ratio::one(), abs: T::zero() } } - /// Create a new relative length from its parts. - pub const fn new(rel: Ratio, abs: Length) -> Self { + /// Create a new relative from its parts. + pub fn new(rel: Ratio, abs: T) -> Self { Self { rel, abs } } - /// Resolve this length relative to the given `length`. - pub fn resolve(self, length: Length) -> Length { - self.rel.resolve(length) + self.abs - } - /// Whether both parts are zero. pub fn is_zero(self) -> bool { self.rel.is_zero() && self.abs.is_zero() } - /// Whether there is a relative part. - pub fn is_relative(self) -> bool { - !self.rel.is_zero() + /// Resolve this relative to the given `whole`. + pub fn resolve(self, whole: T) -> T { + self.rel.resolve(whole) + self.abs } } -impl Debug for Relative { +impl<T: Numeric> Debug for Relative<T> { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "{:?} + {:?}", self.rel, self.abs) } } -impl From<Length> for Relative { - fn from(abs: Length) -> Self { +impl<T: Numeric> From<T> for Relative<T> { + fn from(abs: T) -> Self { Self { rel: Ratio::zero(), abs } } } -impl From<Ratio> for Relative { +impl<T: Numeric> From<Ratio> for Relative<T> { fn from(rel: Ratio) -> Self { - Self { rel, abs: Length::zero() } + Self { rel, abs: T::zero() } } } -impl Neg for Relative { +impl<T: Numeric> Neg for Relative<T> { type Output = Self; fn neg(self) -> Self { @@ -67,10 +62,10 @@ impl Neg for Relative { } } -impl Add for Relative { +impl<T: Numeric> Add for Relative<T> { type Output = Self; - fn add(self, other: Self) -> Self { + fn add(self, other: Self) -> Self::Output { Self { rel: self.rel + other.rel, abs: self.abs + other.abs, @@ -78,97 +73,88 @@ impl Add for Relative { } } -impl Add<Ratio> for Length { - type Output = Relative; +impl<T: Numeric> Sub for Relative<T> { + type Output = Self; - fn add(self, other: Ratio) -> Relative { - Relative { rel: other, abs: self } + fn sub(self, other: Self) -> Self::Output { + self + -other } } -impl Add<Length> for Ratio { - type Output = Relative; +impl<T: Numeric> Mul<f64> for Relative<T> { + type Output = Self; - fn add(self, other: Length) -> Relative { - other + self + fn mul(self, other: f64) -> Self::Output { + Self { + rel: self.rel * other, + abs: self.abs * other, + } } } -impl Add<Length> for Relative { - type Output = Self; +impl<T: Numeric> Mul<Relative<T>> for f64 { + type Output = Relative<T>; - fn add(self, other: Length) -> Self { - Self { rel: self.rel, abs: self.abs + other } + fn mul(self, other: Relative<T>) -> Self::Output { + other * self } } -impl Add<Relative> for Length { - type Output = Relative; +impl<T: Numeric> Div<f64> for Relative<T> { + type Output = Self; - fn add(self, other: Relative) -> Relative { - other + self + fn div(self, other: f64) -> Self::Output { + Self { + rel: self.rel / other, + abs: self.abs / other, + } } } -impl Add<Ratio> for Relative { - type Output = Self; - - fn add(self, other: Ratio) -> Self { - Self { rel: self.rel + other, abs: self.abs } +impl<T: Numeric> AddAssign for Relative<T> { + fn add_assign(&mut self, other: Self) { + *self = *self + other; } } -impl Add<Relative> for Ratio { - type Output = Relative; +impl<T: Numeric> SubAssign for Relative<T> { + fn sub_assign(&mut self, other: Self) { + *self = *self - other; + } +} - fn add(self, other: Relative) -> Relative { - other + self +impl<T: Numeric> MulAssign<f64> for Relative<T> { + fn mul_assign(&mut self, other: f64) { + *self = *self * other; } } -sub_impl!(Relative - Relative -> Relative); -sub_impl!(Length - Ratio -> Relative); -sub_impl!(Ratio - Length -> Relative); -sub_impl!(Relative - Length -> Relative); -sub_impl!(Length - Relative -> Relative); -sub_impl!(Relative - Ratio -> Relative); -sub_impl!(Ratio - Relative -> Relative); +impl<T: Numeric> DivAssign<f64> for Relative<T> { + fn div_assign(&mut self, other: f64) { + *self = *self * other; + } +} -impl Mul<f64> for Relative { - type Output = Self; +impl<T: Numeric> Add<T> for Ratio { + type Output = Relative<T>; - fn mul(self, other: f64) -> Self { - Self { - rel: self.rel * other, - abs: self.abs * other, - } + fn add(self, other: T) -> Self::Output { + Relative::from(self) + Relative::from(other) } } -impl Mul<Relative> for f64 { - type Output = Relative; +impl<T: Numeric> Add<T> for Relative<T> { + type Output = Self; - fn mul(self, other: Relative) -> Relative { - other * self + fn add(self, other: T) -> Self::Output { + self + Relative::from(other) } } -impl Div<f64> for Relative { +impl<T: Numeric> Add<Ratio> for Relative<T> { type Output = Self; - fn div(self, other: f64) -> Self { - Self { - rel: self.rel / other, - abs: self.abs / other, - } + fn add(self, other: Ratio) -> Self::Output { + self + Relative::from(other) } } - -assign_impl!(Relative += Relative); -assign_impl!(Relative += Length); -assign_impl!(Relative += Ratio); -assign_impl!(Relative -= Relative); -assign_impl!(Relative -= Length); -assign_impl!(Relative -= Ratio); -assign_impl!(Relative *= f64); -assign_impl!(Relative /= f64); diff --git a/src/geom/scalar.rs b/src/geom/scalar.rs index 1435654d..91225a2b 100644 --- a/src/geom/scalar.rs +++ b/src/geom/scalar.rs @@ -6,6 +6,16 @@ use super::*; #[derive(Default, Copy, Clone)] pub struct Scalar(pub f64); +impl Numeric for Scalar { + fn zero() -> Self { + Self(0.0) + } + + fn is_finite(self) -> bool { + self.0.is_finite() + } +} + impl From<f64> for Scalar { fn from(float: f64) -> Self { Self(float) diff --git a/src/geom/sides.rs b/src/geom/sides.rs index 45420c18..4539728f 100644 --- a/src/geom/sides.rs +++ b/src/geom/sides.rs @@ -43,7 +43,7 @@ where } } -impl Sides<Relative> { +impl Sides<Relative<Length>> { /// Resolve the sides relative to the given `size`. pub fn resolve(self, size: Size) -> Sides<Length> { Sides { diff --git a/src/geom/spec.rs b/src/geom/spec.rs index 31f93a65..3fe1793a 100644 --- a/src/geom/spec.rs +++ b/src/geom/spec.rs @@ -201,7 +201,7 @@ pub type Size = Spec<Length>; impl Size { /// The zero value. - pub fn zero() -> Self { + pub const fn zero() -> Self { Self { x: Length::zero(), y: Length::zero() } } @@ -210,24 +210,19 @@ impl Size { self.x.fits(other.x) && self.y.fits(other.y) } - /// Whether both components are zero. - pub fn is_zero(self) -> bool { - self.x.is_zero() && self.y.is_zero() - } - - /// Whether both components are finite. - pub fn is_finite(self) -> bool { - self.x.is_finite() && self.y.is_finite() + /// Convert to a point. + pub fn to_point(self) -> Point { + Point::new(self.x, self.y) } +} - /// Whether any of the two components is infinite. - pub fn is_infinite(self) -> bool { - self.x.is_infinite() || self.y.is_infinite() +impl Numeric for Size { + fn zero() -> Self { + Self::zero() } - /// Convert to a point. - pub fn to_point(self) -> Point { - Point::new(self.x, self.y) + fn is_finite(self) -> bool { + self.x.is_finite() && self.y.is_finite() } } diff --git a/src/geom/transform.rs b/src/geom/transform.rs index 8d64ebcf..c0a06e33 100644 --- a/src/geom/transform.rs +++ b/src/geom/transform.rs @@ -24,18 +24,18 @@ impl Transform { } } - /// A translation transform. - pub const fn translation(tx: Length, ty: Length) -> Self { + /// A translate transform. + pub const fn translate(tx: Length, ty: Length) -> Self { Self { tx, ty, ..Self::identity() } } - /// A scaling transform. + /// A scale transform. pub const fn scale(sx: Ratio, sy: Ratio) -> Self { Self { sx, sy, ..Self::identity() } } - /// A rotation transform. - pub fn rotation(angle: Angle) -> Self { + /// A rotate transform. + pub fn rotate(angle: Angle) -> Self { let cos = Ratio::new(angle.cos()); let sin = Ratio::new(angle.sin()); Self { |
