1use std::ops::{AddAssign, Neg, SubAssign};
8
9use crate::{ComputedData, dp::Dp};
10
11#[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
13pub struct Px(pub i32);
14
15impl Px {
16 pub const ZERO: Self = Self(0);
18
19 pub const MAX: Self = Self(i32::MAX);
21
22 pub fn raw(self) -> i32 {
24 self.0
25 }
26
27 pub const fn new(value: i32) -> Self {
29 Px(value)
30 }
31
32 pub fn from_dp(dp: Dp) -> Self {
34 Px(dp.to_pixels_f64() as i32)
35 }
36
37 pub fn to_dp(self) -> Dp {
39 Dp::from_pixels_f64(self.0 as f64)
40 }
41
42 pub fn abs(self) -> u32 {
44 self.0.unsigned_abs()
45 }
46
47 pub fn positive(self) -> u32 {
49 if self.0 < 0 { 0 } else { self.0 as u32 }
50 }
51
52 pub fn negative(self) -> i32 {
54 if self.0 > 0 { 0 } else { self.0 }
55 }
56
57 pub fn to_f32(self) -> f32 {
59 self.0 as f32
60 }
61
62 pub fn from_f32(value: f32) -> Self {
64 Px(value as i32)
65 }
66
67 pub fn saturating_from_f32(value: f32) -> Self {
69 Self::from_f32(value)
70 }
71
72 pub fn saturating_add(self, rhs: Self) -> Self {
79 Px(self.0.saturating_add(rhs.0))
80 }
81
82 pub fn saturating_sub(self, rhs: Self) -> Self {
84 Px(self.0.saturating_sub(rhs.0))
85 }
86
87 pub fn mul_f32(self, rhs: f32) -> Self {
89 Px((self.0 as f32 * rhs) as i32)
90 }
91
92 pub fn div_f32(self, rhs: f32) -> Self {
94 Px::from_f32(self.to_f32() / rhs)
95 }
96}
97
98#[derive(Debug, Clone, Copy, PartialEq, Default)]
100pub struct PxPosition {
101 pub x: f32,
103 pub y: f32,
105}
106
107impl PxPosition {
108 pub const ZERO: Self = Self { x: 0.0, y: 0.0 };
110
111 pub const fn new(x: Px, y: Px) -> Self {
113 Self {
114 x: x.0 as f32,
115 y: y.0 as f32,
116 }
117 }
118
119 pub const fn from_f32_xy(x: f32, y: f32) -> Self {
121 Self { x, y }
122 }
123
124 pub fn x_px(self) -> Px {
126 Px(self.x.round() as i32)
127 }
128
129 pub fn y_px(self) -> Px {
131 Px(self.y.round() as i32)
132 }
133
134 pub fn offset(self, dx: Px, dy: Px) -> Self {
136 Self {
137 x: self.x + dx.0 as f32,
138 y: self.y + dy.0 as f32,
139 }
140 }
141
142 pub fn offset_f32(self, dx: f32, dy: f32) -> Self {
144 Self {
145 x: self.x + dx,
146 y: self.y + dy,
147 }
148 }
149
150 pub fn saturating_offset(self, dx: Px, dy: Px) -> Self {
152 const MIN: f32 = i32::MIN as f32;
153 const MAX: f32 = i32::MAX as f32;
154 Self {
155 x: (self.x + dx.0 as f32).clamp(MIN, MAX),
156 y: (self.y + dy.0 as f32).clamp(MIN, MAX),
157 }
158 }
159
160 pub fn distance_to(self, other: Self) -> f32 {
162 let dx = self.x - other.x;
163 let dy = self.y - other.y;
164 (dx * dx + dy * dy).sqrt()
165 }
166
167 pub fn to_f32_arr2(self) -> [f32; 2] {
169 [self.x, self.y]
170 }
171
172 pub fn to_f32_arr3(self) -> [f32; 3] {
174 [self.x, self.y, 0.0]
175 }
176
177 pub fn from_f32_arr2(arr: [f32; 2]) -> Self {
179 Self {
180 x: arr[0],
181 y: arr[1],
182 }
183 }
184
185 pub fn from_f32_arr3(arr: [f32; 3]) -> Self {
187 Self {
188 x: arr[0],
189 y: arr[1],
190 }
191 }
192
193 pub fn to_f64_arr2(self) -> [f64; 2] {
195 [self.x as f64, self.y as f64]
196 }
197
198 pub fn to_f64_arr3(self) -> [f64; 3] {
200 [self.x as f64, self.y as f64, 0.0]
201 }
202
203 pub fn from_f64_arr2(arr: [f64; 2]) -> Self {
205 Self {
206 x: arr[0] as f32,
207 y: arr[1] as f32,
208 }
209 }
210
211 pub fn from_f64_arr3(arr: [f64; 3]) -> Self {
213 Self {
214 x: arr[0] as f32,
215 y: arr[1] as f32,
216 }
217 }
218}
219
220#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
222pub struct PxSize {
223 pub width: Px,
225 pub height: Px,
227}
228
229impl PxSize {
230 pub const ZERO: Self = Self {
232 width: Px(0),
233 height: Px(0),
234 };
235
236 pub const fn new(width: Px, height: Px) -> Self {
238 Self { width, height }
239 }
240
241 pub fn to_f32_arr2(self) -> [f32; 2] {
243 [self.width.0 as f32, self.height.0 as f32]
244 }
245}
246
247#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
249pub struct PxRect {
250 pub x: Px,
252 pub y: Px,
254 pub width: Px,
256 pub height: Px,
258}
259
260impl PxRect {
261 pub const ZERO: Self = Self {
263 x: Px::ZERO,
264 y: Px::ZERO,
265 width: Px::ZERO,
266 height: Px::ZERO,
267 };
268
269 pub const fn new(x: Px, y: Px, width: Px, height: Px) -> Self {
271 Self {
272 x,
273 y,
274 width,
275 height,
276 }
277 }
278
279 pub fn from_position_size(position: PxPosition, size: PxSize) -> Self {
281 Self {
282 x: position.x_px(),
283 y: position.y_px(),
284 width: size.width,
285 height: size.height,
286 }
287 }
288
289 pub(crate) fn enclosing(position: PxPosition, size: PxSize) -> Self {
290 Self::enclosing_f32(position, size.to_f32_arr2())
291 }
292
293 pub(crate) fn enclosing_f32(position: PxPosition, size: [f32; 2]) -> Self {
294 let x = position.x.floor() as i32;
295 let y = position.y.floor() as i32;
296 Self::new(
297 Px(x),
298 Px(y),
299 Px(((position.x + size[0]).ceil() as i32).saturating_sub(x)),
300 Px(((position.y + size[1]).ceil() as i32).saturating_sub(y)),
301 )
302 }
303
304 pub fn is_orthogonal(&self, other: &Self) -> bool {
306 let x_overlap = self.x.0 < other.x.0 + other.width.0 && other.x.0 < self.x.0 + self.width.0;
307
308 let y_overlap =
309 self.y.0 < other.y.0 + other.height.0 && other.y.0 < self.y.0 + self.height.0;
310
311 !x_overlap || !y_overlap
312 }
313
314 pub fn union(&self, other: &Self) -> Self {
330 let x = self.x.0.min(other.x.0);
331 let y = self.y.0.min(other.y.0);
332 let width = (self.x.0 + self.width.0).max(other.x.0 + other.width.0) - x;
333 let height = (self.y.0 + self.height.0).max(other.y.0 + other.height.0) - y;
334
335 Self {
336 x: Px(x),
337 y: Px(y),
338 width: Px(width),
339 height: Px(height),
340 }
341 }
342
343 pub fn area(&self) -> u32 {
345 let width = self.width.0.max(0) as u32;
346 let height = self.height.0.max(0) as u32;
347 width * height
348 }
349
350 pub fn intersection(&self, other: &Self) -> Option<Self> {
371 let x1 = self.x.0.max(other.x.0);
372 let y1 = self.y.0.max(other.y.0);
373 let x2 = (self.x.0 + self.width.0).min(other.x.0 + other.width.0);
374 let y2 = (self.y.0 + self.height.0).min(other.y.0 + other.height.0);
375
376 if x1 < x2 && y1 < y2 {
377 Some(Self {
378 x: Px(x1),
379 y: Px(y1),
380 width: Px(x2 - x1),
381 height: Px(y2 - y1),
382 })
383 } else {
384 None
385 }
386 }
387
388 pub fn contains(&self, point: PxPosition) -> bool {
390 point.x >= self.x.0 as f32
391 && point.x < (self.x.0 + self.width.0) as f32
392 && point.y >= self.y.0 as f32
393 && point.y < (self.y.0 + self.height.0) as f32
394 }
395}
396
397impl From<[Px; 2]> for PxSize {
398 fn from(size: [Px; 2]) -> Self {
399 Self {
400 width: size[0],
401 height: size[1],
402 }
403 }
404}
405
406impl From<PxSize> for winit::dpi::PhysicalSize<i32> {
407 fn from(size: PxSize) -> Self {
408 winit::dpi::PhysicalSize {
409 width: size.width.raw(),
410 height: size.height.raw(),
411 }
412 }
413}
414
415impl From<winit::dpi::PhysicalSize<u32>> for PxSize {
416 fn from(size: winit::dpi::PhysicalSize<u32>) -> Self {
417 Self {
418 width: Px(size.width as i32),
419 height: Px(size.height as i32),
420 }
421 }
422}
423
424impl From<ComputedData> for PxSize {
425 fn from(data: ComputedData) -> Self {
426 Self {
427 width: data.width,
428 height: data.height,
429 }
430 }
431}
432
433impl From<PxSize> for winit::dpi::Size {
434 fn from(size: PxSize) -> Self {
435 winit::dpi::PhysicalSize::from(size).into()
436 }
437}
438
439impl std::ops::Add for Px {
440 type Output = Self;
441
442 fn add(self, rhs: Self) -> Self::Output {
443 Px(self.0 + rhs.0)
444 }
445}
446
447impl Neg for Px {
448 type Output = Self;
449
450 fn neg(self) -> Self::Output {
451 Px::new(-self.0)
452 }
453}
454
455impl std::ops::Sub for Px {
456 type Output = Self;
457
458 fn sub(self, rhs: Self) -> Self::Output {
459 Px(self.0 - rhs.0)
460 }
461}
462
463impl std::ops::Mul for Px {
464 type Output = Self;
465
466 fn mul(self, rhs: Self) -> Self::Output {
467 Px(self.0 * rhs.0)
468 }
469}
470
471impl std::ops::Div for Px {
472 type Output = Self;
473
474 fn div(self, rhs: Self) -> Self::Output {
475 Px(self.0 / rhs.0)
476 }
477}
478
479impl std::ops::Mul<i32> for Px {
480 type Output = Self;
481
482 fn mul(self, rhs: i32) -> Self::Output {
483 Px(self.0 * rhs)
484 }
485}
486
487impl std::ops::Div<i32> for Px {
488 type Output = Self;
489
490 fn div(self, rhs: i32) -> Self::Output {
491 Px(self.0 / rhs)
492 }
493}
494
495impl From<i32> for Px {
496 fn from(value: i32) -> Self {
497 Px(value)
498 }
499}
500
501impl From<u32> for Px {
502 fn from(value: u32) -> Self {
503 Px(value as i32)
504 }
505}
506
507impl From<Dp> for Px {
508 fn from(dp: Dp) -> Self {
509 Px::from_dp(dp)
510 }
511}
512
513impl From<PxPosition> for winit::dpi::PhysicalPosition<i32> {
514 fn from(pos: PxPosition) -> Self {
515 winit::dpi::PhysicalPosition {
516 x: pos.x_px().0,
517 y: pos.y_px().0,
518 }
519 }
520}
521
522impl From<PxPosition> for winit::dpi::Position {
523 fn from(pos: PxPosition) -> Self {
524 winit::dpi::PhysicalPosition::from(pos).into()
525 }
526}
527
528impl AddAssign for Px {
529 fn add_assign(&mut self, rhs: Self) {
530 self.0 += rhs.0;
531 }
532}
533
534impl SubAssign for Px {
535 fn sub_assign(&mut self, rhs: Self) {
536 self.0 -= rhs.0;
537 }
538}
539
540impl std::ops::Add for PxPosition {
541 type Output = Self;
542
543 fn add(self, rhs: Self) -> Self::Output {
544 PxPosition {
545 x: self.x + rhs.x,
546 y: self.y + rhs.y,
547 }
548 }
549}
550
551impl std::ops::Sub for PxPosition {
552 type Output = Self;
553
554 fn sub(self, rhs: Self) -> Self::Output {
555 PxPosition {
556 x: self.x - rhs.x,
557 y: self.y - rhs.y,
558 }
559 }
560}
561
562impl From<[i32; 2]> for PxPosition {
563 fn from(pos: [i32; 2]) -> Self {
564 PxPosition::from_f32_xy(pos[0] as f32, pos[1] as f32)
565 }
566}
567
568impl From<PxPosition> for [i32; 2] {
569 fn from(pos: PxPosition) -> Self {
570 [pos.x_px().0, pos.y_px().0]
571 }
572}
573
574impl From<[u32; 2]> for PxPosition {
575 fn from(pos: [u32; 2]) -> Self {
576 PxPosition::from_f32_xy(pos[0] as f32, pos[1] as f32)
577 }
578}
579
580impl From<PxPosition> for [u32; 2] {
581 fn from(pos: PxPosition) -> Self {
582 [pos.x_px().positive(), pos.y_px().positive()]
583 }
584}
585
586impl From<[Px; 2]> for PxPosition {
587 fn from(pos: [Px; 2]) -> Self {
588 PxPosition::from_f32_xy(pos[0].0 as f32, pos[1].0 as f32)
589 }
590}
591
592impl From<PxPosition> for [Px; 2] {
593 fn from(pos: PxPosition) -> Self {
594 [pos.x_px(), pos.y_px()]
595 }
596}
597
598#[cfg(test)]
599mod tests {
600 use super::*;
601
602 #[test]
603 fn test_saturating_from_f32() {
604 assert_eq!(Px::saturating_from_f32(f32::MAX), Px(i32::MAX));
605 assert_eq!(Px::saturating_from_f32(f32::MIN), Px(i32::MIN));
606 assert_eq!(Px::saturating_from_f32(100.5), Px(100));
607 assert_eq!(Px::saturating_from_f32(-100.5), Px(-100));
608 }
609}