figures/
size.rs

1use std::cmp::Ordering;
2use std::ops::Mul;
3
4use crate::traits::{IntoComponents, StdNumOps};
5use crate::utils::vec_ord;
6use crate::Point;
7
8/// A width and a height measurement.
9#[derive(Default, Clone, Copy, Eq, PartialEq, Hash, Debug)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct Size<Unit> {
12    /// The width component
13    pub width: Unit,
14    /// The height component
15    pub height: Unit,
16}
17
18impl<Unit> Size<Unit> {
19    /// Returns a new size of the given `width` and `height`.
20    pub const fn new(width: Unit, height: Unit) -> Self {
21        Self { width, height }
22    }
23
24    /// Returns a new size using `dimension` for both width and height.
25    pub fn squared(dimension: Unit) -> Self
26    where
27        Unit: Clone,
28    {
29        Self {
30            width: dimension.clone(),
31            height: dimension,
32        }
33    }
34
35    /// Returns the area of the rectangle.
36    pub fn area(&self) -> <Unit as Mul>::Output
37    where
38        Unit: Mul + Copy,
39    {
40        self.width * self.height
41    }
42
43    /// Converts the contents of this size to `NewUnit` using [`From`].
44    pub fn cast<NewUnit>(self) -> Size<NewUnit>
45    where
46        NewUnit: From<Unit>,
47    {
48        Size {
49            width: self.width.into(),
50            height: self.height.into(),
51        }
52    }
53
54    /// Maps each component to `map` and returns a new value with the mapped
55    /// components.
56    #[must_use]
57    pub fn map<NewUnit>(self, mut map: impl FnMut(Unit) -> NewUnit) -> Size<NewUnit> {
58        Size {
59            width: map(self.width),
60            height: map(self.height),
61        }
62    }
63
64    /// Converts the contents of this size to `NewUnit` using [`TryFrom`].
65    ///
66    /// # Errors
67    ///
68    /// Returns `<NewUnit as TryFrom>::Error` when the inner type cannot be
69    /// converted. For this crate's types, this genenerally will be
70    pub fn try_cast<NewUnit>(self) -> Result<Size<NewUnit>, NewUnit::Error>
71    where
72        NewUnit: TryFrom<Unit>,
73    {
74        Ok(Size {
75            width: self.width.try_into()?,
76            height: self.height.try_into()?,
77        })
78    }
79}
80
81impl<Unit> Ord for Size<Unit>
82where
83    Unit: Ord + Mul<Output = Unit> + Copy,
84{
85    fn cmp(&self, other: &Self) -> Ordering {
86        vec_ord::<Unit>((*self).into_components(), (*other).into_components())
87    }
88
89    fn max(self, other: Self) -> Self
90    where
91        Self: Sized,
92    {
93        Self {
94            width: self.width.max(other.width),
95            height: self.height.max(other.height),
96        }
97    }
98
99    fn min(self, other: Self) -> Self
100    where
101        Self: Sized,
102    {
103        Self {
104            width: self.width.min(other.width),
105            height: self.height.min(other.height),
106        }
107    }
108
109    fn clamp(self, min: Self, max: Self) -> Self
110    where
111        Self: Sized,
112    {
113        Self {
114            width: self.width.clamp(min.width, max.width),
115            height: self.height.clamp(min.height, max.height),
116        }
117    }
118}
119
120impl<Unit> PartialOrd for Size<Unit>
121where
122    Unit: Ord + Mul<Output = Unit> + Copy,
123{
124    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
125        Some(self.cmp(other))
126    }
127}
128
129impl_2d_math!(Size, width, height);
130
131impl<Unit> From<Size<Unit>> for Point<Unit> {
132    fn from(value: Size<Unit>) -> Self {
133        value.to_vec()
134    }
135}
136
137impl<Unit> From<Point<Unit>> for Size<Unit> {
138    fn from(value: Point<Unit>) -> Self {
139        value.to_vec()
140    }
141}
142
143#[cfg(feature = "wgpu")]
144impl From<Size<crate::units::UPx>> for wgpu::Extent3d {
145    fn from(value: Size<crate::units::UPx>) -> Self {
146        Self {
147            width: value.width.into(),
148            height: value.height.into(),
149            depth_or_array_layers: 1,
150        }
151    }
152}
153
154#[cfg(feature = "winit")]
155impl From<winit::dpi::PhysicalSize<u32>> for Size<crate::units::UPx> {
156    fn from(value: winit::dpi::PhysicalSize<u32>) -> Self {
157        Self {
158            width: value.width.try_into().expect("width too large"),
159            height: value.height.try_into().expect("height too large"),
160        }
161    }
162}
163
164#[cfg(feature = "winit")]
165impl From<winit::dpi::PhysicalSize<i32>> for Size<crate::units::Px> {
166    fn from(value: winit::dpi::PhysicalSize<i32>) -> Self {
167        Self {
168            width: value.width.try_into().expect("width too large"),
169            height: value.height.try_into().expect("height too large"),
170        }
171    }
172}
173
174#[cfg(feature = "winit")]
175impl From<Size<crate::units::UPx>> for winit::dpi::PhysicalSize<u32> {
176    fn from(size: Size<crate::units::UPx>) -> Self {
177        Self {
178            width: size.width.into(),
179            height: size.height.into(),
180        }
181    }
182}
183
184#[cfg(feature = "winit")]
185impl From<Size<crate::units::Px>> for winit::dpi::PhysicalSize<i32> {
186    fn from(size: Size<crate::units::Px>) -> Self {
187        Self {
188            width: size.width.into(),
189            height: size.height.into(),
190        }
191    }
192}
193
194impl<T> StdNumOps for Size<T>
195where
196    T: StdNumOps,
197{
198    fn saturating_add(self, other: Self) -> Self {
199        Self::new(
200            self.width.saturating_add(other.width),
201            self.height.saturating_add(other.height),
202        )
203    }
204
205    fn saturating_mul(self, other: Self) -> Self {
206        Self::new(
207            self.width.saturating_mul(other.width),
208            self.height.saturating_mul(other.height),
209        )
210    }
211
212    fn saturating_div(self, other: Self) -> Self {
213        Self::new(
214            self.width.saturating_div(other.width),
215            self.height.saturating_div(other.height),
216        )
217    }
218
219    fn saturating_sub(self, other: Self) -> Self {
220        Self::new(
221            self.width.saturating_sub(other.width),
222            self.height.saturating_sub(other.height),
223        )
224    }
225}