gioser

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 5544Rust 88Markdown 11TOML 91926 Sutichaykuna

⎇ 0.2.x src/geometry.rs Rust · 304 siq'ikuna · 9.7 KB Ñawpa willay Pi imata llamkarqan ch'uya

Rename Rect field start->left end->right (#257) ca514f6c8 · Nico Burns ·

1//! Geometric primitives useful for layout
2
3use crate::style::{Dimension, FlexDirection};
4use core::ops::Add;
5
6/// An axis-aligned UI rectangle
7#[derive(Debug, Copy, Clone, PartialEq, Eq)]
8#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
9#[cfg_attr(feature = "serde", serde(default))]
10pub struct Rect<T> {
11    /// This can represent either the x-coordinate of the starting edge,
12    /// or the amount of padding on the starting side.
13    ///
14    /// The starting edge is the left edge when working with LTR text,
15    /// and the right edge when working with RTL text.
16    pub left: T,
17    /// This can represent either the x-coordinate of the ending edge,
18    /// or the amount of padding on the ending side.
19    ///
20    /// The ending edge is the right edge when working with LTR text,
21    /// and the left edge when working with RTL text.
22    pub right: T,
23    /// This can represent either the y-coordinate of the top edge,
24    /// or the amount of padding on the top side.
25    pub top: T,
26    /// This can represent either the y-coordinate of the bottom edge,
27    /// or the amount of padding on the bottom side.
28    pub bottom: T,
29}
30
31impl<T> Rect<T> {
32    /// Applies the function `f` to all four sides of the rect
33    ///
34    /// When applied to the left and right sides, the width is used
35    /// as the second parameter of `f`.
36    /// When applied to the top or bottom sides, the height is used instead.
37    pub(crate) fn zip_size<R, F, U>(self, size: Size<U>, f: F) -> Rect<R>
38    where
39        F: Fn(T, U) -> R,
40        U: Copy,
41    {
42        Rect {
43            left: f(self.left, size.width),
44            right: f(self.right, size.width),
45            top: f(self.top, size.height),
46            bottom: f(self.bottom, size.height),
47        }
48    }
49}
50
51impl<T> Rect<T>
52where
53    T: Add<Output = T> + Copy + Clone,
54{
55    /// The sum of [`Rect.start`](Rect) and [`Rect.end`](Rect)
56    ///
57    /// This is typically used when computing total padding.
58    ///
59    /// **NOTE:** this is *not* the width of the rectangle.
60    pub(crate) fn horizontal_axis_sum(&self) -> T {
61        self.left + self.right
62    }
63
64    /// The sum of [`Rect.top`](Rect) and [`Rect.bottom`](Rect)
65    ///
66    /// This is typically used when computing total padding.
67    ///
68    /// **NOTE:** this is *not* the height of the rectangle.
69    pub(crate) fn vertical_axis_sum(&self) -> T {
70        self.top + self.bottom
71    }
72
73    /// The sum of the two fields of the [`Rect`] representing the main axis.
74    ///
75    /// This is typically used when computing total padding.
76    ///
77    /// If the [`FlexDirection`] is [`FlexDirection::Row`] or [`FlexDirection::RowReverse`], this is [`Rect::horizontal`].
78    /// Otherwise, this is [`Rect::vertical`].
79    pub(crate) fn main_axis_sum(&self, direction: FlexDirection) -> T {
80        if direction.is_row() {
81            self.horizontal_axis_sum()
82        } else {
83            self.vertical_axis_sum()
84        }
85    }
86
87    /// The sum of the two fields of the [`Rect`] representing the cross axis.
88    ///
89    /// If the [`FlexDirection`] is [`FlexDirection::Row`] or [`FlexDirection::RowReverse`], this is [`Rect::vertical`].
90    /// Otherwise, this is [`Rect::horizontal`].
91    pub(crate) fn cross_axis_sum(&self, direction: FlexDirection) -> T {
92        if direction.is_row() {
93            self.vertical_axis_sum()
94        } else {
95            self.horizontal_axis_sum()
96        }
97    }
98}
99
100impl<T> Rect<T>
101where
102    T: Copy + Clone,
103{
104    /// The `start` or `top` value of the [`Rect`], from the perspective of the main layout axis
105    pub(crate) fn main_start(&self, direction: FlexDirection) -> T {
106        if direction.is_row() {
107            self.left
108        } else {
109            self.top
110        }
111    }
112
113    /// The `end` or `bottom` value of the [`Rect`], from the perspective of the main layout axis
114    pub(crate) fn main_end(&self, direction: FlexDirection) -> T {
115        if direction.is_row() {
116            self.right
117        } else {
118            self.bottom
119        }
120    }
121
122    /// The `start` or `top` value of the [`Rect`], from the perspective of the cross layout axis
123    pub(crate) fn cross_start(&self, direction: FlexDirection) -> T {
124        if direction.is_row() {
125            self.top
126        } else {
127            self.left
128        }
129    }
130
131    /// The `end` or `bottom` value of the [`Rect`], from the perspective of the main layout axis
132    pub(crate) fn cross_end(&self, direction: FlexDirection) -> T {
133        if direction.is_row() {
134            self.bottom
135        } else {
136            self.right
137        }
138    }
139}
140
141impl Rect<f32> {
142    /// Creates a new Rect with `0.0` as all parameters
143    pub const ZERO: Rect<f32> = Self { left: 0.0, right: 0.0, top: 0.0, bottom: 0.0 };
144
145    /// Creates a new Rect
146    #[must_use]
147    pub const fn new(start: f32, end: f32, top: f32, bottom: f32) -> Self {
148        Self { left: start, right: end, top, bottom }
149    }
150}
151
152/// The width and height of a [`Rect`]
153#[derive(Debug, Copy, Clone, PartialEq, Eq)]
154#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
155#[cfg_attr(feature = "serde", serde(default))]
156pub struct Size<T> {
157    /// The x extent of the rectangle
158    pub width: T,
159    /// The y extent of the rectangle
160    pub height: T,
161}
162
163impl<T> Size<T> {
164    /// Applies the function `f` to both the width and height
165    ///
166    /// This is used to transform a `Size<T>` into a `Size<R>`.
167    pub fn map<R, F>(self, f: F) -> Size<R>
168    where
169        F: Fn(T) -> R,
170    {
171        Size { width: f(self.width), height: f(self.height) }
172    }
173
174    /// Applies the function `f` to the width
175    pub fn map_width<F>(self, f: F) -> Size<T>
176    where
177        F: Fn(T) -> T,
178    {
179        Size { width: f(self.width), height: self.height }
180    }
181
182    /// Applies the function `f` to the height
183    pub fn map_height<F>(self, f: F) -> Size<T>
184    where
185        F: Fn(T) -> T,
186    {
187        Size { width: self.width, height: f(self.height) }
188    }
189
190    /// Applies the function `f` to both the width and height
191    /// of this value and another passed value
192    pub fn zip_map<Other, Ret, Func>(self, other: Size<Other>, f: Func) -> Size<Ret>
193    where
194        Func: Fn(T, Other) -> Ret,
195    {
196        Size { width: f(self.width, other.width), height: f(self.height, other.height) }
197    }
198
199    /// Sets the extent of the main layout axis
200    ///
201    /// Whether this is the width or height depends on the `direction` provided
202    pub(crate) fn set_main(&mut self, direction: FlexDirection, value: T) {
203        if direction.is_row() {
204            self.width = value
205        } else {
206            self.height = value
207        }
208    }
209
210    /// Sets the extent of the cross layout axis
211    ///
212    /// Whether this is the width or height depends on the `direction` provided
213    pub(crate) fn set_cross(&mut self, direction: FlexDirection, value: T) {
214        if direction.is_row() {
215            self.height = value
216        } else {
217            self.width = value
218        }
219    }
220
221    /// Gets the extent of the main layout axis
222    ///
223    /// Whether this is the width or height depends on the `direction` provided
224    pub(crate) fn main(self, direction: FlexDirection) -> T {
225        if direction.is_row() {
226            self.width
227        } else {
228            self.height
229        }
230    }
231
232    /// Gets the extent of the cross layout axis
233    ///
234    /// Whether this is the width or height depends on the `direction` provided
235    pub(crate) fn cross(self, direction: FlexDirection) -> T {
236        if direction.is_row() {
237            self.height
238        } else {
239            self.width
240        }
241    }
242}
243
244impl Size<f32> {
245    /// A [`Size`] with zero width and height
246    pub const ZERO: Size<f32> = Self { width: 0.0, height: 0.0 };
247}
248
249impl Size<Option<f32>> {
250    /// A [`Size`] with `None` width and height
251    pub const NONE: Size<Option<f32>> = Self { width: None, height: None };
252
253    /// A [`Size<Option<f32>>`] with `Some(width)` and `Some(height)` as parameters
254    #[must_use]
255    pub const fn new(width: f32, height: f32) -> Self {
256        Size { width: Some(width), height: Some(height) }
257    }
258
259    /// Performs Option::unwrap_or on each component separately
260    pub fn unwrap_or(self, alt: Size<f32>) -> Size<f32> {
261        Size { width: self.width.unwrap_or(alt.width), height: self.height.unwrap_or(alt.height) }
262    }
263
264    /// Performs Option::or on each component separately
265    pub fn or(self, alt: Size<Option<f32>>) -> Size<Option<f32>> {
266        Size { width: self.width.or(alt.width), height: self.height.or(alt.height) }
267    }
268}
269
270impl Size<Dimension> {
271    /// Generates a [`Size<Dimension>`] using [`Dimension::Points`] values
272    #[must_use]
273    pub const fn from_points(width: f32, height: f32) -> Self {
274        Size { width: Dimension::Points(width), height: Dimension::Points(height) }
275    }
276
277    /// Generates a [`Size<Dimension>`] using [`Dimension::Percent`] values
278    #[must_use]
279    pub const fn from_percent(width: f32, height: f32) -> Self {
280        Size { width: Dimension::Percent(width), height: Dimension::Percent(height) }
281    }
282
283    /// Generates a [`Size<Dimension>`] using [`Dimension::Auto`] in both width and height
284    pub const AUTO: Size<Dimension> = Self { width: Dimension::Auto, height: Dimension::Auto };
285
286    /// Generates a [`Size<Dimension>`] using [`Dimension::Undefined`] in both width and height
287    pub const UNDEFINED: Size<Dimension> = Self { width: Dimension::Undefined, height: Dimension::Undefined };
288}
289
290/// A 2-dimensional coordinate.
291///
292/// When used in association with a [`Rect`], represents the bottom-left corner.
293#[derive(Debug, Copy, Clone, PartialEq, Eq)]
294pub struct Point<T> {
295    /// The x-coordinate
296    pub x: T,
297    /// The y-coordinate
298    pub y: T,
299}
300
301impl Point<f32> {
302    /// A [`Point`] with values (0,0), representing the origin
303    pub const ZERO: Point<f32> = Self { x: 0.0, y: 0.0 };
304}