taffy/
geometry.rs

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