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::{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#[ty(cast, name = "relative", title = "Relative Length")]
29#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
30pub struct Rel<T: Numeric = Length> {
31 pub rel: Ratio,
33 pub abs: T,
35}
36
37impl<T: Numeric> Rel<T> {
38 pub fn zero() -> Self {
40 Self { rel: Ratio::zero(), abs: T::zero() }
41 }
42
43 pub fn one() -> Self {
45 Self { rel: Ratio::one(), abs: T::zero() }
46 }
47
48 pub fn new(rel: Ratio, abs: T) -> Self {
50 Self { rel, abs }
51 }
52
53 pub fn is_zero(self) -> bool {
55 self.rel.is_zero() && self.abs == T::zero()
56 }
57
58 pub fn is_one(self) -> bool {
60 self.rel.is_one() && self.abs == T::zero()
61 }
62
63 pub fn relative_to(self, whole: T) -> T {
65 self.rel.of(whole) + self.abs
66 }
67
68 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 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}