polyhorn_ui/layout/
mod.rs

1//! Primitives to work with layout and opaque geometry.
2
3use num_traits::Float;
4use std::borrow::Borrow;
5use strum_macros::EnumString;
6
7use crate::geometry::Size;
8
9/// Implementations of the flexbox algorithm.
10pub mod algorithm;
11mod tree;
12
13pub use algorithm::Algorithm;
14pub use tree::{Layout, LayoutNode, LayoutTree, MeasureFunc};
15
16/// Opaque coordinate within a reference system.
17#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
18pub struct LayoutAnchor<T>
19where
20    T: Float,
21{
22    x: T,
23    y: T,
24}
25
26impl<T> LayoutAnchor<T>
27where
28    T: Float,
29{
30    /// Returns a new anchor with the given absolute coordinates (i.e. relative
31    /// to the origin of the reference system).
32    pub fn new(x: T, y: T) -> LayoutAnchor<T> {
33        LayoutAnchor { x, y }
34    }
35
36    /// Returns the distance between this anchor and the given anchor.
37    pub fn distance(self, other: Self) -> LayoutDistance<T> {
38        LayoutDistance {
39            dx: other.borrow().x - self.x,
40            dy: other.borrow().y - self.y,
41        }
42    }
43}
44
45/// Represents the layout direction of a language (i.e. left-to-right vs.
46/// right-to-left).
47#[derive(Copy, Clone, Debug, Eq, PartialEq, EnumString)]
48pub enum LayoutDirection {
49    /// Left to right. This is the most commonly used direction in most
50    /// languages.
51    #[strum(serialize = "ltr")]
52    LTR,
53
54    /// Right to left. This direction is used in Hebrew and Arabic, for example.
55    #[strum(serialize = "rtl")]
56    RTL,
57}
58
59/// Represents a potentially direction dependent horizontal layout axis.
60#[derive(Copy, Clone, Debug, Eq, PartialEq)]
61pub enum LayoutAxisX<T> {
62    /// A horizontal axis that depends on the layout direction.
63    DirectionDependent {
64        /// Contains the layout direction dependent leading value for this
65        /// horizontal axis.
66        leading: T,
67
68        /// Contains the layout direction dependent leading value for this
69        /// horizontal axis.
70        trailing: T,
71    },
72
73    /// A horizontal axis that is independent of the layout direction.
74    DirectionIndependent {
75        /// Contains the layout direction independent left value for this
76        /// horizontal axis.
77        left: T,
78
79        /// Contains the layout direction independent right value for this
80        /// horizontal axis.
81        right: T,
82    },
83}
84
85impl<T> LayoutAxisX<T> {
86    /// Returns a layout direction dependent horizontal axis with the given
87    /// leading and trailing values.
88    pub fn dependent(leading: T, trailing: T) -> LayoutAxisX<T> {
89        LayoutAxisX::DirectionDependent { leading, trailing }
90    }
91
92    /// Returns a layout direction independent horizontal axis with the given
93    /// left and right values.
94    pub fn independent(left: T, right: T) -> LayoutAxisX<T> {
95        LayoutAxisX::DirectionIndependent { left, right }
96    }
97
98    /// Resolves the left value of this horizontal axis using the given layout
99    /// direction.
100    pub fn left(&self, direction: LayoutDirection) -> &T {
101        match self {
102            LayoutAxisX::DirectionDependent { leading, trailing } => match direction {
103                LayoutDirection::LTR => leading,
104                LayoutDirection::RTL => trailing,
105            },
106            LayoutAxisX::DirectionIndependent { left, .. } => left,
107        }
108    }
109
110    /// Resolves the right value of this horizontal axis using the given layout
111    /// direction.
112    pub fn right(&self, direction: LayoutDirection) -> &T {
113        match self {
114            LayoutAxisX::DirectionDependent { leading, trailing } => match direction {
115                LayoutDirection::LTR => trailing,
116                LayoutDirection::RTL => leading,
117            },
118            LayoutAxisX::DirectionIndependent { right, .. } => right,
119        }
120    }
121}
122
123impl<T> Default for LayoutAxisX<T>
124where
125    T: Default,
126{
127    fn default() -> Self {
128        LayoutAxisX::DirectionIndependent {
129            left: T::default(),
130            right: T::default(),
131        }
132    }
133}
134
135/// Represents a direction independent vertical layout axis.
136#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
137pub struct LayoutAxisY<T> {
138    /// Contains the horizontal axis for the top edge.
139    pub top: T,
140
141    /// Contains the horizontal axis for the bottom edge.
142    pub bottom: T,
143}
144
145/// Opaque rectangle defined by coordinates within a reference system.
146#[derive(Copy, Clone, Debug, Eq, PartialEq)]
147pub struct LayoutGuide<T>
148where
149    T: Float,
150{
151    origin: LayoutAnchor<T>,
152    size: Size<T>,
153    direction: LayoutDirection,
154}
155
156impl<T> LayoutGuide<T>
157where
158    T: Float,
159{
160    /// Returns a new layout guide with the given opaque origin coordinate and
161    /// the given concrete size and layout direction.
162    pub fn new(
163        origin: LayoutAnchor<T>,
164        size: Size<T>,
165        direction: LayoutDirection,
166    ) -> LayoutGuide<T> {
167        LayoutGuide {
168            origin,
169            size,
170            direction,
171        }
172    }
173
174    /// Returns a layout anchor for the top left corner.
175    pub fn top_left(&self) -> LayoutAnchor<T> {
176        self.origin
177    }
178
179    /// Returns a layout anchor for the top right corner.
180    pub fn top_right(&self) -> LayoutAnchor<T> {
181        let mut anchor = self.origin;
182        anchor.x = anchor.x + self.size.width;
183        anchor
184    }
185
186    /// Returns a layout anchor for the top leading corner (which is either the
187    /// top left corner or the top right corner depending on the layout
188    /// direction).
189    pub fn top_leading(&self) -> LayoutAnchor<T> {
190        match self.direction {
191            LayoutDirection::LTR => self.top_left(),
192            LayoutDirection::RTL => self.top_right(),
193        }
194    }
195
196    /// Returns a layout anchor for the top trailing corner (which is either the
197    /// top right corner or the top left corner depending on the layout
198    /// direction).
199    pub fn top_trailing(&self) -> LayoutAnchor<T> {
200        match self.direction {
201            LayoutDirection::LTR => self.top_right(),
202            LayoutDirection::RTL => self.top_left(),
203        }
204    }
205
206    /// Returns a layout anchor for the bottom left corner.
207    pub fn bottom_left(&self) -> LayoutAnchor<T> {
208        let mut anchor = self.origin;
209        anchor.y = anchor.y + self.size.height;
210        anchor
211    }
212
213    /// Returns a layout anchor for the bottom right corner.
214    pub fn bottom_right(&self) -> LayoutAnchor<T> {
215        let mut anchor = self.origin;
216        anchor.x = anchor.x + self.size.width;
217        anchor.y = anchor.y + self.size.height;
218        anchor
219    }
220
221    /// Returns a layout anchor for the bottom leading corner (which is either
222    /// the bottom left corner or the bottom right corner depending on the
223    /// layout direction).
224    pub fn bottom_leading(&self) -> LayoutAnchor<T> {
225        match self.direction {
226            LayoutDirection::LTR => self.bottom_left(),
227            LayoutDirection::RTL => self.bottom_right(),
228        }
229    }
230
231    /// Returns a layout anchor for the bottom trailing corner (which is either
232    /// the bottom right corner or the bottom left corner depending on the
233    /// layout direction).
234    pub fn bottom_trailing(&self) -> LayoutAnchor<T> {
235        match self.direction {
236            LayoutDirection::LTR => self.bottom_right(),
237            LayoutDirection::RTL => self.bottom_left(),
238        }
239    }
240}
241
242/// Distance between opaque coordinates within a reference system.
243#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
244pub struct LayoutDistance<T> {
245    dx: T,
246    dy: T,
247}
248
249impl<T> LayoutDistance<T>
250where
251    T: Float,
252{
253    /// Returns the L1 norm of this distance vector.
254    pub fn l1_norm(&self) -> T
255    where
256        T: Float,
257    {
258        self.dx.abs() + self.dy.abs()
259    }
260
261    /// Returns the L2 norm of this distance vector.
262    pub fn l2_norm(&self) -> T {
263        self.dx * self.dx + self.dy * self.dy
264    }
265}
266
267#[cfg(test)]
268mod tests {
269    use super::LayoutAnchor;
270
271    #[test]
272    fn test_l1_norm() {
273        let a = LayoutAnchor::new(4.0, -42.0);
274        let b = LayoutAnchor::new(-2.0, -8.0);
275
276        assert_eq!(a.distance(b).l1_norm(), 40.0);
277        assert_eq!(b.distance(a).l1_norm(), 40.0);
278    }
279
280    #[test]
281    fn test_l2_norm() {
282        let a = LayoutAnchor::new(4.0, -42.0);
283        let b = LayoutAnchor::new(-2.0, -8.0);
284
285        assert_eq!(a.distance(b).l2_norm(), 1192.0);
286        assert_eq!(b.distance(a).l2_norm(), 1192.0);
287    }
288}