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