1use crate::kurbo::Rect;
7use bytemuck::{Pod, Zeroable};
8use core::num::TryFromIntError;
9use core::ops::Add;
10
11#[repr(C)]
13#[derive(Copy, Clone, Debug, Pod, Zeroable, PartialEq, Eq)]
14pub struct SizeU16(pub [u16; 2]);
15
16impl SizeU16 {
17 pub const ZERO: Self = Self::new(0);
19
20 pub const fn new(size: u16) -> Self {
22 Self([size; 2])
23 }
24
25 pub const fn from_wh(width: u16, height: u16) -> Self {
27 Self([width, height])
28 }
29
30 pub const fn width(self) -> u16 {
32 self.0[0]
33 }
34
35 pub const fn height(self) -> u16 {
37 self.0[1]
38 }
39
40 pub fn max(self, other: Self) -> Self {
42 Self::from_wh(
43 self.width().max(other.width()),
44 self.height().max(other.height()),
45 )
46 }
47
48 pub fn min(self, other: Self) -> Self {
50 Self::from_wh(
51 self.width().min(other.width()),
52 self.height().min(other.height()),
53 )
54 }
55
56 pub fn clamp(self, min: u16, max: u16) -> Self {
58 Self::from_wh(self.width().clamp(min, max), self.height().clamp(min, max))
59 }
60}
61
62impl From<[u16; 2]> for SizeU16 {
63 fn from(value: [u16; 2]) -> Self {
64 Self(value)
65 }
66}
67
68impl Add for SizeU16 {
69 type Output = Self;
70
71 fn add(self, rhs: Self) -> Self::Output {
72 Self::from_wh(
74 self.width().checked_add(rhs.width()).unwrap(),
75 self.height().checked_add(rhs.height()).unwrap(),
76 )
77 }
78}
79
80impl Add<u16> for SizeU16 {
81 type Output = Self;
82
83 fn add(self, rhs: u16) -> Self::Output {
84 self + Self::new(rhs)
85 }
86}
87
88#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
90pub struct PaddingU16 {
91 pub left: u16,
93 pub top: u16,
95 pub right: u16,
97 pub bottom: u16,
99}
100
101impl PaddingU16 {
102 pub const ZERO: Self = Self::new(0, 0, 0, 0);
104
105 pub const fn new(left: u16, top: u16, right: u16, bottom: u16) -> Self {
107 Self {
108 left,
109 top,
110 right,
111 bottom,
112 }
113 }
114}
115
116#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
122pub struct RectU16 {
123 pub x0: u16,
125 pub y0: u16,
127 pub x1: u16,
129 pub y1: u16,
131}
132
133impl RectU16 {
134 pub const ZERO: Self = Self {
136 x0: 0,
137 y0: 0,
138 x1: 0,
139 y1: 0,
140 };
141
142 pub const INVERTED: Self = Self {
147 x0: u16::MAX,
148 y0: u16::MAX,
149 x1: 0,
150 y1: 0,
151 };
152
153 #[inline(always)]
155 pub const fn new(x0: u16, y0: u16, x1: u16, y1: u16) -> Self {
156 Self { x0, y0, x1, y1 }
157 }
158
159 #[inline(always)]
161 pub const fn width(self) -> u16 {
162 self.x1.saturating_sub(self.x0)
163 }
164
165 #[inline(always)]
167 pub const fn height(self) -> u16 {
168 self.y1.saturating_sub(self.y0)
169 }
170
171 #[inline(always)]
173 pub const fn is_empty(self) -> bool {
174 self.x0 >= self.x1 || self.y0 >= self.y1
175 }
176
177 #[inline(always)]
181 pub const fn contains(self, x: u16, y: u16) -> bool {
182 (x >= self.x0) & (x < self.x1) & (y >= self.y0) & (y < self.y1)
183 }
184
185 #[inline(always)]
189 pub const fn intersect(self, other: Self) -> Self {
190 let x0 = const_max(self.x0, other.x0);
191 let y0 = const_max(self.y0, other.y0);
192 let x1 = const_min(self.x1, other.x1);
193 let y1 = const_min(self.y1, other.y1);
194
195 Self::new(x0, y0, const_max(x1, x0), const_max(y1, y0))
196 }
197
198 #[inline(always)]
200 pub const fn expand(self, padding: PaddingU16) -> Self {
201 Self {
202 x0: self.x0.saturating_sub(padding.left),
203 y0: self.y0.saturating_sub(padding.top),
204 x1: self.x1.saturating_add(padding.right),
205 y1: self.y1.saturating_add(padding.bottom),
206 }
207 }
208
209 #[inline(always)]
211 pub fn relative_to_origin(self, origin: (u16, u16)) -> Self {
212 self.shift((-(origin.0 as i32), -(origin.1 as i32)))
213 }
214
215 #[inline]
217 pub fn shift(self, shift: (i32, i32)) -> Self {
218 Self {
219 x0: (self.x0 as i32)
220 .saturating_add(shift.0)
221 .clamp(0, u16::MAX as i32) as u16,
222 y0: (self.y0 as i32)
223 .saturating_add(shift.1)
224 .clamp(0, u16::MAX as i32) as u16,
225 x1: (self.x1 as i32)
226 .saturating_add(shift.0)
227 .clamp(0, u16::MAX as i32) as u16,
228 y1: (self.y1 as i32)
229 .saturating_add(shift.1)
230 .clamp(0, u16::MAX as i32) as u16,
231 }
232 }
233
234 #[inline(always)]
238 pub const fn union(&mut self, other: Self) {
239 self.x0 = const_min(self.x0, other.x0);
240 self.y0 = const_min(self.y0, other.y0);
241 self.x1 = const_max(self.x1, other.x1);
242 self.y1 = const_max(self.y1, other.y1);
243 }
244
245 pub fn as_rect(self) -> Rect {
247 Rect::new(
248 self.x0 as f64,
249 self.y0 as f64,
250 self.x1 as f64,
251 self.y1 as f64,
252 )
253 }
254}
255
256impl From<RectU16> for SizeU16 {
257 fn from(rect: RectU16) -> Self {
258 Self::from_wh(rect.width(), rect.height())
259 }
260}
261
262#[repr(C)]
266#[derive(Copy, Clone, Debug, Pod, Zeroable, PartialEq, Eq)]
267pub struct OffsetU32(pub [u32; 2]);
268
269impl OffsetU32 {
270 pub const ZERO: Self = Self::new(0);
272
273 pub const fn new(offset: u32) -> Self {
275 Self([offset; 2])
276 }
277
278 pub const fn from_xy(x: u32, y: u32) -> Self {
280 Self([x, y])
281 }
282
283 pub const fn x(self) -> u32 {
285 self.0[0]
286 }
287
288 pub const fn y(self) -> u32 {
290 self.0[1]
291 }
292}
293
294impl From<[u32; 2]> for OffsetU32 {
295 fn from(value: [u32; 2]) -> Self {
296 Self(value)
297 }
298}
299
300#[repr(C)]
302#[derive(Copy, Clone, Debug, Pod, Zeroable, PartialEq, Eq)]
303pub struct SizeU32(pub [u32; 2]);
304
305impl SizeU32 {
306 pub const ZERO: Self = Self::new(0);
308
309 pub const fn new(size: u32) -> Self {
311 Self([size; 2])
312 }
313
314 pub const fn from_wh(width: u32, height: u32) -> Self {
316 Self([width, height])
317 }
318
319 pub const fn width(self) -> u32 {
321 self.0[0]
322 }
323
324 pub const fn height(self) -> u32 {
326 self.0[1]
327 }
328
329 pub fn max(self, other: Self) -> Self {
331 Self::from_wh(
332 self.width().max(other.width()),
333 self.height().max(other.height()),
334 )
335 }
336
337 pub fn min(self, other: Self) -> Self {
339 Self::from_wh(
340 self.width().min(other.width()),
341 self.height().min(other.height()),
342 )
343 }
344
345 pub fn clamp(self, min: u32, max: u32) -> Self {
347 Self::from_wh(self.width().clamp(min, max), self.height().clamp(min, max))
348 }
349}
350
351impl From<[u32; 2]> for SizeU32 {
352 fn from(value: [u32; 2]) -> Self {
353 Self(value)
354 }
355}
356
357impl From<(u32, u32)> for SizeU32 {
358 fn from((width, height): (u32, u32)) -> Self {
359 Self::from_wh(width, height)
360 }
361}
362
363impl From<SizeU32> for (u32, u32) {
364 fn from(size: SizeU32) -> Self {
365 (size.width(), size.height())
366 }
367}
368
369impl From<SizeU16> for SizeU32 {
370 fn from(size: SizeU16) -> Self {
371 Self::from_wh(u32::from(size.width()), u32::from(size.height()))
372 }
373}
374
375impl TryFrom<SizeU32> for SizeU16 {
376 type Error = TryFromIntError;
377
378 fn try_from(size: SizeU32) -> Result<Self, Self::Error> {
379 Ok(Self::from_wh(
380 u16::try_from(size.width())?,
381 u16::try_from(size.height())?,
382 ))
383 }
384}
385
386impl Add for SizeU32 {
387 type Output = Self;
388
389 fn add(self, rhs: Self) -> Self::Output {
390 Self::from_wh(self.width() + rhs.width(), self.height() + rhs.height())
391 }
392}
393
394impl Add<u32> for SizeU32 {
395 type Output = Self;
396
397 fn add(self, rhs: u32) -> Self::Output {
398 self + Self::new(rhs)
399 }
400}
401
402#[repr(C)]
404#[derive(Copy, Clone, Debug, Pod, Zeroable, PartialEq, Eq)]
405pub struct RectU32 {
406 pub x0: u32,
408 pub y0: u32,
410 pub x1: u32,
412 pub y1: u32,
414}
415
416impl RectU32 {
417 pub const fn new(x0: u32, y0: u32, x1: u32, y1: u32) -> Self {
419 Self { x0, y0, x1, y1 }
420 }
421
422 pub const fn width(self) -> u32 {
424 self.x1.saturating_sub(self.x0)
425 }
426
427 pub const fn height(self) -> u32 {
429 self.y1.saturating_sub(self.y0)
430 }
431}
432
433#[inline(always)]
434const fn const_max(a: u16, b: u16) -> u16 {
435 if a > b { a } else { b }
436}
437
438#[inline(always)]
439const fn const_min(a: u16, b: u16) -> u16 {
440 if a < b { a } else { b }
441}
442
443#[cfg(test)]
444mod tests {
445 use super::RectU16;
446
447 #[test]
448 fn rect_u16_relative_to_origin() {
449 let rect = RectU16::new(10, 20, 30, 40);
450
451 assert_eq!(rect.relative_to_origin((5, 12)), RectU16::new(5, 8, 25, 28));
452 }
453
454 #[test]
455 fn rect_u16_relative_to_origin_clamps_to_zero() {
456 let rect = RectU16::new(10, 20, 30, 40);
457
458 assert_eq!(rect.relative_to_origin((20, 35)), RectU16::new(0, 0, 10, 5));
459 }
460
461 #[test]
462 fn disjoint_intersection_is_empty_but_not_inverted() {
463 let intersection = RectU16::new(0, 0, 4, 4).intersect(RectU16::new(8, 1, 12, 3));
464
465 assert_eq!(intersection, RectU16::new(8, 1, 8, 3));
466 assert!(intersection.is_empty());
467 assert!(intersection.x0 <= intersection.x1);
468 assert!(intersection.y0 <= intersection.y1);
469 }
470}