1use std::fmt;
4use serde::{Serialize, Deserialize};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
9pub struct Size {
10 pub width: i32,
11 pub height: i32,
12}
13
14impl Size {
15 pub fn new(width: i32, height: i32) -> Self {
17 Self { width, height }
18 }
19
20 pub fn square(size: i32) -> Self {
22 Self::new(size, size)
23 }
24
25 pub fn area(&self) -> i32 {
27 self.width * self.height
28 }
29
30 pub fn is_empty(&self) -> bool {
32 self.width <= 0 || self.height <= 0
33 }
34
35 pub fn aspect_ratio(&self) -> f64 {
37 if self.height != 0 {
38 self.width as f64 / self.height as f64
39 } else {
40 0.0
41 }
42 }
43}
44
45impl Default for Size {
46 fn default() -> Self {
47 Self::new(0, 0)
48 }
49}
50
51impl fmt::Display for Size {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 write!(f, "{}x{}", self.width, self.height)
54 }
55}
56
57impl From<(i32, i32)> for Size {
58 fn from((width, height): (i32, i32)) -> Self {
59 Self::new(width, height)
60 }
61}
62
63impl From<Size> for (i32, i32) {
64 fn from(size: Size) -> Self {
65 (size.width, size.height)
66 }
67}
68
69#[derive(Debug, Clone, Copy, PartialEq)]
71#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
72pub struct Size2f {
73 pub width: f32,
74 pub height: f32,
75}
76
77impl Size2f {
78 pub fn new(width: f32, height: f32) -> Self {
80 Self { width, height }
81 }
82
83 pub fn square(size: f32) -> Self {
85 Self::new(size, size)
86 }
87
88 pub fn area(&self) -> f32 {
90 self.width * self.height
91 }
92
93 pub fn is_empty(&self) -> bool {
95 self.width <= 0.0 || self.height <= 0.0
96 }
97
98 pub fn aspect_ratio(&self) -> f32 {
100 if self.height != 0.0 {
101 self.width / self.height
102 } else {
103 0.0
104 }
105 }
106}
107
108impl Default for Size2f {
109 fn default() -> Self {
110 Self::new(0.0, 0.0)
111 }
112}
113
114impl fmt::Display for Size2f {
115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 write!(f, "{:.2}x{:.2}", self.width, self.height)
117 }
118}
119
120impl From<(f32, f32)> for Size2f {
121 fn from((width, height): (f32, f32)) -> Self {
122 Self::new(width, height)
123 }
124}
125
126impl From<Size2f> for (f32, f32) {
127 fn from(size: Size2f) -> Self {
128 (size.width, size.height)
129 }
130}
131
132impl From<Size> for Size2f {
133 fn from(size: Size) -> Self {
134 Self::new(size.width as f32, size.height as f32)
135 }
136}
137
138#[derive(Debug, Clone, Copy, PartialEq)]
140#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
141pub struct Size2d {
142 pub width: f64,
143 pub height: f64,
144}
145
146impl Size2d {
147 pub fn new(width: f64, height: f64) -> Self {
149 Self { width, height }
150 }
151
152 pub fn square(size: f64) -> Self {
154 Self::new(size, size)
155 }
156
157 pub fn area(&self) -> f64 {
159 self.width * self.height
160 }
161
162 pub fn is_empty(&self) -> bool {
164 self.width <= 0.0 || self.height <= 0.0
165 }
166
167 pub fn aspect_ratio(&self) -> f64 {
169 if self.height != 0.0 {
170 self.width / self.height
171 } else {
172 0.0
173 }
174 }
175}
176
177impl Default for Size2d {
178 fn default() -> Self {
179 Self::new(0.0, 0.0)
180 }
181}
182
183impl fmt::Display for Size2d {
184 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185 write!(f, "{:.6}x{:.6}", self.width, self.height)
186 }
187}
188
189impl From<(f64, f64)> for Size2d {
190 fn from((width, height): (f64, f64)) -> Self {
191 Self::new(width, height)
192 }
193}
194
195impl From<Size2d> for (f64, f64) {
196 fn from(size: Size2d) -> Self {
197 (size.width, size.height)
198 }
199}
200
201impl From<Size2f> for Size2d {
202 fn from(size: Size2f) -> Self {
203 Self::new(size.width as f64, size.height as f64)
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210
211 #[test]
212 fn test_size_creation() {
213 let size = Size::new(640, 480);
214 assert_eq!(size.width, 640);
215 assert_eq!(size.height, 480);
216 assert_eq!(size.area(), 307200);
217 }
218
219 #[test]
220 fn test_size_square() {
221 let size = Size::square(100);
222 assert_eq!(size.width, 100);
223 assert_eq!(size.height, 100);
224 assert_eq!(size.area(), 10000);
225 }
226
227 #[test]
228 fn test_size_aspect_ratio() {
229 let size = Size::new(16, 9);
230 assert!((size.aspect_ratio() - 16.0/9.0).abs() < 1e-10);
231 }
232
233 #[test]
234 fn test_size_empty() {
235 let empty_size = Size::new(0, 0);
236 assert!(empty_size.is_empty());
237
238 let valid_size = Size::new(10, 10);
239 assert!(!valid_size.is_empty());
240 }
241
242 #[test]
243 fn test_size_conversions() {
244 let size = Size::new(100, 200);
245 let tuple: (i32, i32) = size.into();
246 assert_eq!(tuple, (100, 200));
247
248 let size2: Size = (300, 400).into();
249 assert_eq!(size2.width, 300);
250 assert_eq!(size2.height, 400);
251 }
252}