typst_library/layout/
abs.rs1use std::fmt::{self, Debug, Formatter};
2use std::iter::Sum;
3use std::ops::{Add, Div, Mul, Neg, Rem};
4
5use ecow::EcoString;
6use typst_utils::{Numeric, Scalar};
7
8use crate::foundations::{cast, repr, Fold, Repr, Value};
9
10#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
12pub struct Abs(Scalar);
13
14impl Abs {
15 pub const fn zero() -> Self {
17 Self(Scalar::ZERO)
18 }
19
20 pub const fn inf() -> Self {
22 Self(Scalar::INFINITY)
23 }
24
25 pub const fn raw(raw: f64) -> Self {
27 Self(Scalar::new(raw))
28 }
29
30 pub fn with_unit(val: f64, unit: AbsUnit) -> Self {
32 Self(Scalar::new(val * unit.raw_scale()))
33 }
34
35 pub fn pt(pt: f64) -> Self {
37 Self::with_unit(pt, AbsUnit::Pt)
38 }
39
40 pub fn mm(mm: f64) -> Self {
42 Self::with_unit(mm, AbsUnit::Mm)
43 }
44
45 pub fn cm(cm: f64) -> Self {
47 Self::with_unit(cm, AbsUnit::Cm)
48 }
49
50 pub fn inches(inches: f64) -> Self {
52 Self::with_unit(inches, AbsUnit::In)
53 }
54
55 pub const fn to_raw(self) -> f64 {
57 self.0.get()
58 }
59
60 pub fn to_unit(self, unit: AbsUnit) -> f64 {
62 self.to_raw() / unit.raw_scale()
63 }
64
65 pub fn to_pt(self) -> f64 {
67 self.to_unit(AbsUnit::Pt)
68 }
69
70 pub fn to_mm(self) -> f64 {
72 self.to_unit(AbsUnit::Mm)
73 }
74
75 pub fn to_cm(self) -> f64 {
77 self.to_unit(AbsUnit::Cm)
78 }
79
80 pub fn to_inches(self) -> f64 {
82 self.to_unit(AbsUnit::In)
83 }
84
85 pub fn abs(self) -> Self {
87 Self::raw(self.to_raw().abs())
88 }
89
90 pub fn min(self, other: Self) -> Self {
92 Self(self.0.min(other.0))
93 }
94
95 pub fn set_min(&mut self, other: Self) {
97 *self = (*self).min(other);
98 }
99
100 pub fn max(self, other: Self) -> Self {
102 Self(self.0.max(other.0))
103 }
104
105 pub fn set_max(&mut self, other: Self) {
107 *self = (*self).max(other);
108 }
109
110 pub fn fits(self, other: Self) -> bool {
113 self.0 + AbsUnit::EPS >= other.0
114 }
115
116 pub fn approx_eq(self, other: Self) -> bool {
118 self == other || (self - other).to_raw().abs() < AbsUnit::EPS
119 }
120
121 pub fn approx_empty(self) -> bool {
123 self.to_raw() <= AbsUnit::EPS
124 }
125
126 pub fn signum(self) -> f64 {
128 self.0.get().signum()
129 }
130}
131
132impl Numeric for Abs {
133 fn zero() -> Self {
134 Self::zero()
135 }
136
137 fn is_finite(self) -> bool {
138 self.0.is_finite()
139 }
140}
141
142impl Debug for Abs {
143 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
144 write!(f, "{:?}pt", self.to_pt())
145 }
146}
147
148impl Repr for Abs {
149 fn repr(&self) -> EcoString {
150 repr::format_float_with_unit(self.to_pt(), "pt")
151 }
152}
153
154impl Neg for Abs {
155 type Output = Self;
156
157 fn neg(self) -> Self {
158 Self(-self.0)
159 }
160}
161
162impl Add for Abs {
163 type Output = Self;
164
165 fn add(self, other: Self) -> Self {
166 Self(self.0 + other.0)
167 }
168}
169
170typst_utils::sub_impl!(Abs - Abs -> Abs);
171
172impl Mul<f64> for Abs {
173 type Output = Self;
174
175 fn mul(self, other: f64) -> Self {
176 Self(self.0 * other)
177 }
178}
179
180impl Mul<Abs> for f64 {
181 type Output = Abs;
182
183 fn mul(self, other: Abs) -> Abs {
184 other * self
185 }
186}
187
188impl Div<f64> for Abs {
189 type Output = Self;
190
191 fn div(self, other: f64) -> Self {
192 Self(self.0 / other)
193 }
194}
195
196impl Div for Abs {
197 type Output = f64;
198
199 fn div(self, other: Self) -> f64 {
200 self.to_raw() / other.to_raw()
201 }
202}
203
204typst_utils::assign_impl!(Abs += Abs);
205typst_utils::assign_impl!(Abs -= Abs);
206typst_utils::assign_impl!(Abs *= f64);
207typst_utils::assign_impl!(Abs /= f64);
208
209impl Rem for Abs {
210 type Output = Self;
211
212 fn rem(self, other: Self) -> Self::Output {
213 Self(self.0 % other.0)
214 }
215}
216
217impl Sum for Abs {
218 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
219 Self(iter.map(|s| s.0).sum())
220 }
221}
222
223impl<'a> Sum<&'a Self> for Abs {
224 fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
225 Self(iter.map(|s| s.0).sum())
226 }
227}
228
229impl Fold for Abs {
230 fn fold(self, _: Self) -> Self {
231 self
232 }
233}
234
235cast! {
236 Abs,
237 self => Value::Length(self.into()),
238}
239
240#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
242pub enum AbsUnit {
243 Pt,
245 Mm,
247 Cm,
249 In,
251}
252
253impl AbsUnit {
254 const EPS: f64 = 1e-4;
256
257 const fn raw_scale(self) -> f64 {
259 match self {
263 AbsUnit::Pt => 127.0,
264 AbsUnit::Mm => 360.0,
265 AbsUnit::Cm => 3600.0,
266 AbsUnit::In => 9144.0,
267 }
268 }
269}
270
271#[cfg(test)]
272mod tests {
273 use super::*;
274
275 #[test]
276 fn test_length_unit_conversion() {
277 assert!((Abs::mm(150.0).to_cm() - 15.0) < 1e-4);
278 }
279}