Skip to main content

taffy/
geometry.rs

1//! Geometric primitives useful for layout
2
3use crate::util::sys::f32_max;
4use crate::CompactLength;
5use crate::{style::Dimension, util::sys::f32_min};
6use core::ops::{Add, Sub};
7
8#[cfg(feature = "flexbox")]
9use crate::style::FlexDirection;
10
11/// The simple absolute horizontal and vertical axis
12#[derive(Copy, Clone, Debug, PartialEq, Eq)]
13pub enum AbsoluteAxis {
14    /// The horizontal axis
15    Horizontal,
16    /// The vertical axis
17    Vertical,
18}
19
20impl AbsoluteAxis {
21    /// Returns the other variant of the enum
22    #[inline]
23    pub const fn other_axis(&self) -> Self {
24        match *self {
25            AbsoluteAxis::Horizontal => AbsoluteAxis::Vertical,
26            AbsoluteAxis::Vertical => AbsoluteAxis::Horizontal,
27        }
28    }
29}
30
31impl<T> Size<T> {
32    #[inline(always)]
33    /// Get either the width or height depending on the AbsoluteAxis passed in
34    pub fn get_abs(self, axis: AbsoluteAxis) -> T {
35        match axis {
36            AbsoluteAxis::Horizontal => self.width,
37            AbsoluteAxis::Vertical => self.height,
38        }
39    }
40
41    /// Swap the width and height components
42    #[inline(always)]
43    pub fn transpose(self) -> Size<T> {
44        Size { width: self.height, height: self.width }
45    }
46}
47
48impl<T: Add> Rect<T> {
49    #[inline(always)]
50    /// Get either the width or height depending on the AbsoluteAxis passed in
51    pub fn grid_axis_sum(self, axis: AbsoluteAxis) -> <T as Add>::Output {
52        match axis {
53            AbsoluteAxis::Horizontal => self.left + self.right,
54            AbsoluteAxis::Vertical => self.top + self.bottom,
55        }
56    }
57}
58
59/// The CSS abstract axis
60/// <https://www.w3.org/TR/css-writing-modes-3/#abstract-axes>
61#[derive(Copy, Clone, Debug, PartialEq, Eq)]
62pub enum AbstractAxis {
63    /// The axis in the inline dimension, i.e. the horizontal axis in horizontal writing modes and the vertical axis in vertical writing modes.
64    Inline,
65    /// The axis in the block dimension, i.e. the vertical axis in horizontal writing modes and the horizontal axis in vertical writing modes.
66    Block,
67}
68
69impl AbstractAxis {
70    /// Returns the other variant of the enum
71    #[inline]
72    pub const fn other(&self) -> AbstractAxis {
73        match *self {
74            AbstractAxis::Inline => AbstractAxis::Block,
75            AbstractAxis::Block => AbstractAxis::Inline,
76        }
77    }
78
79    /// Convert an `AbstractAxis` into an `AbsoluteAxis` naively assuming that the Inline axis is Horizontal
80    /// This is currently always true, but will change if Taffy ever implements the `writing_mode` property
81    #[inline]
82    pub const fn as_abs_naive(&self) -> AbsoluteAxis {
83        match self {
84            AbstractAxis::Inline => AbsoluteAxis::Horizontal,
85            AbstractAxis::Block => AbsoluteAxis::Vertical,
86        }
87    }
88}
89
90/// Container that holds an item in each absolute axis without specifying
91/// what kind of item it is.
92#[derive(Clone, Copy, Debug, PartialEq, Eq)]
93pub(crate) struct InBothAbsAxis<T> {
94    /// The item in the horizontal axis
95    pub horizontal: T,
96    /// The item in the vertical axis
97    pub vertical: T,
98}
99
100impl<T: Copy> InBothAbsAxis<T> {
101    #[cfg(feature = "grid")]
102    /// Get the contained item based on the AbsoluteAxis passed
103    pub const fn get(&self, axis: AbsoluteAxis) -> T {
104        match axis {
105            AbsoluteAxis::Horizontal => self.horizontal,
106            AbsoluteAxis::Vertical => self.vertical,
107        }
108    }
109}
110
111/// An axis-aligned UI rectangle
112#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
113#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
114pub struct Rect<T> {
115    /// This can represent either the x-coordinate of the starting edge,
116    /// or the amount of padding on the starting side.
117    ///
118    /// The starting edge is the left edge when working with LTR text,
119    /// and the right edge when working with RTL text.
120    pub left: T,
121    /// This can represent either the x-coordinate of the ending edge,
122    /// or the amount of padding on the ending side.
123    ///
124    /// The ending edge is the right edge when working with LTR text,
125    /// and the left edge when working with RTL text.
126    pub right: T,
127    /// This can represent either the y-coordinate of the top edge,
128    /// or the amount of padding on the top side.
129    pub top: T,
130    /// This can represent either the y-coordinate of the bottom edge,
131    /// or the amount of padding on the bottom side.
132    pub bottom: T,
133}
134
135impl<U, T: Add<U>> Add<Rect<U>> for Rect<T> {
136    type Output = Rect<T::Output>;
137
138    fn add(self, rhs: Rect<U>) -> Self::Output {
139        Rect {
140            left: self.left + rhs.left,
141            right: self.right + rhs.right,
142            top: self.top + rhs.top,
143            bottom: self.bottom + rhs.bottom,
144        }
145    }
146}
147
148impl<T> Rect<T> {
149    /// Swap the horizontal and vertical sides (`left <-> top`, `right <-> bottom`)
150    #[inline(always)]
151    pub fn transpose(self) -> Rect<T> {
152        Rect { left: self.top, right: self.bottom, top: self.left, bottom: self.right }
153    }
154
155    /// Applies the function `f` to all four sides of the rect
156    ///
157    /// When applied to the left and right sides, the width is used
158    /// as the second parameter of `f`.
159    /// When applied to the top or bottom sides, the height is used instead.
160    #[cfg(any(feature = "flexbox", feature = "block_layout"))]
161    #[inline(always)]
162    pub(crate) fn zip_size<R, F, U>(self, size: Size<U>, f: F) -> Rect<R>
163    where
164        F: Fn(T, U) -> R,
165        U: Copy,
166    {
167        Rect {
168            left: f(self.left, size.width),
169            right: f(self.right, size.width),
170            top: f(self.top, size.height),
171            bottom: f(self.bottom, size.height),
172        }
173    }
174
175    /// Applies the function `f` to the left, right, top, and bottom properties
176    ///
177    /// This is used to transform a `Rect<T>` into a `Rect<R>`.
178    #[inline(always)]
179    pub fn map<R, F>(self, f: F) -> Rect<R>
180    where
181        F: Fn(T) -> R,
182    {
183        Rect { left: f(self.left), right: f(self.right), top: f(self.top), bottom: f(self.bottom) }
184    }
185
186    /// Returns a `Line<T>` representing the left and right properties of the Rect
187    pub fn horizontal_components(self) -> Line<T> {
188        Line { start: self.left, end: self.right }
189    }
190
191    /// Returns a `Line<T>` containing the top and bottom properties of the Rect
192    pub fn vertical_components(self) -> Line<T> {
193        Line { start: self.top, end: self.bottom }
194    }
195}
196
197impl<T, U> Rect<T>
198where
199    T: Add<Output = U> + Copy + Clone,
200{
201    /// The sum of [`Rect.start`](Rect) and [`Rect.end`](Rect)
202    ///
203    /// This is typically used when computing total padding.
204    ///
205    /// **NOTE:** this is *not* the width of the rectangle.
206    #[inline(always)]
207    pub fn horizontal_axis_sum(&self) -> U {
208        self.left + self.right
209    }
210
211    /// The sum of [`Rect.top`](Rect) and [`Rect.bottom`](Rect)
212    ///
213    /// This is typically used when computing total padding.
214    ///
215    /// **NOTE:** this is *not* the height of the rectangle.
216    #[inline(always)]
217    pub fn vertical_axis_sum(&self) -> U {
218        self.top + self.bottom
219    }
220
221    /// Both horizontal_axis_sum and vertical_axis_sum as a `Size<T>`
222    ///
223    /// **NOTE:** this is *not* the width/height of the rectangle.
224    #[inline(always)]
225    #[allow(dead_code)] // Fixes spurious clippy warning: this function is used!
226    pub fn sum_axes(&self) -> Size<U> {
227        Size { width: self.horizontal_axis_sum(), height: self.vertical_axis_sum() }
228    }
229
230    /// The sum of the two fields of the [`Rect`] representing the main axis.
231    ///
232    /// This is typically used when computing total padding.
233    ///
234    /// If the [`FlexDirection`] is [`FlexDirection::Row`] or [`FlexDirection::RowReverse`], this is [`Rect::horizontal`].
235    /// Otherwise, this is [`Rect::vertical`].
236    #[cfg(feature = "flexbox")]
237    pub(crate) fn main_axis_sum(&self, direction: FlexDirection) -> U {
238        if direction.is_row() {
239            self.horizontal_axis_sum()
240        } else {
241            self.vertical_axis_sum()
242        }
243    }
244
245    /// The sum of the two fields of the [`Rect`] representing the cross axis.
246    ///
247    /// If the [`FlexDirection`] is [`FlexDirection::Row`] or [`FlexDirection::RowReverse`], this is [`Rect::vertical`].
248    /// Otherwise, this is [`Rect::horizontal`].
249    #[cfg(feature = "flexbox")]
250    pub(crate) fn cross_axis_sum(&self, direction: FlexDirection) -> U {
251        if direction.is_row() {
252            self.vertical_axis_sum()
253        } else {
254            self.horizontal_axis_sum()
255        }
256    }
257}
258
259impl<T> Rect<T>
260where
261    T: Copy + Clone,
262{
263    /// The `start` or `top` value of the [`Rect`], from the perspective of the main layout axis
264    #[cfg(feature = "flexbox")]
265    pub(crate) const fn main_start(&self, direction: FlexDirection) -> T {
266        if direction.is_row() {
267            self.left
268        } else {
269            self.top
270        }
271    }
272
273    /// The `end` or `bottom` value of the [`Rect`], from the perspective of the main layout axis
274    #[cfg(feature = "flexbox")]
275    pub(crate) const fn main_end(&self, direction: FlexDirection) -> T {
276        if direction.is_row() {
277            self.right
278        } else {
279            self.bottom
280        }
281    }
282
283    /// The `start` or `top` value of the [`Rect`], from the perspective of the cross layout axis
284    #[cfg(feature = "flexbox")]
285    pub(crate) const fn cross_start(&self, direction: FlexDirection) -> T {
286        if direction.is_row() {
287            self.top
288        } else {
289            self.left
290        }
291    }
292
293    /// The `end` or `bottom` value of the [`Rect`], from the perspective of the main layout axis
294    #[cfg(feature = "flexbox")]
295    pub(crate) const fn cross_end(&self, direction: FlexDirection) -> T {
296        if direction.is_row() {
297            self.bottom
298        } else {
299            self.right
300        }
301    }
302}
303
304impl Rect<Option<f32>> {
305    /// A [`Rect`] with `None` as all parameters
306    pub const NONE: Rect<Option<f32>> = Self { left: None, right: None, top: None, bottom: None };
307}
308
309impl Rect<f32> {
310    /// Creates a new Rect with `0.0` as all parameters
311    pub const ZERO: Rect<f32> = Self { left: 0.0, right: 0.0, top: 0.0, bottom: 0.0 };
312
313    /// Creates a new Rect
314    #[must_use]
315    pub const fn new(start: f32, end: f32, top: f32, bottom: f32) -> Self {
316        Self { left: start, right: end, top, bottom }
317    }
318
319    /// Returns the smallest rectangle that contains both `self` and `other`, where the fields
320    /// of each `Rect` are interpreted as edge coordinates (`left`/`top` being the minimum
321    /// coordinates and `right`/`bottom` the maximum coordinates of the rectangle)
322    #[must_use]
323    pub fn union(self, other: Self) -> Self {
324        Self {
325            left: self.left.min(other.left),
326            right: self.right.max(other.right),
327            top: self.top.min(other.top),
328            bottom: self.bottom.max(other.bottom),
329        }
330    }
331}
332
333/// An abstract "line". Represents any type that has a start and an end
334#[derive(Debug, Copy, Clone, PartialEq, Eq)]
335#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
336#[cfg_attr(feature = "serde", serde(default))]
337pub struct Line<T> {
338    /// The start position of a line
339    pub start: T,
340    /// The end position of a line
341    pub end: T,
342}
343
344impl<T> Line<T> {
345    /// Applies the function `f` to both the width and height
346    ///
347    /// This is used to transform a `Line<T>` into a `Line<R>`.
348    pub fn map<R, F>(self, f: F) -> Line<R>
349    where
350        F: Fn(T) -> R,
351    {
352        Line { start: f(self.start), end: f(self.end) }
353    }
354}
355
356impl Line<bool> {
357    /// A `Line<bool>` with both start and end set to `true`
358    pub const TRUE: Self = Line { start: true, end: true };
359    /// A `Line<bool>` with both start and end set to `false`
360    pub const FALSE: Self = Line { start: false, end: false };
361}
362
363impl<T: Add + Copy> Line<T> {
364    /// Adds the start and end values together and returns the result
365    pub fn sum(&self) -> <T as Add>::Output {
366        self.start + self.end
367    }
368}
369
370/// The width and height of a [`Rect`]
371#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
372#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
373pub struct Size<T> {
374    /// The x extent of the rectangle
375    pub width: T,
376    /// The y extent of the rectangle
377    pub height: T,
378}
379
380// Generic Add impl for Size<T> + Size<U> where T + U has an Add impl
381impl<U, T: Add<U>> Add<Size<U>> for Size<T> {
382    type Output = Size<<T as Add<U>>::Output>;
383
384    fn add(self, rhs: Size<U>) -> Self::Output {
385        Size { width: self.width + rhs.width, height: self.height + rhs.height }
386    }
387}
388
389// Generic Sub impl for Size<T> + Size<U> where T + U has an Sub impl
390impl<U, T: Sub<U>> Sub<Size<U>> for Size<T> {
391    type Output = Size<<T as Sub<U>>::Output>;
392
393    fn sub(self, rhs: Size<U>) -> Self::Output {
394        Size { width: self.width - rhs.width, height: self.height - rhs.height }
395    }
396}
397
398// Note: we allow dead_code here as we want to provide a complete API of helpers that is symmetrical in all axes,
399// but sometimes we only currently have a use for the helper in a single axis
400#[allow(dead_code)]
401impl<T> Size<T> {
402    /// Applies the function `f` to both the width and height
403    ///
404    /// This is used to transform a `Size<T>` into a `Size<R>`.
405    pub fn map<R, F>(self, f: F) -> Size<R>
406    where
407        F: Fn(T) -> R,
408    {
409        Size { width: f(self.width), height: f(self.height) }
410    }
411
412    /// Applies the function `f` to the width
413    pub fn map_width<F>(self, f: F) -> Size<T>
414    where
415        F: Fn(T) -> T,
416    {
417        Size { width: f(self.width), height: self.height }
418    }
419
420    /// Applies the function `f` to the height
421    pub fn map_height<F>(self, f: F) -> Size<T>
422    where
423        F: Fn(T) -> T,
424    {
425        Size { width: self.width, height: f(self.height) }
426    }
427
428    /// Applies the function `f` to both the width and height
429    /// of this value and another passed value
430    pub fn zip_map<Other, Ret, Func>(self, other: Size<Other>, f: Func) -> Size<Ret>
431    where
432        Func: Fn(T, Other) -> Ret,
433    {
434        Size { width: f(self.width, other.width), height: f(self.height, other.height) }
435    }
436
437    /// Sets the extent of the main layout axis
438    ///
439    /// Whether this is the width or height depends on the `direction` provided
440    #[cfg(feature = "flexbox")]
441    pub(crate) fn set_main(&mut self, direction: FlexDirection, value: T) {
442        if direction.is_row() {
443            self.width = value
444        } else {
445            self.height = value
446        }
447    }
448
449    /// Sets the extent of the cross layout axis
450    ///
451    /// Whether this is the width or height depends on the `direction` provided
452    #[cfg(feature = "flexbox")]
453    pub(crate) fn set_cross(&mut self, direction: FlexDirection, value: T) {
454        if direction.is_row() {
455            self.height = value
456        } else {
457            self.width = value
458        }
459    }
460
461    /// Creates a new value of type Self with the main axis set to value provided
462    ///
463    /// Whether this is the width or height depends on the `direction` provided
464    #[cfg(feature = "flexbox")]
465    pub(crate) fn with_main(self, direction: FlexDirection, value: T) -> Self {
466        let mut new = self;
467        if direction.is_row() {
468            new.width = value
469        } else {
470            new.height = value
471        }
472        new
473    }
474
475    /// Creates a new value of type Self with the cross axis set to value provided
476    ///
477    /// Whether this is the width or height depends on the `direction` provided
478    #[cfg(feature = "flexbox")]
479    pub(crate) fn with_cross(self, direction: FlexDirection, value: T) -> Self {
480        let mut new = self;
481        if direction.is_row() {
482            new.height = value
483        } else {
484            new.width = value
485        }
486        new
487    }
488
489    /// Creates a new value of type Self with the main axis modified by the callback provided
490    ///
491    /// Whether this is the width or height depends on the `direction` provided
492    #[cfg(feature = "flexbox")]
493    pub(crate) fn map_main(self, direction: FlexDirection, mapper: impl FnOnce(T) -> T) -> Self {
494        let mut new = self;
495        if direction.is_row() {
496            new.width = mapper(new.width);
497        } else {
498            new.height = mapper(new.height);
499        }
500        new
501    }
502
503    /// Creates a new value of type Self with the cross axis modified by the callback provided
504    ///
505    /// Whether this is the width or height depends on the `direction` provided
506    #[cfg(feature = "flexbox")]
507    pub(crate) fn map_cross(self, direction: FlexDirection, mapper: impl FnOnce(T) -> T) -> Self {
508        let mut new = self;
509        if direction.is_row() {
510            new.height = mapper(new.height);
511        } else {
512            new.width = mapper(new.width);
513        }
514        new
515    }
516
517    /// Gets the extent of the main layout axis
518    ///
519    /// Whether this is the width or height depends on the `direction` provided
520    #[cfg(feature = "flexbox")]
521    pub(crate) fn main(self, direction: FlexDirection) -> T {
522        if direction.is_row() {
523            self.width
524        } else {
525            self.height
526        }
527    }
528
529    /// Gets the extent of the cross layout axis
530    ///
531    /// Whether this is the width or height depends on the `direction` provided
532    #[cfg(feature = "flexbox")]
533    pub(crate) fn cross(self, direction: FlexDirection) -> T {
534        if direction.is_row() {
535            self.height
536        } else {
537            self.width
538        }
539    }
540
541    /// Gets the extent of the specified layout axis
542    /// Whether this is the width or height depends on the `GridAxis` provided
543    #[cfg(feature = "grid")]
544    pub(crate) fn get(self, axis: AbstractAxis) -> T {
545        match axis {
546            AbstractAxis::Inline => self.width,
547            AbstractAxis::Block => self.height,
548        }
549    }
550
551    /// Sets the extent of the specified layout axis
552    /// Whether this is the width or height depends on the `GridAxis` provided
553    #[cfg(feature = "grid")]
554    pub(crate) fn set(&mut self, axis: AbstractAxis, value: T) {
555        match axis {
556            AbstractAxis::Inline => self.width = value,
557            AbstractAxis::Block => self.height = value,
558        }
559    }
560
561    /// Sets the extent of the specified layout axis
562    /// Whether this is the width or height depends on the `GridAxis` provided
563    #[cfg(feature = "grid")]
564    pub(crate) fn with(mut self, axis: AbstractAxis, value: T) -> Self {
565        match axis {
566            AbstractAxis::Inline => self.width = value,
567            AbstractAxis::Block => self.height = value,
568        }
569        self
570    }
571}
572
573impl Size<f32> {
574    /// A [`Size`] with zero width and height
575    pub const ZERO: Size<f32> = Self { width: 0.0, height: 0.0 };
576
577    /// Applies f32_max to each component separately
578    #[inline(always)]
579    pub fn f32_max(self, rhs: Size<f32>) -> Size<f32> {
580        Size { width: f32_max(self.width, rhs.width), height: f32_max(self.height, rhs.height) }
581    }
582
583    /// Applies f32_min to each component separately
584    #[inline(always)]
585    pub fn f32_min(self, rhs: Size<f32>) -> Size<f32> {
586        Size { width: f32_min(self.width, rhs.width), height: f32_min(self.height, rhs.height) }
587    }
588
589    /// Return true if both width and height are greater than 0 else false
590    #[inline(always)]
591    pub fn has_non_zero_area(self) -> bool {
592        self.width > 0.0 && self.height > 0.0
593    }
594}
595
596impl Size<Option<f32>> {
597    /// A [`Size`] with `None` width and height
598    pub const NONE: Size<Option<f32>> = Self { width: None, height: None };
599
600    /// A [`Size<Option<f32>>`] with `Some(width)` and `Some(height)` as parameters
601    #[must_use]
602    pub const fn new(width: f32, height: f32) -> Self {
603        Size { width: Some(width), height: Some(height) }
604    }
605
606    /// Creates a new [`Size<Option<f32>>`] with either the width or height set based on the provided `direction`
607    #[cfg(feature = "flexbox")]
608    pub const fn from_cross(direction: FlexDirection, value: Option<f32>) -> Self {
609        let mut new = Self::NONE;
610        if direction.is_row() {
611            new.height = value
612        } else {
613            new.width = value
614        }
615        new
616    }
617
618    /// Applies aspect_ratio (if one is supplied) to the Size:
619    ///   - If width is `Some` but height is `None`, then height is computed from width and aspect_ratio
620    ///   - If height is `Some` but width is `None`, then width is computed from height and aspect_ratio
621    ///
622    /// If aspect_ratio is `None` then this function simply returns self.
623    pub fn maybe_apply_aspect_ratio(self, aspect_ratio: Option<f32>) -> Size<Option<f32>> {
624        match aspect_ratio {
625            Some(ratio) => match (self.width, self.height) {
626                (Some(width), None) => Size { width: Some(width), height: Some(width / ratio) },
627                (None, Some(height)) => Size { width: Some(height * ratio), height: Some(height) },
628                _ => self,
629            },
630            None => self,
631        }
632    }
633}
634
635impl<T> Size<Option<T>> {
636    /// Performs Option::unwrap_or on each component separately
637    pub fn unwrap_or(self, alt: Size<T>) -> Size<T> {
638        Size { width: self.width.unwrap_or(alt.width), height: self.height.unwrap_or(alt.height) }
639    }
640
641    /// Performs Option::or on each component separately
642    pub fn or(self, alt: Size<Option<T>>) -> Size<Option<T>> {
643        Size { width: self.width.or(alt.width), height: self.height.or(alt.height) }
644    }
645
646    /// Return true if both components are Some, else false.
647    #[inline(always)]
648    pub fn both_axis_defined(&self) -> bool {
649        self.width.is_some() && self.height.is_some()
650    }
651}
652
653impl Size<Dimension> {
654    /// Generates a [`Size<Dimension>`] using length values
655    #[must_use]
656    pub const fn from_lengths(width: f32, height: f32) -> Self {
657        Size { width: Dimension(CompactLength::length(width)), height: Dimension(CompactLength::length(height)) }
658    }
659
660    /// Generates a [`Size<Dimension>`] using percentage values
661    #[must_use]
662    pub const fn from_percent(width: f32, height: f32) -> Self {
663        Size { width: Dimension(CompactLength::percent(width)), height: Dimension(CompactLength::percent(height)) }
664    }
665}
666
667/// A 2-dimensional coordinate.
668///
669/// When used in association with a [`Rect`], represents the top-left corner.
670#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
671#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
672pub struct Point<T> {
673    /// The x-coordinate
674    pub x: T,
675    /// The y-coordinate
676    pub y: T,
677}
678
679impl Point<f32> {
680    /// A [`Point`] with values (0,0), representing the origin
681    pub const ZERO: Self = Self { x: 0.0, y: 0.0 };
682}
683
684impl Point<Option<f32>> {
685    /// A [`Point`] with values (None, None)
686    pub const NONE: Self = Self { x: None, y: None };
687}
688
689// Generic Add impl for Point<T> + Point<U> where T + U has an Add impl
690impl<U, T: Add<U>> Add<Point<U>> for Point<T> {
691    type Output = Point<<T as Add<U>>::Output>;
692
693    fn add(self, rhs: Point<U>) -> Self::Output {
694        Point { x: self.x + rhs.x, y: self.y + rhs.y }
695    }
696}
697
698impl<T> Point<T> {
699    /// Applies the function `f` to both the x and y
700    ///
701    /// This is used to transform a `Point<T>` into a `Point<R>`.
702    pub fn map<R, F>(self, f: F) -> Point<R>
703    where
704        F: Fn(T) -> R,
705    {
706        Point { x: f(self.x), y: f(self.y) }
707    }
708
709    /// Gets the extent of the specified layout axis
710    /// Whether this is the width or height depends on the `GridAxis` provided
711    #[cfg(feature = "grid")]
712    pub fn get(self, axis: AbstractAxis) -> T {
713        match axis {
714            AbstractAxis::Inline => self.x,
715            AbstractAxis::Block => self.y,
716        }
717    }
718
719    /// Swap x and y components
720    pub fn transpose(self) -> Point<T> {
721        Point { x: self.y, y: self.x }
722    }
723
724    /// Sets the extent of the specified layout axis
725    /// Whether this is the width or height depends on the `GridAxis` provided
726    #[cfg(feature = "grid")]
727    pub fn set(&mut self, axis: AbstractAxis, value: T) {
728        match axis {
729            AbstractAxis::Inline => self.x = value,
730            AbstractAxis::Block => self.y = value,
731        }
732    }
733
734    /// Gets the component in the main layout axis
735    ///
736    /// Whether this is the x or y depends on the `direction` provided
737    #[cfg(feature = "flexbox")]
738    pub(crate) fn main(self, direction: FlexDirection) -> T {
739        if direction.is_row() {
740            self.x
741        } else {
742            self.y
743        }
744    }
745
746    /// Gets the component in the cross layout axis
747    ///
748    /// Whether this is the x or y depends on the `direction` provided
749    #[cfg(feature = "flexbox")]
750    pub(crate) fn cross(self, direction: FlexDirection) -> T {
751        if direction.is_row() {
752            self.y
753        } else {
754            self.x
755        }
756    }
757}
758
759impl<T> From<Point<T>> for Size<T> {
760    fn from(value: Point<T>) -> Self {
761        Size { width: value.x, height: value.y }
762    }
763}
764
765/// Generic struct which holds a "min" value and a "max" value
766#[derive(Debug, Copy, Clone, PartialEq, Eq)]
767#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
768pub struct MinMax<Min, Max> {
769    /// The value representing the minimum
770    pub min: Min,
771    /// The value representing the maximum
772    pub max: Max,
773}