summaryrefslogtreecommitdiff
path: root/crates/typst-library/src/layout/abs.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2024-10-27 19:04:55 +0100
committerGitHub <noreply@github.com>2024-10-27 18:04:55 +0000
commitbe7cfc85d08c545abfac08098b7b33b4bd71f37e (patch)
treef4137fa2aaa57babae1f7603a9b2ed7e688f43d8 /crates/typst-library/src/layout/abs.rs
parentb8034a343831e8609aec2ec81eb7eeda57aa5d81 (diff)
Split out four new crates (#5302)
Diffstat (limited to 'crates/typst-library/src/layout/abs.rs')
-rw-r--r--crates/typst-library/src/layout/abs.rs279
1 files changed, 279 insertions, 0 deletions
diff --git a/crates/typst-library/src/layout/abs.rs b/crates/typst-library/src/layout/abs.rs
new file mode 100644
index 00000000..e19b037a
--- /dev/null
+++ b/crates/typst-library/src/layout/abs.rs
@@ -0,0 +1,279 @@
+use std::fmt::{self, Debug, Formatter};
+use std::iter::Sum;
+use std::ops::{Add, Div, Mul, Neg, Rem};
+
+use ecow::EcoString;
+use typst_utils::{Numeric, Scalar};
+
+use crate::foundations::{cast, repr, Fold, Repr, Value};
+
+/// An absolute length.
+#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub struct Abs(Scalar);
+
+impl Abs {
+ /// The zero length.
+ pub const fn zero() -> Self {
+ Self(Scalar::ZERO)
+ }
+
+ /// The infinite length.
+ pub const fn inf() -> Self {
+ Self(Scalar::INFINITY)
+ }
+
+ /// Create an absolute length from a number of raw units.
+ pub const fn raw(raw: f64) -> Self {
+ Self(Scalar::new(raw))
+ }
+
+ /// Create an absolute length from a value in a unit.
+ pub fn with_unit(val: f64, unit: AbsUnit) -> Self {
+ Self(Scalar::new(val * unit.raw_scale()))
+ }
+
+ /// Create an absolute length from a number of points.
+ pub fn pt(pt: f64) -> Self {
+ Self::with_unit(pt, AbsUnit::Pt)
+ }
+
+ /// Create an absolute length from a number of millimeters.
+ pub fn mm(mm: f64) -> Self {
+ Self::with_unit(mm, AbsUnit::Mm)
+ }
+
+ /// Create an absolute length from a number of centimeters.
+ pub fn cm(cm: f64) -> Self {
+ Self::with_unit(cm, AbsUnit::Cm)
+ }
+
+ /// Create an absolute length from a number of inches.
+ pub fn inches(inches: f64) -> Self {
+ Self::with_unit(inches, AbsUnit::In)
+ }
+
+ /// Get the value of this absolute length in raw units.
+ pub const fn to_raw(self) -> f64 {
+ self.0.get()
+ }
+
+ /// Get the value of this absolute length in a unit.
+ pub fn to_unit(self, unit: AbsUnit) -> f64 {
+ self.to_raw() / unit.raw_scale()
+ }
+
+ /// Convert this to a number of points.
+ pub fn to_pt(self) -> f64 {
+ self.to_unit(AbsUnit::Pt)
+ }
+
+ /// Convert this to a number of millimeters.
+ pub fn to_mm(self) -> f64 {
+ self.to_unit(AbsUnit::Mm)
+ }
+
+ /// Convert this to a number of centimeters.
+ pub fn to_cm(self) -> f64 {
+ self.to_unit(AbsUnit::Cm)
+ }
+
+ /// Convert this to a number of inches.
+ pub fn to_inches(self) -> f64 {
+ self.to_unit(AbsUnit::In)
+ }
+
+ /// The absolute value of this length.
+ pub fn abs(self) -> Self {
+ Self::raw(self.to_raw().abs())
+ }
+
+ /// The minimum of this and another absolute length.
+ pub fn min(self, other: Self) -> Self {
+ Self(self.0.min(other.0))
+ }
+
+ /// Set to the minimum of this and another absolute length.
+ pub fn set_min(&mut self, other: Self) {
+ *self = (*self).min(other);
+ }
+
+ /// The maximum of this and another absolute length.
+ pub fn max(self, other: Self) -> Self {
+ Self(self.0.max(other.0))
+ }
+
+ /// Set to the maximum of this and another absolute length.
+ pub fn set_max(&mut self, other: Self) {
+ *self = (*self).max(other);
+ }
+
+ /// Whether the other absolute length fits into this one (i.e. is smaller).
+ /// Allows for a bit of slack.
+ pub fn fits(self, other: Self) -> bool {
+ self.0 + AbsUnit::EPS >= other.0
+ }
+
+ /// Compares two absolute lengths for whether they are approximately equal.
+ pub fn approx_eq(self, other: Self) -> bool {
+ self == other || (self - other).to_raw().abs() < AbsUnit::EPS
+ }
+
+ /// Whether the size is close to zero or negative.
+ pub fn approx_empty(self) -> bool {
+ self.to_raw() <= AbsUnit::EPS
+ }
+
+ /// Returns a number that represent the sign of this length
+ pub fn signum(self) -> f64 {
+ self.0.get().signum()
+ }
+}
+
+impl Numeric for Abs {
+ fn zero() -> Self {
+ Self::zero()
+ }
+
+ fn is_finite(self) -> bool {
+ self.0.is_finite()
+ }
+}
+
+impl Debug for Abs {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "{:?}pt", self.to_pt())
+ }
+}
+
+impl Repr for Abs {
+ fn repr(&self) -> EcoString {
+ repr::format_float_with_unit(self.to_pt(), "pt")
+ }
+}
+
+impl Neg for Abs {
+ type Output = Self;
+
+ fn neg(self) -> Self {
+ Self(-self.0)
+ }
+}
+
+impl Add for Abs {
+ type Output = Self;
+
+ fn add(self, other: Self) -> Self {
+ Self(self.0 + other.0)
+ }
+}
+
+typst_utils::sub_impl!(Abs - Abs -> Abs);
+
+impl Mul<f64> for Abs {
+ type Output = Self;
+
+ fn mul(self, other: f64) -> Self {
+ Self(self.0 * other)
+ }
+}
+
+impl Mul<Abs> for f64 {
+ type Output = Abs;
+
+ fn mul(self, other: Abs) -> Abs {
+ other * self
+ }
+}
+
+impl Div<f64> for Abs {
+ type Output = Self;
+
+ fn div(self, other: f64) -> Self {
+ Self(self.0 / other)
+ }
+}
+
+impl Div for Abs {
+ type Output = f64;
+
+ fn div(self, other: Self) -> f64 {
+ self.to_raw() / other.to_raw()
+ }
+}
+
+typst_utils::assign_impl!(Abs += Abs);
+typst_utils::assign_impl!(Abs -= Abs);
+typst_utils::assign_impl!(Abs *= f64);
+typst_utils::assign_impl!(Abs /= f64);
+
+impl Rem for Abs {
+ type Output = Self;
+
+ fn rem(self, other: Self) -> Self::Output {
+ Self(self.0 % other.0)
+ }
+}
+
+impl Sum for Abs {
+ fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
+ Self(iter.map(|s| s.0).sum())
+ }
+}
+
+impl<'a> Sum<&'a Self> for Abs {
+ fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
+ Self(iter.map(|s| s.0).sum())
+ }
+}
+
+impl Fold for Abs {
+ fn fold(self, _: Self) -> Self {
+ self
+ }
+}
+
+cast! {
+ Abs,
+ self => Value::Length(self.into()),
+}
+
+/// Different units of absolute measurement.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub enum AbsUnit {
+ /// Points.
+ Pt,
+ /// Millimeters.
+ Mm,
+ /// Centimeters.
+ Cm,
+ /// Inches.
+ In,
+}
+
+impl AbsUnit {
+ /// The epsilon for approximate length comparisons.
+ const EPS: f64 = 1e-4;
+
+ /// How many raw units correspond to a value of `1.0` in this unit.
+ const fn raw_scale(self) -> f64 {
+ // We choose a raw scale which has an integer conversion value to all
+ // four units of interest, so that whole numbers in all units can be
+ // represented accurately.
+ match self {
+ AbsUnit::Pt => 127.0,
+ AbsUnit::Mm => 360.0,
+ AbsUnit::Cm => 3600.0,
+ AbsUnit::In => 9144.0,
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_length_unit_conversion() {
+ assert!((Abs::mm(150.0).to_cm() - 15.0) < 1e-4);
+ }
+}