Skip to main content

zintl_render_math/
geometry.rs

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    /// A device pixels width. (non-logical, in physical pixels)
492    pub device_width: PhysicalPixels,
493    /// A device pixels height. (non-logical, in physical pixels)
494    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                // SAFETY: Division by non-zero value
731                x += (bounds.width() - size.width)
732                    .checked_div_value(2.0)
733                    .unwrap();
734                // SAFETY: Division by non-zero value
735                y += (bounds.height() - size.height)
736                    .checked_div_value(2.0)
737                    .unwrap();
738            }
739            Alignment::CenterLeft => {
740                // SAFETY: Division by non-zero value
741                y += (bounds.height() - size.height)
742                    .checked_div_value(2.0)
743                    .unwrap();
744            }
745            Alignment::CenterRight => {
746                // SAFETY: Division by non-zero value
747                x += bounds.width() - size.width;
748                // SAFETY: Division by non-zero value
749                y += (bounds.height() - size.height)
750                    .checked_div_value(2.0)
751                    .unwrap();
752            }
753            Alignment::CenterTop => {
754                // SAFETY: Division by non-zero value
755                x += (bounds.width() - size.width)
756                    .checked_div_value(2.0)
757                    .unwrap();
758            }
759            Alignment::CenterBottom => {
760                // SAFETY: Division by non-zero value
761                x += (bounds.width() - size.width)
762                    .checked_div_value(2.0)
763                    .unwrap();
764                // SAFETY: Division by non-zero value
765                y += bounds.height() - size.height;
766            }
767        }
768
769        LogicalPixelsRect::with_size(LogicalPixelsPoint::new(x, y), size)
770    }
771}
772
773/// A Normalized Texture Point.
774/// This is a point in the range [0.0, 1.0] for both x and y coordinates.
775/// Do not use this to logically scale pixels.
776#[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    /// Safety: If the x and y values are not in the range [0.0, 1.0], this returns `None`.
785    #[inline]
786    pub fn new(x: f32, y: f32) -> Option<Self> {
787        // TODO: Validate that x and y are in the range [0.0, 1.0].
788        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        // Convert the point to PhysicalPixelsFPoint to avoid rounding issues.
797        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/// A Normalized Texture Rect.
819#[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}