1use std::cmp::Ordering;
2use std::ops::Mul;
3
4use crate::traits::{IntoComponents, StdNumOps};
5use crate::utils::vec_ord;
6use crate::Point;
7
8#[derive(Default, Clone, Copy, Eq, PartialEq, Hash, Debug)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct Size<Unit> {
12 pub width: Unit,
14 pub height: Unit,
16}
17
18impl<Unit> Size<Unit> {
19 pub const fn new(width: Unit, height: Unit) -> Self {
21 Self { width, height }
22 }
23
24 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 pub fn area(&self) -> <Unit as Mul>::Output
37 where
38 Unit: Mul + Copy,
39 {
40 self.width * self.height
41 }
42
43 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 #[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 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}