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