kas_core/
geom.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License in the LICENSE-APACHE file or at:
4//     https://www.apache.org/licenses/LICENSE-2.0
5
6//! Geometry data types
7//!
8//! [`Coord`], [`Size`] and [`Offset`] are all 2D integer (`i32`) types,
9//! representing positions, sizes and scroll deltas respectively.
10//!
11//! [`Vec2`] is a 2D floating-point (`f32`) type used mainly for screen-space
12//! position during rendering.
13//!
14//! Conversions types mostly use [`Cast`] and [`Conv`]. [`From`] may be used to
15//! simply pack/unpack components. To convert from floating-point types to
16//! integer types, use [`CastApprox`] or [`CastFloat`] to specify the rounding
17//! mode.
18
19use crate::cast::*;
20use crate::dir::Directional;
21use std::cmp::{Ordering, PartialOrd};
22
23mod transform;
24mod vector;
25pub use transform::{Affine, Linear};
26pub use vector::{DVec2, Quad, Vec2, Vec3};
27
28macro_rules! impl_common {
29    ($T:ty) => {
30        impl $T {
31            /// The constant `(0, 0)`
32            pub const ZERO: Self = Self(0, 0);
33
34            /// The minimum value
35            pub const MIN: Self = Self(i32::MIN, i32::MIN);
36
37            /// The maximum value
38            pub const MAX: Self = Self(i32::MAX, i32::MAX);
39
40            /// Return the minimum, componentwise
41            #[inline]
42            #[must_use = "method does not modify self but returns a new value"]
43            pub fn min(self, other: Self) -> Self {
44                Self(self.0.min(other.0), self.1.min(other.1))
45            }
46
47            /// Return the maximum, componentwise
48            #[inline]
49            #[must_use = "method does not modify self but returns a new value"]
50            pub fn max(self, other: Self) -> Self {
51                Self(self.0.max(other.0), self.1.max(other.1))
52            }
53
54            /// Restrict a value to the specified interval, componentwise
55            #[inline]
56            #[must_use = "method does not modify self but returns a new value"]
57            pub fn clamp(self, min: Self, max: Self) -> Self {
58                debug_assert!(min <= max);
59                self.min(max).max(min)
60            }
61
62            /// Return the transpose (swap x and y values)
63            #[inline]
64            #[must_use = "method does not modify self but returns a new value"]
65            pub fn transpose(self) -> Self {
66                Self(self.1, self.0)
67            }
68
69            /// Return the result of component-wise multiplication
70            #[inline]
71            #[must_use = "method does not modify self but returns a new value"]
72            pub fn cwise_mul(self, rhs: Self) -> Self {
73                Self(self.0 * rhs.0, self.1 * rhs.1)
74            }
75
76            /// Return the result of component-wise division
77            #[inline]
78            #[must_use = "method does not modify self but returns a new value"]
79            pub fn cwise_div(self, rhs: Self) -> Self {
80                Self(self.0 / rhs.0, self.1 / rhs.1)
81            }
82
83            /// Return the L1 (rectilinear / taxicab) distance
84            #[inline]
85            pub fn distance_l1(self) -> i32 {
86                self.0.abs() + self.1.abs()
87            }
88
89            /// Return the L-inf (max) distance
90            #[inline]
91            pub fn distance_l_inf(self) -> i32 {
92                self.0.abs().max(self.1.abs())
93            }
94
95            /// Extract one component, based on a direction
96            ///
97            /// This merely extracts the horizontal or vertical component.
98            /// It never negates it, even if the axis is reversed.
99            #[inline]
100            pub fn extract<D: Directional>(self, dir: D) -> i32 {
101                match dir.is_vertical() {
102                    false => self.0,
103                    true => self.1,
104                }
105            }
106
107            /// Set one component of self, based on a direction
108            ///
109            /// This does not negate components when the direction is reversed.
110            #[inline]
111            pub fn set_component<D: Directional>(&mut self, dir: D, value: i32) {
112                match dir.is_vertical() {
113                    false => self.0 = value,
114                    true => self.1 = value,
115                }
116            }
117        }
118
119        impl PartialOrd for $T {
120            fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
121                if self == rhs {
122                    Some(Ordering::Equal)
123                } else if self.0 < rhs.0 && self.1 < rhs.1 {
124                    Some(Ordering::Less)
125                } else if self.0 > rhs.0 && self.1 > rhs.1 {
126                    Some(Ordering::Greater)
127                } else {
128                    None
129                }
130            }
131
132            #[inline]
133            fn lt(&self, rhs: &Self) -> bool {
134                self.0 < rhs.0 && self.1 < rhs.1
135            }
136
137            #[inline]
138            fn le(&self, rhs: &Self) -> bool {
139                self.0 <= rhs.0 && self.1 <= rhs.1
140            }
141
142            #[inline]
143            fn ge(&self, rhs: &Self) -> bool {
144                self.0 >= rhs.0 && self.1 >= rhs.1
145            }
146
147            #[inline]
148            fn gt(&self, rhs: &Self) -> bool {
149                self.0 > rhs.0 && self.1 > rhs.1
150            }
151        }
152
153        impl From<(i32, i32)> for $T {
154            #[inline]
155            fn from(v: (i32, i32)) -> Self {
156                Self(v.0, v.1)
157            }
158        }
159        impl Conv<(i32, i32)> for $T {
160            #[inline]
161            fn conv(v: (i32, i32)) -> Self {
162                Self(v.0, v.1)
163            }
164            #[inline]
165            fn try_conv(v: (i32, i32)) -> Result<Self> {
166                Ok(Self::conv(v))
167            }
168        }
169    };
170}
171
172/// A 2D coordinate, also known as a point
173///
174/// A coordinate (or point) is an absolute position. One cannot add a point to
175/// a point. The difference between two points is an [`Offset`].
176///
177/// `Coord` implements [`PartialOrd`] such that the comparison must be true of
178/// all components: for example `a < b == a.0 < b.0 && a.1 < b.1`.
179/// If `c == Coord(0, 1)` and `d == Coord(1, 0)` then
180/// `c != d && !(c < d) && !(c > d)`. `Coord` does not implement [`Ord`].
181#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
182#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
183pub struct Coord(pub i32, pub i32);
184
185impl_common!(Coord);
186
187impl Coord {
188    /// Construct
189    #[inline]
190    pub fn new(x: i32, y: i32) -> Self {
191        Self(x, y)
192    }
193
194    /// Construct, using the same value on all axes
195    #[inline]
196    pub const fn splat(n: i32) -> Self {
197        Self(n, n)
198    }
199}
200
201impl std::ops::Sub for Coord {
202    type Output = Offset;
203
204    #[inline]
205    fn sub(self, other: Self) -> Offset {
206        Offset(self.0 - other.0, self.1 - other.1)
207    }
208}
209
210impl std::ops::Add<Offset> for Coord {
211    type Output = Self;
212
213    #[inline]
214    fn add(self, other: Offset) -> Self {
215        Coord(self.0 + other.0, self.1 + other.1)
216    }
217}
218impl std::ops::AddAssign<Offset> for Coord {
219    #[inline]
220    fn add_assign(&mut self, rhs: Offset) {
221        self.0 += rhs.0;
222        self.1 += rhs.1;
223    }
224}
225impl std::ops::Sub<Offset> for Coord {
226    type Output = Self;
227
228    #[inline]
229    fn sub(self, other: Offset) -> Self {
230        Coord(self.0 - other.0, self.1 - other.1)
231    }
232}
233impl std::ops::SubAssign<Offset> for Coord {
234    #[inline]
235    fn sub_assign(&mut self, rhs: Offset) {
236        self.0 -= rhs.0;
237        self.1 -= rhs.1;
238    }
239}
240
241impl std::ops::Add<Size> for Coord {
242    type Output = Self;
243
244    #[inline]
245    fn add(self, other: Size) -> Self {
246        Coord(self.0 + other.0, self.1 + other.1)
247    }
248}
249impl std::ops::AddAssign<Size> for Coord {
250    #[inline]
251    fn add_assign(&mut self, rhs: Size) {
252        self.0 += rhs.0;
253        self.1 += rhs.1;
254    }
255}
256impl std::ops::Sub<Size> for Coord {
257    type Output = Self;
258
259    #[inline]
260    fn sub(self, other: Size) -> Self {
261        Coord(self.0 - other.0, self.1 - other.1)
262    }
263}
264impl std::ops::SubAssign<Size> for Coord {
265    #[inline]
266    fn sub_assign(&mut self, rhs: Size) {
267        self.0 -= rhs.0;
268        self.1 -= rhs.1;
269    }
270}
271
272impl Conv<Coord> for kas_text::Vec2 {
273    #[inline]
274    fn try_conv(pos: Coord) -> Result<Self> {
275        Ok(Vec2::try_conv(pos)?.into())
276    }
277}
278
279/// A 2D size, also known as an extent
280///
281/// This is both a size and a relative position. One can add or subtract a size
282/// from a [`Coord`]. One can multiply a size by a scalar.
283///
284/// A `Size` is expected to be non-negative; some methods such as [`Size::new`]
285/// and implementations of subtraction will check this, but only in debug mode
286/// (similar to overflow checks on integers).
287///
288/// Subtraction is defined to be saturating subtraction.
289///
290/// `Size` implements [`PartialOrd`] such that the comparison must be true of
291/// all components: for example `a < b == a.0 < b.0 && a.1 < b.1`.
292/// If `c == Size(0, 1)` and `d == Size(1, 0)` then
293/// `c != d && !(c < d) && !(c > d)`. `Size` does not implement [`Ord`].
294///
295/// This may be converted to [`Offset`] with `from` / `into`.
296#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
297#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
298pub struct Size(pub i32, pub i32);
299
300impl_common!(Size);
301
302impl Size {
303    /// Construct
304    ///
305    /// In debug mode, this asserts that components are non-negative.
306    #[inline]
307    pub fn new(w: i32, h: i32) -> Self {
308        debug_assert!(w >= 0 && h >= 0, "Size::new({w}, {h}): negative value");
309        Self(w, h)
310    }
311
312    /// Construct, using the same value on all axes
313    ///
314    /// In debug mode, this asserts that components are non-negative.
315    #[inline]
316    pub fn splat(n: i32) -> Self {
317        debug_assert!(n >= 0, "Size::splat({n}): negative value");
318        Self(n, n)
319    }
320
321    /// Scale to fit within the target size, keeping aspect ratio
322    ///
323    /// If either dimension of self is 0, this returns None.
324    pub fn aspect_scale_to(self, target: Size) -> Option<Size> {
325        if self.0 == 0 || self.1 == 0 {
326            return None;
327        }
328
329        let h = i32::conv((i64::conv(self.1) * i64::conv(target.0)) / i64::conv(self.0));
330        if h <= target.1 {
331            Some(Size(target.0, h))
332        } else {
333            let w = i32::conv((i64::conv(self.0) * i64::conv(target.1)) / i64::conv(self.1));
334            Some(Size(w, target.1))
335        }
336    }
337}
338
339impl std::ops::Add for Size {
340    type Output = Self;
341
342    #[inline]
343    fn add(self, other: Self) -> Self {
344        Size(self.0 + other.0, self.1 + other.1)
345    }
346}
347impl std::ops::AddAssign for Size {
348    #[inline]
349    fn add_assign(&mut self, rhs: Self) {
350        self.0 += rhs.0;
351        self.1 += rhs.1;
352    }
353}
354
355/// Subtract a `Size` from a `Size`
356///
357/// This is saturating subtraction: `Size::ZERO - Size::splat(6) == Size::ZERO`.
358impl std::ops::Sub for Size {
359    type Output = Self;
360
361    #[inline]
362    fn sub(self, rhs: Self) -> Self {
363        // This impl should aid vectorisation.
364        Size(self.0 - rhs.0, self.1 - rhs.1).max(Size::ZERO)
365    }
366}
367/// Subtract a `Size` from a `Size`
368///
369/// This is saturating subtraction.
370impl std::ops::SubAssign for Size {
371    #[inline]
372    fn sub_assign(&mut self, rhs: Self) {
373        *self = *self - rhs;
374    }
375}
376
377/// Multiply a `Size` by an integer
378///
379/// In debug mode this asserts that the result is non-negative.
380impl std::ops::Mul<i32> for Size {
381    type Output = Self;
382
383    #[inline]
384    fn mul(self, x: i32) -> Self {
385        debug_assert!(x >= 0);
386        Size(self.0 * x, self.1 * x)
387    }
388}
389/// Divide a `Size` by an integer
390///
391/// In debug mode this asserts that the result is non-negative.
392impl std::ops::Div<i32> for Size {
393    type Output = Self;
394
395    #[inline]
396    fn div(self, x: i32) -> Self {
397        debug_assert!(x >= 0);
398        Size(self.0 / x, self.1 / x)
399    }
400}
401
402/// Convert an [`Offset`] into a [`Coord`]
403///
404/// In debug mode this asserts that the result is non-negative.
405impl Conv<Offset> for Coord {
406    #[inline]
407    fn try_conv(v: Offset) -> Result<Self> {
408        debug_assert!(v.0 >= 0 && v.1 >= 0, "Coord::conv({v:?}): negative value");
409        Ok(Self(v.0, v.1))
410    }
411}
412
413/// Convert an [`Offset`] into a [`Size`]
414///
415/// In debug mode this asserts that the result is non-negative.
416impl Conv<Offset> for Size {
417    #[inline]
418    fn try_conv(v: Offset) -> Result<Self> {
419        debug_assert!(v.0 >= 0 && v.1 >= 0, "Size::conv({v:?}): negative value");
420        Ok(Self(v.0, v.1))
421    }
422}
423
424// used for marigns
425impl Conv<Size> for (u16, u16) {
426    #[inline]
427    fn try_conv(size: Size) -> Result<Self> {
428        Ok((size.0.try_cast()?, size.1.try_cast()?))
429    }
430}
431impl Conv<(u16, u16)> for Size {
432    #[inline]
433    fn try_conv(v: (u16, u16)) -> Result<Self> {
434        Ok(Self(i32::try_conv(v.0)?, i32::try_conv(v.1)?))
435    }
436}
437
438impl Conv<(u32, u32)> for Size {
439    #[inline]
440    fn try_conv(v: (u32, u32)) -> Result<Self> {
441        Ok(Self(i32::try_conv(v.0)?, i32::try_conv(v.1)?))
442    }
443}
444
445impl Conv<Size> for (u32, u32) {
446    #[inline]
447    fn try_conv(size: Size) -> Result<Self> {
448        Ok((u32::try_conv(size.0)?, u32::try_conv(size.1)?))
449    }
450}
451
452impl Conv<Size> for kas_text::Vec2 {
453    #[inline]
454    fn try_conv(size: Size) -> Result<Self> {
455        Ok(Vec2::try_conv(size)?.into())
456    }
457}
458
459/// A `(x, y)` offset, also known as a **vector**
460///
461/// This is a relative position. It can be added to or subtracted from a
462/// [`Coord`], and it can be added to or subtracted from itself. It can be
463/// negative. It can be multiplied by a scalar.
464///
465/// `Offset` implements [`PartialOrd`] such that the comparison must be true of
466/// all components: for example `a < b == a.0 < b.0 && a.1 < b.1`.
467/// If `c == Offset(0, 1)` and `d == Offset(1, 0)` then
468/// `c != d && !(c < d) && !(c > d)`. `Offset` does not implement [`Ord`].
469///
470/// This may be converted to [`Size`] with `from` / `into`.
471#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
472#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
473pub struct Offset(pub i32, pub i32);
474
475impl_common!(Offset);
476
477impl Offset {
478    /// Construct
479    #[inline]
480    pub fn new(x: i32, y: i32) -> Self {
481        Self(x, y)
482    }
483
484    /// Construct, using the same value on all axes
485    #[inline]
486    pub const fn splat(n: i32) -> Self {
487        Self(n, n)
488    }
489}
490
491impl std::ops::Neg for Offset {
492    type Output = Self;
493
494    #[inline]
495    fn neg(self) -> Self {
496        Offset(-self.0, -self.1)
497    }
498}
499
500impl std::ops::Add for Offset {
501    type Output = Self;
502
503    #[inline]
504    fn add(self, other: Self) -> Self {
505        Offset(self.0 + other.0, self.1 + other.1)
506    }
507}
508impl std::ops::AddAssign for Offset {
509    #[inline]
510    fn add_assign(&mut self, rhs: Self) {
511        self.0 += rhs.0;
512        self.1 += rhs.1;
513    }
514}
515
516impl std::ops::Sub for Offset {
517    type Output = Self;
518
519    #[inline]
520    fn sub(self, other: Self) -> Self {
521        Offset(self.0 - other.0, self.1 - other.1)
522    }
523}
524impl std::ops::SubAssign for Offset {
525    #[inline]
526    fn sub_assign(&mut self, rhs: Self) {
527        self.0 -= rhs.0;
528        self.1 -= rhs.1;
529    }
530}
531
532impl std::ops::Mul<i32> for Offset {
533    type Output = Self;
534
535    #[inline]
536    fn mul(self, x: i32) -> Self {
537        Offset(self.0 * x, self.1 * x)
538    }
539}
540impl std::ops::Div<i32> for Offset {
541    type Output = Self;
542
543    #[inline]
544    fn div(self, x: i32) -> Self {
545        Offset(self.0 / x, self.1 / x)
546    }
547}
548
549impl Conv<Coord> for Offset {
550    #[inline]
551    fn try_conv(v: Coord) -> Result<Self> {
552        Ok(Self(v.0, v.1))
553    }
554}
555
556impl Conv<Size> for Offset {
557    #[inline]
558    fn try_conv(v: Size) -> Result<Self> {
559        Ok(Self(v.0, v.1))
560    }
561}
562
563impl Conv<Offset> for kas_text::Vec2 {
564    #[inline]
565    fn try_conv(v: Offset) -> Result<Self> {
566        Ok(Vec2::try_conv(v)?.into())
567    }
568}
569
570/// An axis-aligned rectangular region
571///
572/// The region is defined by a point `pos` and an extent `size`, allowing easy
573/// translations. It is empty unless `size` is positive on both axes.
574#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, Hash)]
575#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
576pub struct Rect {
577    pub pos: Coord,
578    pub size: Size,
579}
580
581impl Rect {
582    /// The empty rect (all fields zero)
583    pub const ZERO: Self = Self::new(Coord::ZERO, Size::ZERO);
584
585    /// Construct from a [`Coord`] and [`Size`]
586    #[inline]
587    pub const fn new(pos: Coord, size: Size) -> Self {
588        Rect { pos, size }
589    }
590
591    /// Construct from two coords
592    ///
593    /// It is expected that `pos <= pos2`.
594    #[inline]
595    pub fn from_coords(pos: Coord, pos2: Coord) -> Self {
596        let size = (pos2 - pos).cast();
597        Rect { pos, size }
598    }
599
600    /// Get the second point (pos + size)
601    #[inline]
602    pub fn pos2(&self) -> Coord {
603        self.pos + self.size
604    }
605
606    /// Check whether `p` is contained within this rect
607    #[inline]
608    pub fn contains<P: PartialOrd<Coord>>(&self, p: P) -> bool {
609        p >= self.pos && p < self.pos2()
610    }
611
612    /// Calculate the intersection of two rects
613    #[inline]
614    pub fn intersection(&self, rhs: &Rect) -> Option<Rect> {
615        let (l1, l2) = (self.pos, self.pos2());
616        let (r1, r2) = (rhs.pos, rhs.pos2());
617        let pos = l1.max(r1);
618        let pos2 = l2.min(r2);
619        if pos <= pos2 {
620            Some(Rect::new(pos, (pos2 - pos).cast()))
621        } else {
622            None
623        }
624    }
625
626    /// Shrink self in all directions by the given `n`
627    #[inline]
628    #[must_use = "method does not modify self but returns a new value"]
629    pub fn shrink(&self, n: i32) -> Rect {
630        let pos = self.pos + Offset::splat(n);
631        let size = self.size - Size::splat(n + n);
632        Rect { pos, size }
633    }
634
635    /// Expand self in all directions by the given `n`
636    ///
637    /// In debug mode this asserts that `n` is non-negative.
638    #[inline]
639    #[must_use = "method does not modify self but returns a new value"]
640    pub fn expand(&self, n: i32) -> Rect {
641        debug_assert!(n >= 0);
642        let pos = self.pos - Offset::splat(n);
643        let size = self.size + Size::splat(n + n);
644        Rect { pos, size }
645    }
646}
647
648impl std::ops::Add<Offset> for Rect {
649    type Output = Self;
650
651    #[inline]
652    fn add(self, offset: Offset) -> Self {
653        Rect::new(self.pos + offset, self.size)
654    }
655}
656impl std::ops::AddAssign<Offset> for Rect {
657    #[inline]
658    fn add_assign(&mut self, offset: Offset) {
659        self.pos += offset;
660    }
661}
662
663impl std::ops::Sub<Offset> for Rect {
664    type Output = Self;
665
666    #[inline]
667    fn sub(self, offset: Offset) -> Self {
668        Rect::new(self.pos - offset, self.size)
669    }
670}
671impl std::ops::SubAssign<Offset> for Rect {
672    #[inline]
673    fn sub_assign(&mut self, offset: Offset) {
674        self.pos -= offset;
675    }
676}
677
678#[cfg(feature = "accesskit")]
679mod accesskit_impls {
680    use super::{Coord, Offset, Rect};
681    use crate::cast::{Cast, CastApprox, Conv, ConvApprox, Result};
682
683    impl ConvApprox<accesskit::Point> for Coord {
684        fn try_conv_approx(p: accesskit::Point) -> Result<Self> {
685            Ok(Coord(p.x.try_cast_approx()?, p.y.try_cast_approx()?))
686        }
687    }
688
689    impl Conv<Rect> for accesskit::Rect {
690        fn try_conv(rect: Rect) -> Result<Self> {
691            let p = rect.pos;
692            let p2 = rect.pos2();
693            Ok(accesskit::Rect {
694                x0: p.0.try_cast()?,
695                y0: p.1.try_cast()?,
696                x1: p2.0.try_cast()?,
697                y1: p2.1.try_cast()?,
698            })
699        }
700    }
701
702    impl ConvApprox<accesskit::Rect> for Rect {
703        fn try_conv_approx(rect: accesskit::Rect) -> Result<Self> {
704            let pos = Coord(rect.x0.try_cast_approx()?, rect.y0.try_cast_approx()?);
705            let p2 = Coord(rect.x1.try_cast_approx()?, rect.y1.try_cast_approx()?);
706            let size = (p2 - pos).cast();
707            Ok(Rect { pos, size })
708        }
709    }
710
711    impl ConvApprox<accesskit::Point> for Offset {
712        fn try_conv_approx(point: accesskit::Point) -> Result<Self> {
713            Ok(Offset(
714                point.x.try_cast_approx()?,
715                point.y.try_cast_approx()?,
716            ))
717        }
718    }
719}
720
721mod winit_impls {
722    use super::{Coord, Size};
723    use crate::cast::{Cast, CastApprox, Conv, ConvApprox, Result};
724    use winit::dpi::{PhysicalPosition, PhysicalSize};
725
726    impl<X: CastApprox<i32>> ConvApprox<PhysicalPosition<X>> for Coord {
727        #[inline]
728        fn try_conv_approx(pos: PhysicalPosition<X>) -> Result<Self> {
729            Ok(Coord(pos.x.try_cast_approx()?, pos.y.try_cast_approx()?))
730        }
731    }
732
733    impl<X: Cast<i32>> Conv<PhysicalSize<X>> for Size {
734        #[inline]
735        fn try_conv(size: PhysicalSize<X>) -> Result<Self> {
736            Ok(Size(size.width.cast(), size.height.cast()))
737        }
738    }
739
740    impl Coord {
741        /// Convert to a "physical" [`winit::dpi::Position`]
742        ///
743        /// This implies that the [`Coord`] was calculated using the correct
744        /// scale factor. Before the window has been constructed (when the
745        /// scale factor is supposedly unknown) this should not be used.
746        #[inline]
747        pub fn as_physical(self) -> winit::dpi::Position {
748            winit::dpi::Position::Physical(PhysicalPosition::new(self.0, self.1))
749        }
750    }
751
752    impl Size {
753        /// Convert to a "physical" [`winit::dpi::Size`]
754        ///
755        /// This implies that the [`Size`] was calculated using the correct
756        /// scale factor. Before the window has been constructed (when the
757        /// scale factor is supposedly unknown) this should not be used.
758        #[inline]
759        pub fn as_physical(self) -> winit::dpi::Size {
760            let (w, h): (u32, u32) = self.cast();
761            winit::dpi::Size::Physical(PhysicalSize::new(w, h))
762        }
763    }
764}