1use paste::paste;
2
3macro_rules! define_unit {
4 ($unit_name:ident, $type_name:ident, $zero_value:expr, $one_value:expr) => {
5 #[derive(Debug, Default, Clone, Copy, PartialEq, PartialOrd)]
6 pub struct $unit_name($type_name);
7
8 impl $unit_name {
9 const ZERO: $unit_name = $unit_name($zero_value);
10
11 pub fn new(value: $type_name) -> Self {
12 $unit_name(value)
13 }
14
15 #[inline]
16 pub fn zero() -> Self {
17 $unit_name($zero_value)
18 }
19
20 #[inline]
21 pub fn is_zero(self) -> bool {
22 self.0 == Self::ZERO.0
23 }
24
25 #[inline]
26 pub fn value(self) -> $type_name {
27 self.0
28 }
29
30 #[inline]
31 pub fn checked_div(self, other: Self) -> Option<Self> {
32 if other == Self::ZERO {
33 None
34 } else {
35 Some($unit_name(self.0 / other.0))
36 }
37 }
38
39 #[inline]
40 pub fn checked_div_value(self, other: $type_name) -> Option<Self> {
41 if other == $zero_value {
42 None
43 } else {
44 Some($unit_name(self.0 / other))
45 }
46 }
47
48 pub fn checked_rem(self, other: Self) -> Option<Self> {
49 if other == Self::ZERO {
50 None
51 } else {
52 Some($unit_name(self.0 % other.0))
53 }
54 }
55
56 pub fn checked_rem_value(self, other: $type_name) -> Option<Self> {
57 if other == $zero_value {
58 None
59 } else {
60 Some($unit_name(self.0 % other))
61 }
62 }
63
64 pub fn max(self, other: Self) -> Self {
65 $unit_name(self.0.max(other.0))
66 }
67
68 pub fn min(self, other: Self) -> Self {
69 $unit_name(self.0.min(other.0))
70 }
71 }
72
73 impl From<$type_name> for $unit_name {
74 #[inline]
75 fn from(value: $type_name) -> Self {
76 $unit_name(value)
77 }
78 }
79
80 impl From<$unit_name> for $type_name {
81 #[inline]
82 fn from(value: $unit_name) -> Self {
83 value.0
84 }
85 }
86
87 impl std::ops::Add for $unit_name {
88 type Output = Self;
89
90 #[inline]
91 fn add(self, other: Self) -> Self::Output {
92 $unit_name(self.0 + other.0)
93 }
94 }
95
96 impl std::ops::AddAssign for $unit_name {
97 #[inline]
98 fn add_assign(&mut self, other: Self) {
99 self.0 += other.0;
100 }
101 }
102
103 impl std::ops::Sub for $unit_name {
104 type Output = Self;
105
106 #[inline]
107 fn sub(self, other: Self) -> Self::Output {
108 $unit_name(self.0 - other.0)
109 }
110 }
111
112 impl std::ops::SubAssign for $unit_name {
113 #[inline]
114 fn sub_assign(&mut self, other: Self) {
115 self.0 -= other.0;
116 }
117 }
118
119 impl std::ops::Mul for $unit_name {
120 type Output = Self;
121
122 #[inline]
123 fn mul(self, other: Self) -> Self::Output {
124 $unit_name(self.0 * other.0)
125 }
126 }
127
128 impl std::ops::MulAssign for $unit_name {
129 #[inline]
130 fn mul_assign(&mut self, other: Self) {
131 self.0 *= other.0;
132 }
133 }
134
135 impl std::ops::Add<$type_name> for $unit_name {
136 type Output = Self;
137
138 #[inline]
139 fn add(self, other: $type_name) -> Self::Output {
140 $unit_name(self.0 + other)
141 }
142 }
143
144 impl std::ops::AddAssign<$type_name> for $unit_name {
145 #[inline]
146 fn add_assign(&mut self, other: $type_name) {
147 self.0 += other;
148 }
149 }
150
151 impl std::ops::Sub<$type_name> for $unit_name {
152 type Output = Self;
153
154 #[inline]
155 fn sub(self, other: $type_name) -> Self::Output {
156 $unit_name(self.0 - other)
157 }
158 }
159
160 impl std::ops::SubAssign<$type_name> for $unit_name {
161 #[inline]
162 fn sub_assign(&mut self, other: $type_name) {
163 self.0 -= other;
164 }
165 }
166
167 impl std::ops::Mul<$type_name> for $unit_name {
168 type Output = Self;
169
170 #[inline]
171 fn mul(self, other: $type_name) -> Self::Output {
172 $unit_name(self.0 * other)
173 }
174 }
175
176 impl std::ops::MulAssign<$type_name> for $unit_name {
177 #[inline]
178 fn mul_assign(&mut self, other: $type_name) {
179 self.0 *= other;
180 }
181 }
182
183 impl std::ops::Add<$unit_name> for $type_name {
184 type Output = $unit_name;
185
186 #[inline]
187 fn add(self, other: $unit_name) -> Self::Output {
188 $unit_name(self + other.0)
189 }
190 }
191
192 impl std::ops::AddAssign<$unit_name> for $type_name {
193 #[inline]
194 fn add_assign(&mut self, other: $unit_name) {
195 *self += other.0;
196 }
197 }
198
199 impl std::ops::Sub<$unit_name> for $type_name {
200 type Output = $unit_name;
201
202 #[inline]
203 fn sub(self, other: $unit_name) -> Self::Output {
204 $unit_name(self - other.0)
205 }
206 }
207
208 impl std::ops::SubAssign<$unit_name> for $type_name {
209 #[inline]
210 fn sub_assign(&mut self, other: $unit_name) {
211 *self -= other.0;
212 }
213 }
214
215 impl std::ops::Mul<$unit_name> for $type_name {
216 type Output = $unit_name;
217
218 #[inline]
219 fn mul(self, other: $unit_name) -> Self::Output {
220 $unit_name(self * other.0)
221 }
222 }
223
224 impl std::ops::MulAssign<$unit_name> for $type_name {
225 #[inline]
226 fn mul_assign(&mut self, other: $unit_name) {
227 *self *= other.0;
228 }
229 }
230
231 paste! {
232 #[derive(Debug, Clone, Copy, Default, PartialEq)]
233 pub struct [<$unit_name Point>] {
234 pub x: $unit_name,
235 pub y: $unit_name,
236 }
237
238 impl [<$unit_name Point>] {
239 #[inline]
240 pub fn new(x: $unit_name, y: $unit_name) -> Self {
241 [<$unit_name Point>] { x, y }
242 }
243
244 #[inline]
245 pub fn zero() -> Self {
246 [<$unit_name Point>] {
247 x: $unit_name::zero(),
248 y: $unit_name::zero(),
249 }
250 }
251
252 #[inline]
253 pub fn distance(self, other: Self) -> f32 {
254 let dx = (self.x.0 as f32 - other.x.0 as f32).powi(2);
255 let dy = (self.y.0 as f32 - other.y.0 as f32).powi(2);
256 (dx + dy).sqrt()
257 }
258
259 #[inline]
260 fn x_value(&self) -> $type_name {
261 self.x.0
262 }
263
264 #[inline]
265 fn y_value(&self) -> $type_name {
266 self.y.0
267 }
268
269 #[inline]
270 fn checked_div(self, other: Self) -> Option<Self> {
271 if other.x == $unit_name::ZERO || other.y == $unit_name::ZERO {
272 None
273 } else {
274 Some([<$unit_name Point>] {
275 x: self.x.checked_div(other.x)?,
276 y: self.y.checked_div(other.y)?,
277 })
278 }
279 }
280 }
281
282 impl std::ops::Add for [<$unit_name Point>] {
283 type Output = Self;
284
285 #[inline]
286 fn add(self, other: Self) -> Self::Output {
287 [<$unit_name Point>] {
288 x: self.x + other.x,
289 y: self.y + other.y,
290 }
291 }
292 }
293
294 impl std::ops::Sub for [<$unit_name Point>] {
295 type Output = Self;
296
297 #[inline]
298 fn sub(self, other: Self) -> Self::Output {
299 [<$unit_name Point>] {
300 x: self.x - other.x,
301 y: self.y - other.y,
302 }
303 }
304 }
305
306 impl std::ops::Mul for [<$unit_name Point>] {
307 type Output = Self;
308
309 #[inline]
310 fn mul(self, other: Self) -> Self::Output {
311 [<$unit_name Point>] {
312 x: self.x * other.x,
313 y: self.y * other.y,
314 }
315 }
316 }
317
318
319 impl From<[$unit_name; 2]> for [<$unit_name Point>] {
320 #[inline]
321 fn from(arr: [$unit_name; 2]) -> Self {
322 [<$unit_name Point>] {
323 x: arr[0],
324 y: arr[1],
325 }
326 }
327 }
328
329 impl From<[<$unit_name Point>]> for [$unit_name; 2] {
330 #[inline]
331 fn from(point: [<$unit_name Point>]) -> Self {
332 [point.x, point.y]
333 }
334 }
335
336 impl From<($unit_name, $unit_name)> for [<$unit_name Point>] {
337 #[inline]
338 fn from(tuple: ($unit_name, $unit_name)) -> Self {
339 [<$unit_name Point>] {
340 x: tuple.0,
341 y: tuple.1,
342 }
343 }
344 }
345
346 impl From<[<$unit_name Point>]> for ($unit_name, $unit_name) {
347 #[inline]
348 fn from(point: [<$unit_name Point>]) -> Self {
349 (point.x, point.y)
350 }
351 }
352
353 impl From<[$type_name; 2]> for [<$unit_name Point>] {
354 #[inline]
355 fn from(arr: [$type_name; 2]) -> Self {
356 [<$unit_name Point>] {
357 x: $unit_name(arr[0]),
358 y: $unit_name(arr[1]),
359 }
360 }
361 }
362
363 impl From<[<$unit_name Point>]> for [$type_name; 2] {
364 #[inline]
365 fn from(point: [<$unit_name Point>]) -> Self {
366 [point.x.0, point.y.0]
367 }
368 }
369
370 impl From<($type_name, $type_name)> for [<$unit_name Point>] {
371 #[inline]
372 fn from(tuple: ($type_name, $type_name)) -> Self {
373 [<$unit_name Point>] {
374 x: $unit_name(tuple.0),
375 y: $unit_name(tuple.1),
376 }
377 }
378 }
379
380 impl From<[<$unit_name Point>]> for ($type_name, $type_name) {
381 #[inline]
382 fn from(point: [<$unit_name Point>]) -> Self {
383 (point.x.0, point.y.0)
384 }
385 }
386
387 #[derive(Debug, Clone, Copy, Default, PartialEq)]
388 pub struct [<$unit_name Rect>] {
389 pub min: [<$unit_name Point>],
390 pub max: [<$unit_name Point>],
391 }
392
393 impl [<$unit_name Rect>] {
394 const TWO: $type_name = $one_value + $one_value;
395
396 #[inline]
397 pub fn new(min: [<$unit_name Point>], max: [<$unit_name Point>]) -> Self {
398 [<$unit_name Rect>] { min, max }
399 }
400
401 #[inline]
402 pub fn with_size(
403 min: [<$unit_name Point>],
404 size: [<$unit_name Size>],
405 ) -> Self {
406 [<$unit_name Rect>] {
407 min,
408 max: [<$unit_name Point>] {
409 x: min.x + size.width,
410 y: min.y + size.height,
411 },
412 }
413 }
414
415 #[inline]
416 pub fn zero() -> Self {
417 Self {
418 min: [<$unit_name Point>]::zero(),
419 max: [<$unit_name Point>]::zero(),
420 }
421 }
422
423 #[inline]
424 pub fn width(&self) -> $unit_name {
425 self.max.x - self.min.x
426 }
427
428 #[inline]
429 pub fn height(&self) -> $unit_name {
430 self.max.y - self.min.y
431 }
432
433 #[inline]
434 pub fn checked_div(self, other: Self) -> Option<Self> {
435 if other.min.x_value() == $zero_value || other.min.y_value() == $zero_value ||
436 other.max.x_value() == $zero_value || other.max.y_value() == $zero_value {
437 None
438 } else {
439 Some([<$unit_name Rect>] {
440 min: self.min.checked_div(other.min)?,
441 max: self.max.checked_div(other.max)?,
442 })
443 }
444 }
445
446 #[inline]
447 pub fn center(&self) -> [<$unit_name Point>] {
448 [<$unit_name Point>] {
449 x: $unit_name((self.min.x_value() + self.max.x_value()) / Self::TWO),
450 y: $unit_name((self.min.y_value() + self.max.y_value()) / Self::TWO),
451 }
452 }
453 }
454
455 #[derive(Debug, Clone, Copy, Default, PartialEq)]
456 pub struct [<$unit_name Size>] {
457 pub width: $unit_name,
458 pub height: $unit_name,
459 }
460
461 impl [<$unit_name Size>] {
462 #[inline]
463 pub fn new(width: $unit_name, height: $unit_name) -> Self {
464 [<$unit_name Size>] { width, height }
465 }
466
467 #[inline]
468 pub fn zero() -> Self {
469 [<$unit_name Size>] {
470 width: $unit_name::zero(),
471 height: $unit_name::zero(),
472 }
473 }
474
475 #[inline]
476 pub fn is_zero(&self) -> bool {
477 self.width.is_zero() && self.height.is_zero()
478 }
479
480 #[inline]
481 pub fn area(&self) -> $type_name {
482 self.width.0 * self.height.0
483 }
484 }
485 }
486 };
487}
488
489#[derive(Debug, Clone, Copy, PartialEq)]
490pub struct Viewport {
491 pub device_width: PhysicalPixels,
493 pub device_height: PhysicalPixels,
495 pub scale_factor: ScaleFactor,
496 pub rect: PhysicalPixelsRect,
497}
498
499impl Viewport {
500 pub fn new(
501 device_width: PhysicalPixels,
502 device_height: PhysicalPixels,
503 scale_factor: ScaleFactor,
504 ) -> Self {
505 let rect = PhysicalPixelsRect::with_size(
506 PhysicalPixelsPoint::new(0.into(), 0.into()),
507 PhysicalPixelsSize::new(device_width, device_height),
508 );
509 Viewport {
510 device_width,
511 device_height,
512 scale_factor,
513 rect,
514 }
515 }
516}
517
518#[derive(Debug, Clone, Copy, PartialEq)]
519pub struct ScaleFactor {
520 dpi: f32,
521 dpr: f32,
522}
523
524impl ScaleFactor {
525 pub fn new(dpi: f32, dpr: f32) -> Self {
526 assert!(dpi > 0.0, "DPI must be greater than 0");
527 assert!(dpr > 0.0, "DPR must be greater than 0");
528 ScaleFactor { dpi, dpr }
529 }
530
531 #[inline]
532 pub fn dpi(&self) -> f32 {
533 self.dpi
534 }
535
536 #[inline]
537 pub fn dpr(&self) -> f32 {
538 self.dpr
539 }
540}
541
542define_unit!(PhysicalPixels, u32, 0, 1);
543define_unit!(PhysicalPixelsF, f32, 0.0, 1.0);
544define_unit!(LogicalPixels, f32, 0.0, 1.0);
545
546impl From<PhysicalPixels> for PhysicalPixelsF {
547 #[inline]
548 fn from(value: PhysicalPixels) -> Self {
549 PhysicalPixelsF::new(value.0 as f32)
550 }
551}
552
553impl From<PhysicalPixelsF> for PhysicalPixels {
554 #[inline]
555 fn from(value: PhysicalPixelsF) -> Self {
556 PhysicalPixels::new(value.0.round() as u32)
557 }
558}
559
560impl From<PhysicalPixelsPoint> for PhysicalPixelsFPoint {
561 #[inline]
562 fn from(point: PhysicalPixelsPoint) -> Self {
563 PhysicalPixelsFPoint {
564 x: point.x.into(),
565 y: point.y.into(),
566 }
567 }
568}
569
570impl From<PhysicalPixelsFPoint> for PhysicalPixelsPoint {
571 #[inline]
572 fn from(point: PhysicalPixelsFPoint) -> Self {
573 PhysicalPixelsPoint {
574 x: point.x.into(),
575 y: point.y.into(),
576 }
577 }
578}
579
580impl From<PhysicalPixelsSize> for PhysicalPixelsFSize {
581 #[inline]
582 fn from(size: PhysicalPixelsSize) -> Self {
583 PhysicalPixelsFSize {
584 width: size.width.into(),
585 height: size.height.into(),
586 }
587 }
588}
589
590impl From<PhysicalPixelsFSize> for PhysicalPixelsSize {
591 #[inline]
592 fn from(size: PhysicalPixelsFSize) -> Self {
593 PhysicalPixelsSize {
594 width: size.width.into(),
595 height: size.height.into(),
596 }
597 }
598}
599
600pub trait InPhysicalScale<PhysicalUnit> {
601 fn in_physical_scale(&self, scale_factor: &ScaleFactor) -> PhysicalUnit;
602}
603
604pub trait InLogicalScale<LogicalUnit> {
605 fn in_logical_scale(&self, scale_factor: &ScaleFactor) -> LogicalUnit;
606}
607
608macro_rules! impl_in_physical_scale {
609 ($from:ty, $to:ty, $base_type:ty) => {
610 impl InPhysicalScale<$to> for $from {
611 #[inline]
612 fn in_physical_scale(&self, scale_factor: &ScaleFactor) -> $to {
613 <$to>::new((self.0 * scale_factor.dpr()).round() as $base_type)
614 }
615 }
616 paste! {
617 impl InPhysicalScale<[<$to Point>]> for [<$from Point>] {
618 #[inline]
619 fn in_physical_scale(&self, scale_factor: &ScaleFactor) -> [<$to Point>] {
620 [<$to Point>] {
621 x: self.x.in_physical_scale(scale_factor),
622 y: self.y.in_physical_scale(scale_factor),
623 }
624 }
625 }
626
627 impl InPhysicalScale<[<$to Rect>]> for [<$from Rect>] {
628 #[inline]
629 fn in_physical_scale(&self, scale_factor: &ScaleFactor) -> [<$to Rect>] {
630 [<$to Rect>] {
631 min: self.min.in_physical_scale(scale_factor),
632 max: self.max.in_physical_scale(scale_factor),
633 }
634 }
635 }
636
637 impl InPhysicalScale<[<$to Size>]> for [<$from Size>] {
638 #[inline]
639 fn in_physical_scale(&self, scale_factor: &ScaleFactor) -> [<$to Size>] {
640 [<$to Size>] {
641 width: self.width.in_physical_scale(scale_factor),
642 height: self.height.in_physical_scale(scale_factor),
643 }
644 }
645 }
646 }
647 };
648}
649
650macro_rules! impl_in_logical_scale {
651 ($from:ty, $to:ty, $base_type:ty) => {
652 impl InLogicalScale<$to> for $from {
653 #[inline]
654 fn in_logical_scale(&self, scale_factor: &ScaleFactor) -> $to {
655 <$to>::new((self.0 as f32 / scale_factor.dpr()).round() as $base_type)
656 }
657 }
658
659 paste! {
660 impl InLogicalScale<[<$to Point>]> for [<$from Point>] {
661 #[inline]
662 fn in_logical_scale(&self, scale_factor: &ScaleFactor) -> [<$to Point>] {
663 [<$to Point>] {
664 x: self.x.in_logical_scale(scale_factor),
665 y: self.y.in_logical_scale(scale_factor),
666 }
667 }
668 }
669
670 impl InLogicalScale<[<$to Rect>]> for [<$from Rect>] {
671 #[inline]
672 fn in_logical_scale(&self, scale_factor: &ScaleFactor) -> [<$to Rect>] {
673 [<$to Rect>] {
674 min: self.min.in_logical_scale(scale_factor),
675 max: self.max.in_logical_scale(scale_factor),
676 }
677 }
678 }
679
680 impl InLogicalScale<[<$to Size>]> for [<$from Size>] {
681 #[inline]
682 fn in_logical_scale(&self, scale_factor: &ScaleFactor) -> [<$to Size>] {
683 [<$to Size>] {
684 width: self.width.in_logical_scale(scale_factor),
685 height: self.height.in_logical_scale(scale_factor),
686 }
687 }
688 }
689 }
690 };
691}
692
693impl_in_physical_scale!(LogicalPixels, PhysicalPixels, u32);
694impl_in_physical_scale!(LogicalPixels, PhysicalPixelsF, f32);
695impl_in_logical_scale!(PhysicalPixels, LogicalPixels, f32);
696impl_in_logical_scale!(PhysicalPixelsF, LogicalPixels, f32);
697
698#[derive(Clone, Copy, Debug, Default, PartialEq)]
699pub enum Alignment {
700 #[default]
701 TopLeft,
702 TopRight,
703 BottomLeft,
704 BottomRight,
705 Center,
706 CenterLeft,
707 CenterRight,
708 CenterTop,
709 CenterBottom,
710}
711
712impl Alignment {
713 pub fn align_size(
714 &self,
715 bounds: LogicalPixelsRect,
716 size: LogicalPixelsSize,
717 ) -> LogicalPixelsRect {
718 let mut x = bounds.min.x;
719 let mut y = bounds.min.y;
720
721 match self {
722 Alignment::TopLeft => {}
723 Alignment::TopRight => x += bounds.width() - size.width,
724 Alignment::BottomLeft => y += bounds.height() - size.height,
725 Alignment::BottomRight => {
726 x += bounds.width() - size.width;
727 y += bounds.height() - size.height;
728 }
729 Alignment::Center => {
730 x += (bounds.width() - size.width)
732 .checked_div_value(2.0)
733 .unwrap();
734 y += (bounds.height() - size.height)
736 .checked_div_value(2.0)
737 .unwrap();
738 }
739 Alignment::CenterLeft => {
740 y += (bounds.height() - size.height)
742 .checked_div_value(2.0)
743 .unwrap();
744 }
745 Alignment::CenterRight => {
746 x += bounds.width() - size.width;
748 y += (bounds.height() - size.height)
750 .checked_div_value(2.0)
751 .unwrap();
752 }
753 Alignment::CenterTop => {
754 x += (bounds.width() - size.width)
756 .checked_div_value(2.0)
757 .unwrap();
758 }
759 Alignment::CenterBottom => {
760 x += (bounds.width() - size.width)
762 .checked_div_value(2.0)
763 .unwrap();
764 y += bounds.height() - size.height;
766 }
767 }
768
769 LogicalPixelsRect::with_size(LogicalPixelsPoint::new(x, y), size)
770 }
771}
772
773#[repr(C)]
777#[derive(Clone, Copy, Debug, Default, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
778pub struct TexturePoint {
779 x: f32,
780 y: f32,
781}
782
783impl TexturePoint {
784 #[inline]
786 pub fn new(x: f32, y: f32) -> Option<Self> {
787 Some(TexturePoint { x, y })
789 }
790
791 #[inline]
792 pub fn from_physical_point(
793 point: PhysicalPixelsPoint,
794 texture_size: PhysicalPixelsSize,
795 ) -> Option<Self> {
796 let point: PhysicalPixelsFPoint = point.into();
798 let x = point
799 .x
800 .checked_div_value(texture_size.width.value() as f32)?;
801 let y = point
802 .y
803 .checked_div_value(texture_size.height.value() as f32)?;
804 TexturePoint::new(x.into(), y.into())
805 }
806
807 #[inline]
808 pub fn x(&self) -> f32 {
809 self.x
810 }
811
812 #[inline]
813 pub fn y(&self) -> f32 {
814 self.y
815 }
816}
817
818#[derive(Clone, Copy, Debug, Default, PartialEq)]
820pub struct TextureBounds {
821 pub min: TexturePoint,
822 pub max: TexturePoint,
823}
824
825impl TextureBounds {
826 pub fn new(min: TexturePoint, max: TexturePoint) -> Self {
827 TextureBounds { min, max }
828 }
829
830 pub fn from_phisical_rect(
831 rect: PhysicalPixelsRect,
832 texture_size: PhysicalPixelsSize,
833 ) -> Option<Self> {
834 let min = TexturePoint::from_physical_point(rect.min, texture_size)?;
835 let max = TexturePoint::from_physical_point(rect.max, texture_size)?;
836 Some(TextureBounds { min, max })
837 }
838}