typst_library/layout/
rel.rs1use std::cmp::Ordering;
2use std::fmt::{self, Debug, Formatter};
3use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
4
5use ecow::{EcoString, eco_format};
6use typst_utils::{Numeric, NumericLength};
7
8use crate::foundations::{Fold, Repr, Resolve, StyleChain, cast, ty};
9use crate::layout::{Abs, Em, Length, Ratio};
10
11#[ty(cast, name = "relative", title = "Relative Length")]
77#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
78pub struct Rel<T: NumericLength = Length> {
79 pub rel: Ratio,
81 pub abs: T,
83}
84
85impl<T: NumericLength> Rel<T> {
86 pub fn zero() -> Self {
88 Self { rel: Ratio::zero(), abs: T::zero() }
89 }
90
91 pub fn one() -> Self {
93 Self { rel: Ratio::one(), abs: T::zero() }
94 }
95
96 pub const fn new(rel: Ratio, abs: T) -> Self {
98 Self { rel, abs }
99 }
100
101 pub fn is_zero(self) -> bool {
103 self.rel.is_zero() && self.abs == T::zero()
104 }
105
106 pub fn is_one(self) -> bool {
108 self.rel.is_one() && self.abs == T::zero()
109 }
110
111 pub fn relative_to(self, whole: T) -> T {
113 self.rel.of(whole) + self.abs
114 }
115
116 pub fn map<F, U>(self, f: F) -> Rel<U>
118 where
119 F: FnOnce(T) -> U,
120 U: NumericLength,
121 {
122 Rel { rel: self.rel, abs: f(self.abs) }
123 }
124}
125
126impl Rel<Length> {
127 pub fn try_div(self, other: Self) -> Option<f64> {
129 if self.rel.is_zero() && other.rel.is_zero() {
130 self.abs.try_div(other.abs)
131 } else if self.abs.is_zero() && other.abs.is_zero() {
132 Some(self.rel / other.rel)
133 } else {
134 None
135 }
136 }
137}
138
139impl<T: NumericLength + Debug> Debug for Rel<T> {
140 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
141 match (self.rel.is_zero(), self.abs.is_zero()) {
142 (false, false) => write!(f, "{:?} + {:?}", self.rel, self.abs),
143 (false, true) => self.rel.fmt(f),
144 (true, _) => self.abs.fmt(f),
145 }
146 }
147}
148
149impl<T: NumericLength + Repr> Repr for Rel<T> {
150 fn repr(&self) -> EcoString {
151 eco_format!("{} + {}", self.rel.repr(), self.abs.repr())
152 }
153}
154
155impl<T: NumericLength> Numeric for Rel<T> {
156 fn zero() -> Self {
157 Self::zero()
158 }
159
160 fn is_zero(self) -> bool {
161 self.rel.is_zero() && self.abs.is_zero()
162 }
163
164 fn is_finite(self) -> bool {
165 self.rel.is_finite() && self.abs.is_finite()
166 }
167}
168
169impl From<Abs> for Rel<Length> {
170 fn from(abs: Abs) -> Self {
171 Rel::from(Length::from(abs))
172 }
173}
174
175impl From<Em> for Rel<Length> {
176 fn from(em: Em) -> Self {
177 Rel::from(Length::from(em))
178 }
179}
180
181impl<T: NumericLength> From<T> for Rel<T> {
182 fn from(abs: T) -> Self {
183 Self { rel: Ratio::zero(), abs }
184 }
185}
186
187impl<T: NumericLength> From<Ratio> for Rel<T> {
188 fn from(rel: Ratio) -> Self {
189 Self { rel, abs: T::zero() }
190 }
191}
192
193impl<T: NumericLength + PartialOrd> PartialOrd for Rel<T> {
194 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
195 if self.rel.is_zero() && other.rel.is_zero() {
196 self.abs.partial_cmp(&other.abs)
197 } else if self.abs.is_zero() && other.abs.is_zero() {
198 self.rel.partial_cmp(&other.rel)
199 } else {
200 None
201 }
202 }
203}
204
205impl<T: NumericLength> Neg for Rel<T> {
206 type Output = Self;
207
208 fn neg(self) -> Self {
209 Self { rel: -self.rel, abs: -self.abs }
210 }
211}
212
213impl<T: NumericLength> Add for Rel<T> {
214 type Output = Self;
215
216 fn add(self, other: Self) -> Self::Output {
217 Self {
218 rel: self.rel + other.rel,
219 abs: self.abs + other.abs,
220 }
221 }
222}
223
224impl<T: NumericLength> Sub for Rel<T> {
225 type Output = Self;
226
227 fn sub(self, other: Self) -> Self::Output {
228 self + -other
229 }
230}
231
232impl<T: NumericLength> Mul<f64> for Rel<T> {
233 type Output = Self;
234
235 fn mul(self, other: f64) -> Self::Output {
236 Self { rel: self.rel * other, abs: self.abs * other }
237 }
238}
239
240impl<T: NumericLength> Mul<Rel<T>> for f64 {
241 type Output = Rel<T>;
242
243 fn mul(self, other: Rel<T>) -> Self::Output {
244 other * self
245 }
246}
247
248impl<T: NumericLength> Div<f64> for Rel<T> {
249 type Output = Self;
250
251 fn div(self, other: f64) -> Self::Output {
252 Self { rel: self.rel / other, abs: self.abs / other }
253 }
254}
255
256impl<T: NumericLength + AddAssign> AddAssign for Rel<T> {
257 fn add_assign(&mut self, other: Self) {
258 self.rel += other.rel;
259 self.abs += other.abs;
260 }
261}
262
263impl<T: NumericLength + SubAssign> SubAssign for Rel<T> {
264 fn sub_assign(&mut self, other: Self) {
265 self.rel -= other.rel;
266 self.abs -= other.abs;
267 }
268}
269
270impl<T: NumericLength + MulAssign<f64>> MulAssign<f64> for Rel<T> {
271 fn mul_assign(&mut self, other: f64) {
272 self.rel *= other;
273 self.abs *= other;
274 }
275}
276
277impl<T: NumericLength + DivAssign<f64>> DivAssign<f64> for Rel<T> {
278 fn div_assign(&mut self, other: f64) {
279 self.rel /= other;
280 self.abs /= other;
281 }
282}
283
284impl<T: NumericLength> Add<T> for Ratio {
285 type Output = Rel<T>;
286
287 fn add(self, other: T) -> Self::Output {
288 Rel::from(self) + Rel::from(other)
289 }
290}
291
292impl<T: NumericLength> Add<T> for Rel<T> {
293 type Output = Self;
294
295 fn add(self, other: T) -> Self::Output {
296 self + Rel::from(other)
297 }
298}
299
300impl<T: NumericLength> Add<Ratio> for Rel<T> {
301 type Output = Self;
302
303 fn add(self, other: Ratio) -> Self::Output {
304 self + Rel::from(other)
305 }
306}
307
308impl<T: NumericLength> Sub<T> for Rel<T> {
309 type Output = Self;
310
311 fn sub(self, other: T) -> Self::Output {
312 self - Rel::from(other)
313 }
314}
315
316impl<T> Resolve for Rel<T>
317where
318 T: Resolve + NumericLength,
319 <T as Resolve>::Output: NumericLength,
320{
321 type Output = Rel<<T as Resolve>::Output>;
322
323 fn resolve(self, styles: StyleChain) -> Self::Output {
324 self.map(|abs| abs.resolve(styles))
325 }
326}
327
328impl<T> Fold for Rel<T>
329where
330 T: NumericLength + Fold,
331{
332 fn fold(self, outer: Self) -> Self {
333 Self { rel: self.rel, abs: self.abs.fold(outer.abs) }
334 }
335}
336
337cast! {
338 Rel<Abs>,
339 self => self.map(Length::from).into_value(),
340}