typst_library/layout/
rel.rs

1use std::cmp::Ordering;
2use std::fmt::{self, Debug, Formatter};
3use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
4
5use ecow::{eco_format, EcoString};
6use typst_utils::Numeric;
7
8use crate::foundations::{cast, ty, Fold, Repr, Resolve, StyleChain};
9use crate::layout::{Abs, Em, Length, Ratio};
10
11/// A length in relation to some known length.
12///
13/// This type is a combination of a [length] with a [ratio]. It results from
14/// addition and subtraction of a length and a ratio. Wherever a relative length
15/// is expected, you can also use a bare length or ratio.
16///
17/// # Example
18/// ```example
19/// #rect(width: 100% - 50pt)
20///
21/// #(100% - 50pt).length \
22/// #(100% - 50pt).ratio
23/// ```
24///
25/// A relative length has the following fields:
26/// - `length`: Its length component.
27/// - `ratio`: Its ratio component.
28#[ty(cast, name = "relative", title = "Relative Length")]
29#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
30pub struct Rel<T: Numeric = Length> {
31    /// The relative part.
32    pub rel: Ratio,
33    /// The absolute part.
34    pub abs: T,
35}
36
37impl<T: Numeric> Rel<T> {
38    /// The zero relative.
39    pub fn zero() -> Self {
40        Self { rel: Ratio::zero(), abs: T::zero() }
41    }
42
43    /// A relative with a ratio of `100%` and no absolute part.
44    pub fn one() -> Self {
45        Self { rel: Ratio::one(), abs: T::zero() }
46    }
47
48    /// Create a new relative from its parts.
49    pub fn new(rel: Ratio, abs: T) -> Self {
50        Self { rel, abs }
51    }
52
53    /// Whether both parts are zero.
54    pub fn is_zero(self) -> bool {
55        self.rel.is_zero() && self.abs == T::zero()
56    }
57
58    /// Whether the relative part is one and the absolute part is zero.
59    pub fn is_one(self) -> bool {
60        self.rel.is_one() && self.abs == T::zero()
61    }
62
63    /// Evaluate this relative to the given `whole`.
64    pub fn relative_to(self, whole: T) -> T {
65        self.rel.of(whole) + self.abs
66    }
67
68    /// Map the absolute part with `f`.
69    pub fn map<F, U>(self, f: F) -> Rel<U>
70    where
71        F: FnOnce(T) -> U,
72        U: Numeric,
73    {
74        Rel { rel: self.rel, abs: f(self.abs) }
75    }
76}
77
78impl Rel<Length> {
79    /// Try to divide two relative lengths.
80    pub fn try_div(self, other: Self) -> Option<f64> {
81        if self.rel.is_zero() && other.rel.is_zero() {
82            self.abs.try_div(other.abs)
83        } else if self.abs.is_zero() && other.abs.is_zero() {
84            Some(self.rel / other.rel)
85        } else {
86            None
87        }
88    }
89}
90
91impl<T: Numeric + Debug> Debug for Rel<T> {
92    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
93        match (self.rel.is_zero(), self.abs.is_zero()) {
94            (false, false) => write!(f, "{:?} + {:?}", self.rel, self.abs),
95            (false, true) => self.rel.fmt(f),
96            (true, _) => self.abs.fmt(f),
97        }
98    }
99}
100
101impl<T: Numeric + Repr> Repr for Rel<T> {
102    fn repr(&self) -> EcoString {
103        eco_format!("{} + {}", self.rel.repr(), self.abs.repr())
104    }
105}
106
107impl From<Abs> for Rel<Length> {
108    fn from(abs: Abs) -> Self {
109        Rel::from(Length::from(abs))
110    }
111}
112
113impl From<Em> for Rel<Length> {
114    fn from(em: Em) -> Self {
115        Rel::from(Length::from(em))
116    }
117}
118
119impl<T: Numeric> From<T> for Rel<T> {
120    fn from(abs: T) -> Self {
121        Self { rel: Ratio::zero(), abs }
122    }
123}
124
125impl<T: Numeric> From<Ratio> for Rel<T> {
126    fn from(rel: Ratio) -> Self {
127        Self { rel, abs: T::zero() }
128    }
129}
130
131impl<T: Numeric + PartialOrd> PartialOrd for Rel<T> {
132    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
133        if self.rel.is_zero() && other.rel.is_zero() {
134            self.abs.partial_cmp(&other.abs)
135        } else if self.abs.is_zero() && other.abs.is_zero() {
136            self.rel.partial_cmp(&other.rel)
137        } else {
138            None
139        }
140    }
141}
142
143impl<T: Numeric> Neg for Rel<T> {
144    type Output = Self;
145
146    fn neg(self) -> Self {
147        Self { rel: -self.rel, abs: -self.abs }
148    }
149}
150
151impl<T: Numeric> Add for Rel<T> {
152    type Output = Self;
153
154    fn add(self, other: Self) -> Self::Output {
155        Self {
156            rel: self.rel + other.rel,
157            abs: self.abs + other.abs,
158        }
159    }
160}
161
162impl<T: Numeric> Sub for Rel<T> {
163    type Output = Self;
164
165    fn sub(self, other: Self) -> Self::Output {
166        self + -other
167    }
168}
169
170impl<T: Numeric> Mul<f64> for Rel<T> {
171    type Output = Self;
172
173    fn mul(self, other: f64) -> Self::Output {
174        Self { rel: self.rel * other, abs: self.abs * other }
175    }
176}
177
178impl<T: Numeric> Mul<Rel<T>> for f64 {
179    type Output = Rel<T>;
180
181    fn mul(self, other: Rel<T>) -> Self::Output {
182        other * self
183    }
184}
185
186impl<T: Numeric> Div<f64> for Rel<T> {
187    type Output = Self;
188
189    fn div(self, other: f64) -> Self::Output {
190        Self { rel: self.rel / other, abs: self.abs / other }
191    }
192}
193
194impl<T: Numeric + AddAssign> AddAssign for Rel<T> {
195    fn add_assign(&mut self, other: Self) {
196        self.rel += other.rel;
197        self.abs += other.abs;
198    }
199}
200
201impl<T: Numeric + SubAssign> SubAssign for Rel<T> {
202    fn sub_assign(&mut self, other: Self) {
203        self.rel -= other.rel;
204        self.abs -= other.abs;
205    }
206}
207
208impl<T: Numeric + MulAssign<f64>> MulAssign<f64> for Rel<T> {
209    fn mul_assign(&mut self, other: f64) {
210        self.rel *= other;
211        self.abs *= other;
212    }
213}
214
215impl<T: Numeric + DivAssign<f64>> DivAssign<f64> for Rel<T> {
216    fn div_assign(&mut self, other: f64) {
217        self.rel /= other;
218        self.abs /= other;
219    }
220}
221
222impl<T: Numeric> Add<T> for Ratio {
223    type Output = Rel<T>;
224
225    fn add(self, other: T) -> Self::Output {
226        Rel::from(self) + Rel::from(other)
227    }
228}
229
230impl<T: Numeric> Add<T> for Rel<T> {
231    type Output = Self;
232
233    fn add(self, other: T) -> Self::Output {
234        self + Rel::from(other)
235    }
236}
237
238impl<T: Numeric> Add<Ratio> for Rel<T> {
239    type Output = Self;
240
241    fn add(self, other: Ratio) -> Self::Output {
242        self + Rel::from(other)
243    }
244}
245
246impl<T> Resolve for Rel<T>
247where
248    T: Resolve + Numeric,
249    <T as Resolve>::Output: Numeric,
250{
251    type Output = Rel<<T as Resolve>::Output>;
252
253    fn resolve(self, styles: StyleChain) -> Self::Output {
254        self.map(|abs| abs.resolve(styles))
255    }
256}
257
258impl<T> Fold for Rel<T>
259where
260    T: Numeric + Fold,
261{
262    fn fold(self, outer: Self) -> Self {
263        Self { rel: self.rel, abs: self.abs.fold(outer.abs) }
264    }
265}
266
267cast! {
268    Rel<Abs>,
269    self => self.map(Length::from).into_value(),
270}