typst_library/layout/
axes.rs

1use std::any::Any;
2use std::fmt::{self, Debug, Formatter};
3use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Deref, Not};
4
5use typst_utils::Get;
6
7use crate::diag::{HintedStrResult, bail};
8use crate::foundations::{
9    Array, CastInfo, FromValue, IntoValue, Reflect, Resolve, Smart, StyleChain, Value,
10    array, cast,
11};
12use crate::layout::{Abs, Dir, Rel, Size};
13
14/// A container with a horizontal and vertical component.
15#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
16pub struct Axes<T> {
17    /// The horizontal component.
18    pub x: T,
19    /// The vertical component.
20    pub y: T,
21}
22
23impl<T> Axes<T> {
24    /// Create a new instance from the two components.
25    pub const fn new(x: T, y: T) -> Self {
26        Self { x, y }
27    }
28
29    /// Create a new instance with two equal components.
30    pub fn splat(v: T) -> Self
31    where
32        T: Clone,
33    {
34        Self { x: v.clone(), y: v }
35    }
36
37    /// Map the individual fields with `f`.
38    pub fn map<F, U>(self, mut f: F) -> Axes<U>
39    where
40        F: FnMut(T) -> U,
41    {
42        Axes { x: f(self.x), y: f(self.y) }
43    }
44
45    /// Convert from `&Axes<T>` to `Axes<&T>`.
46    pub fn as_ref(&self) -> Axes<&T> {
47        Axes { x: &self.x, y: &self.y }
48    }
49
50    /// Convert from `&Axes<T>` to `Axes<&<T as Deref>::Target>`.
51    pub fn as_deref(&self) -> Axes<&T::Target>
52    where
53        T: Deref,
54    {
55        Axes { x: &self.x, y: &self.y }
56    }
57
58    /// Convert from `&mut Axes<T>` to `Axes<&mut T>`.
59    pub fn as_mut(&mut self) -> Axes<&mut T> {
60        Axes { x: &mut self.x, y: &mut self.y }
61    }
62
63    /// Zip two instances into an instance over a tuple.
64    pub fn zip<U>(self, other: Axes<U>) -> Axes<(T, U)> {
65        Axes { x: (self.x, other.x), y: (self.y, other.y) }
66    }
67
68    /// Apply a function to this and another-instance componentwise.
69    pub fn zip_map<F, V, U>(self, other: Axes<V>, mut f: F) -> Axes<U>
70    where
71        F: FnMut(T, V) -> U,
72    {
73        Axes { x: f(self.x, other.x), y: f(self.y, other.y) }
74    }
75
76    /// Whether a condition is true for at least one of fields.
77    pub fn any<F>(self, mut f: F) -> bool
78    where
79        F: FnMut(&T) -> bool,
80    {
81        f(&self.x) || f(&self.y)
82    }
83
84    /// Whether a condition is true for both fields.
85    pub fn all<F>(self, mut f: F) -> bool
86    where
87        F: FnMut(&T) -> bool,
88    {
89        f(&self.x) && f(&self.y)
90    }
91}
92
93impl<T: Default> Axes<T> {
94    /// Create a new instance with y set to its default value.
95    pub fn with_x(x: T) -> Self {
96        Self { x, y: T::default() }
97    }
98
99    /// Create a new instance with x set to its default value.
100    pub fn with_y(y: T) -> Self {
101        Self { x: T::default(), y }
102    }
103}
104
105impl<T: Ord> Axes<T> {
106    /// The component-wise minimum of this and another instance.
107    pub fn min(self, other: Self) -> Self {
108        Self { x: self.x.min(other.x), y: self.y.min(other.y) }
109    }
110
111    /// The component-wise minimum of this and another instance.
112    pub fn max(self, other: Self) -> Self {
113        Self { x: self.x.max(other.x), y: self.y.max(other.y) }
114    }
115
116    /// The minimum of width and height.
117    pub fn min_by_side(self) -> T {
118        self.x.min(self.y)
119    }
120
121    /// The minimum of width and height.
122    pub fn max_by_side(self) -> T {
123        self.x.max(self.y)
124    }
125}
126
127impl Axes<Rel<Abs>> {
128    /// Evaluate the axes relative to the given `size`.
129    pub fn relative_to(&self, size: Size) -> Size {
130        Size {
131            x: self.x.relative_to(size.x),
132            y: self.y.relative_to(size.y),
133        }
134    }
135}
136
137impl<T> Get<Axis> for Axes<T> {
138    type Component = T;
139
140    fn get_ref(&self, axis: Axis) -> &T {
141        match axis {
142            Axis::X => &self.x,
143            Axis::Y => &self.y,
144        }
145    }
146
147    fn get_mut(&mut self, axis: Axis) -> &mut T {
148        match axis {
149            Axis::X => &mut self.x,
150            Axis::Y => &mut self.y,
151        }
152    }
153}
154
155impl<T> Debug for Axes<T>
156where
157    T: Debug + 'static,
158{
159    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
160        if (&self.x as &dyn Any).is::<Abs>() {
161            write!(f, "Size({:?}, {:?})", self.x, self.y)
162        } else {
163            write!(f, "Axes({:?}, {:?})", self.x, self.y)
164        }
165    }
166}
167
168/// The two layouting axes.
169#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
170pub enum Axis {
171    /// The horizontal axis.
172    X,
173    /// The vertical  axis.
174    Y,
175}
176
177impl Axis {
178    /// The direction with the given positivity for this axis.
179    pub fn dir(self, positive: bool) -> Dir {
180        match (self, positive) {
181            (Self::X, true) => Dir::LTR,
182            (Self::X, false) => Dir::RTL,
183            (Self::Y, true) => Dir::TTB,
184            (Self::Y, false) => Dir::BTT,
185        }
186    }
187
188    /// The other axis.
189    pub fn other(self) -> Self {
190        match self {
191            Self::X => Self::Y,
192            Self::Y => Self::X,
193        }
194    }
195}
196
197cast! {
198    Axis,
199    self => match self {
200        Self::X => "horizontal".into_value(),
201        Self::Y => "vertical".into_value(),
202    },
203    "horizontal" => Self::X,
204    "vertical" => Self::Y,
205}
206
207impl<T> Axes<Smart<T>> {
208    /// Unwrap the individual fields.
209    pub fn unwrap_or(self, other: Axes<T>) -> Axes<T> {
210        Axes {
211            x: self.x.unwrap_or(other.x),
212            y: self.y.unwrap_or(other.y),
213        }
214    }
215}
216
217impl Axes<bool> {
218    /// Select `t.x` if `self.x` is true and `f.x` otherwise and same for `y`.
219    pub fn select<T>(self, t: Axes<T>, f: Axes<T>) -> Axes<T> {
220        Axes {
221            x: if self.x { t.x } else { f.x },
222            y: if self.y { t.y } else { f.y },
223        }
224    }
225}
226
227impl Not for Axes<bool> {
228    type Output = Self;
229
230    fn not(self) -> Self::Output {
231        Self { x: !self.x, y: !self.y }
232    }
233}
234
235impl BitOr for Axes<bool> {
236    type Output = Self;
237
238    fn bitor(self, rhs: Self) -> Self::Output {
239        Self { x: self.x | rhs.x, y: self.y | rhs.y }
240    }
241}
242
243impl BitOr<bool> for Axes<bool> {
244    type Output = Self;
245
246    fn bitor(self, rhs: bool) -> Self::Output {
247        Self { x: self.x | rhs, y: self.y | rhs }
248    }
249}
250
251impl BitAnd for Axes<bool> {
252    type Output = Self;
253
254    fn bitand(self, rhs: Self) -> Self::Output {
255        Self { x: self.x & rhs.x, y: self.y & rhs.y }
256    }
257}
258
259impl BitAnd<bool> for Axes<bool> {
260    type Output = Self;
261
262    fn bitand(self, rhs: bool) -> Self::Output {
263        Self { x: self.x & rhs, y: self.y & rhs }
264    }
265}
266
267impl BitOrAssign for Axes<bool> {
268    fn bitor_assign(&mut self, rhs: Self) {
269        self.x |= rhs.x;
270        self.y |= rhs.y;
271    }
272}
273
274impl BitAndAssign for Axes<bool> {
275    fn bitand_assign(&mut self, rhs: Self) {
276        self.x &= rhs.x;
277        self.y &= rhs.y;
278    }
279}
280
281impl<T: Reflect> Reflect for Axes<T> {
282    fn input() -> CastInfo {
283        Array::input()
284    }
285
286    fn output() -> CastInfo {
287        Array::output()
288    }
289
290    fn castable(value: &Value) -> bool {
291        Array::castable(value)
292    }
293}
294
295impl<T: FromValue> FromValue for Axes<T> {
296    fn from_value(value: Value) -> HintedStrResult<Self> {
297        let array = value.cast::<Array>()?;
298        let mut iter = array.into_iter();
299        match (iter.next(), iter.next(), iter.next()) {
300            (Some(a), Some(b), None) => Ok(Axes::new(a.cast()?, b.cast()?)),
301            _ => bail!(
302                "array must contain exactly two items";
303                hint: "the first item determines the value for the X axis \
304                       and the second item the value for the Y axis"
305            ),
306        }
307    }
308}
309
310impl<T: IntoValue> IntoValue for Axes<T> {
311    fn into_value(self) -> Value {
312        array![self.x.into_value(), self.y.into_value()].into_value()
313    }
314}
315
316impl<T: Resolve> Resolve for Axes<T> {
317    type Output = Axes<T::Output>;
318
319    fn resolve(self, styles: StyleChain) -> Self::Output {
320        self.map(|v| v.resolve(styles))
321    }
322}