typst_library/layout/
ratio.rs

1use std::fmt::{self, Debug, Formatter};
2use std::ops::{Add, Div, Mul, Neg};
3
4use ecow::EcoString;
5use typst_utils::{Numeric, Scalar};
6
7use crate::foundations::{repr, ty, Repr};
8
9/// A ratio of a whole.
10///
11/// Written as a number, followed by a percent sign.
12///
13/// # Example
14/// ```example
15/// #set align(center)
16/// #scale(x: 150%)[
17///   Scaled apart.
18/// ]
19/// ```
20#[ty(cast)]
21#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
22pub struct Ratio(Scalar);
23
24impl Ratio {
25    /// A ratio of `0%` represented as `0.0`.
26    pub const fn zero() -> Self {
27        Self(Scalar::ZERO)
28    }
29
30    /// A ratio of `100%` represented as `1.0`.
31    pub const fn one() -> Self {
32        Self(Scalar::ONE)
33    }
34
35    /// Create a new ratio from a value, where `1.0` means `100%`.
36    pub const fn new(ratio: f64) -> Self {
37        Self(Scalar::new(ratio))
38    }
39
40    /// Get the underlying ratio.
41    pub const fn get(self) -> f64 {
42        (self.0).get()
43    }
44
45    /// Whether the ratio is zero.
46    pub fn is_zero(self) -> bool {
47        self.0 == 0.0
48    }
49
50    /// Whether the ratio is one.
51    pub fn is_one(self) -> bool {
52        self.0 == 1.0
53    }
54
55    /// The absolute value of this ratio.
56    pub fn abs(self) -> Self {
57        Self::new(self.get().abs())
58    }
59
60    /// Return the ratio of the given `whole`.
61    pub fn of<T: Numeric>(self, whole: T) -> T {
62        let resolved = whole * self.get();
63        if resolved.is_finite() {
64            resolved
65        } else {
66            T::zero()
67        }
68    }
69}
70
71impl Debug for Ratio {
72    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
73        write!(f, "{:?}%", self.get() * 100.0)
74    }
75}
76
77impl Repr for Ratio {
78    fn repr(&self) -> EcoString {
79        repr::format_float_with_unit(self.get() * 100.0, "%")
80    }
81}
82
83impl Neg for Ratio {
84    type Output = Self;
85
86    fn neg(self) -> Self {
87        Self(-self.0)
88    }
89}
90
91impl Add for Ratio {
92    type Output = Self;
93
94    fn add(self, other: Self) -> Self {
95        Self(self.0 + other.0)
96    }
97}
98
99typst_utils::sub_impl!(Ratio - Ratio -> Ratio);
100
101impl Mul for Ratio {
102    type Output = Self;
103
104    fn mul(self, other: Self) -> Self {
105        Self(self.0 * other.0)
106    }
107}
108
109impl Mul<f64> for Ratio {
110    type Output = Self;
111
112    fn mul(self, other: f64) -> Self {
113        Self(self.0 * other)
114    }
115}
116
117impl Mul<Ratio> for f64 {
118    type Output = Ratio;
119
120    fn mul(self, other: Ratio) -> Ratio {
121        other * self
122    }
123}
124
125impl Div for Ratio {
126    type Output = f64;
127
128    fn div(self, other: Self) -> f64 {
129        self.get() / other.get()
130    }
131}
132
133impl Div<f64> for Ratio {
134    type Output = Self;
135
136    fn div(self, other: f64) -> Self {
137        Self(self.0 / other)
138    }
139}
140
141impl Div<Ratio> for f64 {
142    type Output = Self;
143
144    fn div(self, other: Ratio) -> Self {
145        self / other.get()
146    }
147}
148
149typst_utils::assign_impl!(Ratio += Ratio);
150typst_utils::assign_impl!(Ratio -= Ratio);
151typst_utils::assign_impl!(Ratio *= Ratio);
152typst_utils::assign_impl!(Ratio *= f64);
153typst_utils::assign_impl!(Ratio /= f64);