playdate_rs/
math.rs

1use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
2// required for thumbv7em builds
3#[allow(unused_imports)]
4use num_traits::Float;
5use num_traits::{NumCast, Signed};
6
7#[macro_export]
8macro_rules! vec2 {
9    ($x:expr, $y:expr $(,)?) => {
10        $crate::math::Vec2::new($x, $y)
11    };
12    (x: $x:expr, y: $y:expr $(,)?) => {
13        $crate::math::Vec2::new($x, $y)
14    };
15    () => {
16        $crate::math::Vec2::default()
17    };
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
21pub struct Vec2<T> {
22    pub x: T,
23    pub y: T,
24}
25
26impl<T> Vec2<T> {
27    /// Create a new vector.
28    #[inline]
29    pub const fn new(x: T, y: T) -> Self {
30        Self { x, y }
31    }
32
33    /// Set all components of the vector to the same value.
34    #[inline]
35    pub fn splat(v: T) -> Self
36    where
37        T: Clone,
38    {
39        Self { x: v.clone(), y: v }
40    }
41
42    /// Vector dot product.
43    #[inline]
44    pub fn dot(self, other: Self) -> T
45    where
46        T: Add<Output = T> + Mul<Output = T>,
47    {
48        self.x * other.x + self.y * other.y
49    }
50
51    /// Vector length.
52    #[inline]
53    pub fn length(self) -> f32
54    where
55        T: Into<f32> + Clone,
56    {
57        let x = self.x.clone().into();
58        let y = self.y.clone().into();
59        (x * x + y * y).sqrt()
60    }
61
62    /// Type conversion.
63    #[inline]
64    pub fn cast<U: NumCast>(self) -> Vec2<U>
65    where
66        T: NumCast,
67    {
68        self.try_cast().unwrap()
69    }
70
71    /// Type conversion.
72    #[inline]
73    pub fn try_cast<U: NumCast>(self) -> Option<Vec2<U>>
74    where
75        T: NumCast,
76    {
77        match (NumCast::from(self.x), NumCast::from(self.y)) {
78            (Some(x), Some(y)) => Some(Vec2::new(x, y)),
79            _ => None,
80        }
81    }
82
83    /// Returns a vector with the absolute value of each component.
84    #[inline]
85    pub fn abs(self) -> Self
86    where
87        T: Signed,
88    {
89        Self::new(self.x.abs(), self.y.abs())
90    }
91
92    /// cross product.
93    #[inline]
94    pub fn cross(self, other: Self) -> T
95    where
96        T: Sub<Output = T> + Mul<Output = T>,
97    {
98        self.x * other.y - self.y * other.x
99    }
100
101    /// swap x and y
102    #[inline]
103    pub fn yx(self) -> Self {
104        Self::new(self.y, self.x)
105    }
106
107    /// Returns the square of the vector length.
108    #[inline]
109    pub fn square_length(self) -> T
110    where
111        T: Add<Output = T> + Mul<Output = T> + Clone,
112    {
113        self.x.clone() * self.x + self.y.clone() * self.y
114    }
115
116    /// Returns the distance between two vectors.
117    #[inline]
118    pub fn distance(self, other: Self) -> f32
119    where
120        T: Into<f32> + Clone,
121    {
122        let x = self.x.clone().into() - other.x.clone().into();
123        let y = self.y.clone().into() - other.y.clone().into();
124        (x * x + y * y).sqrt()
125    }
126}
127
128impl Vec2<f32> {
129    /// Round each component to the nearest integer.
130    #[inline]
131    pub fn round(self) -> Self {
132        Self::new(self.x.round(), self.y.round())
133    }
134
135    /// Round each component up to the nearest integer.
136    #[inline]
137    pub fn ceil(self) -> Self {
138        Self::new(self.x.ceil(), self.y.ceil())
139    }
140
141    /// Round each component down to the nearest integer.
142    #[inline]
143    pub fn floor(self) -> Self {
144        Self::new(self.x.floor(), self.y.floor())
145    }
146
147    /// Normalize the vector.
148    #[inline]
149    pub fn normalize(self) -> Self {
150        let length = self.length();
151        if length == 0.0 {
152            return Self::ZERO;
153        }
154        self / length
155    }
156
157    /// scale the vector to the given length.
158    #[inline]
159    pub fn with_length(self, length: f32) -> Self {
160        self.normalize() * length
161    }
162
163    /// Rotate the vector around a center point by the given angle in radians.
164    #[inline]
165    pub fn rotate_around(self, center: Self, radians: f32) -> Self {
166        let (sin, cos) = radians.sin_cos();
167        let x = self.x - center.x;
168        let y = self.y - center.y;
169        Self::new(x * cos - y * sin + center.x, x * sin + y * cos + center.y)
170    }
171
172    /// Rotate the vector by the given angle in radians.
173    #[inline]
174    pub fn rotate(self, radians: f32) -> Self {
175        let (sin, cos) = radians.sin_cos();
176        Self::new(self.x * cos - self.y * sin, self.x * sin + self.y * cos)
177    }
178
179    /// Scale the vector by another vector.
180    #[inline]
181    pub fn scale(self, other: Self) -> Self {
182        Self::new(self.x * other.x, self.y * other.y)
183    }
184
185    /// Translate the vector by another vector.
186    #[inline]
187    pub fn translate(self, other: Self) -> Self {
188        Self::new(self.x + other.x, self.y + other.y)
189    }
190
191    /// Project the vector onto another vector.
192    #[inline]
193    pub fn project_onto(self, other: Self) -> Self {
194        let other = other.normalize();
195        other * self.dot(other)
196    }
197
198    /// Reflect the vector around a normal.
199    #[inline]
200    pub fn reflect(self, normal: Self) -> Self {
201        self - normal * 2.0 * self.dot(normal)
202    }
203}
204
205impl<T> From<(T, T)> for Vec2<T> {
206    #[inline]
207    fn from(v: (T, T)) -> Self {
208        Self { x: v.0, y: v.1 }
209    }
210}
211
212impl<T> From<Vec2<T>> for (T, T) {
213    #[inline]
214    fn from(val: Vec2<T>) -> Self {
215        (val.x, val.y)
216    }
217}
218
219impl<T> From<Vec2<T>> for [T; 2] {
220    #[inline]
221    fn from(val: Vec2<T>) -> Self {
222        [val.x, val.y]
223    }
224}
225
226impl<T: Add<Output = T> + Clone> Add<T> for Vec2<T> {
227    type Output = Self;
228    #[inline]
229    fn add(self, rhs: T) -> Self::Output {
230        Self::new(self.x + rhs.clone(), self.y + rhs)
231    }
232}
233
234impl<T: Add<Output = T>> Add<Self> for Vec2<T> {
235    type Output = Self;
236    #[inline]
237    fn add(self, rhs: Self) -> Self::Output {
238        Self::new(self.x + rhs.x, self.y + rhs.y)
239    }
240}
241
242impl<T: AddAssign<T> + Clone> AddAssign<T> for Vec2<T> {
243    #[inline]
244    fn add_assign(&mut self, rhs: T) {
245        self.x += rhs.clone();
246        self.y += rhs;
247    }
248}
249
250impl<T: AddAssign<T>> AddAssign<Self> for Vec2<T> {
251    #[inline]
252    fn add_assign(&mut self, rhs: Self) {
253        self.x += rhs.x;
254        self.y += rhs.y;
255    }
256}
257
258impl<T: Sub<Output = T> + Clone> Sub<T> for Vec2<T> {
259    type Output = Self;
260    #[inline]
261    fn sub(self, rhs: T) -> Self::Output {
262        Self::new(self.x - rhs.clone(), self.y - rhs)
263    }
264}
265
266impl<T: Sub<Output = T>> Sub<Self> for Vec2<T> {
267    type Output = Self;
268    #[inline]
269    fn sub(self, rhs: Self) -> Self::Output {
270        Self::new(self.x - rhs.x, self.y - rhs.y)
271    }
272}
273
274impl<T: SubAssign<T> + Clone> SubAssign<T> for Vec2<T> {
275    #[inline]
276    fn sub_assign(&mut self, rhs: T) {
277        self.x -= rhs.clone();
278        self.y -= rhs;
279    }
280}
281
282impl<T: SubAssign<T>> SubAssign<Self> for Vec2<T> {
283    #[inline]
284    fn sub_assign(&mut self, rhs: Self) {
285        self.x -= rhs.x;
286        self.y -= rhs.y;
287    }
288}
289
290impl<T: Mul<Output = T> + Clone> Mul<T> for Vec2<T> {
291    type Output = Self;
292    #[inline]
293    fn mul(self, rhs: T) -> Self::Output {
294        Self::new(self.x * rhs.clone(), self.y * rhs)
295    }
296}
297
298impl<T: MulAssign<T>> MulAssign<Self> for Vec2<T> {
299    #[inline]
300    fn mul_assign(&mut self, rhs: Self) {
301        self.x *= rhs.x;
302        self.y *= rhs.y;
303    }
304}
305
306impl<T: Div<Output = T> + Clone> Div<T> for Vec2<T> {
307    type Output = Self;
308    #[inline]
309    fn div(self, rhs: T) -> Self::Output {
310        Self::new(self.x / rhs.clone(), self.y / rhs)
311    }
312}
313
314impl<T: DivAssign<T>> DivAssign<Self> for Vec2<T> {
315    #[inline]
316    fn div_assign(&mut self, rhs: Self) {
317        self.x /= rhs.x;
318        self.y /= rhs.y;
319    }
320}
321
322impl Vec2<i8> {
323    pub const ZERO: Self = Self { x: 0, y: 0 };
324}
325
326impl Vec2<u8> {
327    pub const ZERO: Self = Self { x: 0, y: 0 };
328}
329
330impl Vec2<i32> {
331    pub const ZERO: Self = Self { x: 0, y: 0 };
332}
333
334impl Vec2<u32> {
335    pub const ZERO: Self = Self { x: 0, y: 0 };
336}
337
338impl Vec2<isize> {
339    pub const ZERO: Self = Self { x: 0, y: 0 };
340}
341
342impl Vec2<usize> {
343    pub const ZERO: Self = Self { x: 0, y: 0 };
344}
345
346impl Vec2<f32> {
347    pub const ZERO: Self = Self { x: 0.0, y: 0.0 };
348}
349
350#[macro_export]
351macro_rules! size {
352    ($w:expr, $h:expr $(,)?) => {
353        $crate::math::Size::new($w, $h)
354    };
355    (w: $w:expr, h: $h:expr $(,)?) => {
356        $crate::math::Size::new($w, $h)
357    };
358    (width: $w:expr, height: $h:expr $(,)?) => {
359        $crate::math::Size::new($w, $h)
360    };
361    (square: $square: expr $(,)?) => {
362        $crate::math::Size::splat($square)
363    };
364    () => {
365        $crate::math::Size::default()
366    };
367}
368
369#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
370pub struct Size<T> {
371    pub width: T,
372    pub height: T,
373}
374
375impl<T> Size<T> {
376    /// Create a new size.
377    #[inline]
378    pub const fn new(width: T, height: T) -> Self {
379        Self { width, height }
380    }
381
382    /// Set all components of the size to the same value.
383    #[inline]
384    pub fn splat(v: T) -> Self
385    where
386        T: Clone,
387    {
388        Self {
389            width: v.clone(),
390            height: v,
391        }
392    }
393
394    /// Type conversion.
395    #[inline]
396    pub fn cast<U: NumCast>(self) -> Size<U>
397    where
398        T: NumCast,
399    {
400        self.try_cast().unwrap()
401    }
402
403    /// Type conversion.
404    #[inline]
405    pub fn try_cast<U: NumCast>(self) -> Option<Size<U>>
406    where
407        T: NumCast,
408    {
409        match (NumCast::from(self.width), NumCast::from(self.height)) {
410            (Some(w), Some(h)) => Some(Size::new(w, h)),
411            _ => None,
412        }
413    }
414
415    /// Returns the area of the size.
416    #[inline]
417    pub fn area(self) -> T
418    where
419        T: Mul<Output = T>,
420    {
421        self.width * self.height
422    }
423
424    /// Returns the aspect ratio of the size.
425    #[inline]
426    pub fn aspect_ratio(self) -> f32
427    where
428        T: Into<f32> + Clone,
429    {
430        let width = self.width.clone().into();
431        let height = self.height.clone().into();
432        width / height
433    }
434}
435
436impl Size<u8> {
437    pub const ZERO: Self = Self::new(0, 0);
438    pub const ONE: Self = Self::new(1, 1);
439    pub const TILE16: Self = Self::new(16, 16);
440    pub const TILE32: Self = Self::new(32, 32);
441    pub const TILE64: Self = Self::new(64, 64);
442}
443
444impl Size<i8> {
445    pub const ZERO: Self = Self::new(0, 0);
446    pub const ONE: Self = Self::new(1, 1);
447    pub const TILE16: Self = Self::new(16, 16);
448    pub const TILE32: Self = Self::new(32, 32);
449    pub const TILE64: Self = Self::new(64, 64);
450}
451
452impl Size<u16> {
453    pub const ZERO: Self = Self::new(0, 0);
454    pub const ONE: Self = Self::new(1, 1);
455    pub const TILE16: Self = Self::new(16, 16);
456    pub const TILE32: Self = Self::new(32, 32);
457    pub const TILE64: Self = Self::new(64, 64);
458}
459
460impl Size<i16> {
461    pub const ZERO: Self = Self::new(0, 0);
462    pub const ONE: Self = Self::new(1, 1);
463    pub const TILE16: Self = Self::new(16, 16);
464    pub const TILE32: Self = Self::new(32, 32);
465    pub const TILE64: Self = Self::new(64, 64);
466}
467
468impl Size<u32> {
469    pub const ZERO: Self = Self::new(0, 0);
470    pub const ONE: Self = Self::new(1, 1);
471    pub const TILE16: Self = Self::new(16, 16);
472    pub const TILE32: Self = Self::new(32, 32);
473    pub const TILE64: Self = Self::new(64, 64);
474}
475
476impl Size<i32> {
477    pub const ZERO: Self = Self::new(0, 0);
478    pub const ONE: Self = Self::new(1, 1);
479    pub const TILE16: Self = Self::new(16, 16);
480    pub const TILE32: Self = Self::new(32, 32);
481    pub const TILE64: Self = Self::new(64, 64);
482}
483
484impl Size<usize> {
485    pub const ZERO: Self = Self::new(0, 0);
486    pub const ONE: Self = Self::new(1, 1);
487    pub const TILE16: Self = Self::new(16, 16);
488    pub const TILE32: Self = Self::new(32, 32);
489    pub const TILE64: Self = Self::new(64, 64);
490}
491
492impl Size<isize> {
493    pub const ZERO: Self = Self::new(0, 0);
494    pub const ONE: Self = Self::new(1, 1);
495    pub const TILE16: Self = Self::new(16, 16);
496    pub const TILE32: Self = Self::new(32, 32);
497    pub const TILE64: Self = Self::new(64, 64);
498}
499
500impl Size<f32> {
501    pub const ZERO: Self = Self::new(0.0, 0.0);
502    pub const ONE: Self = Self::new(1.0, 1.0);
503    pub const TILE16: Self = Self::new(16.0, 16.0);
504    pub const TILE32: Self = Self::new(32.0, 32.0);
505    pub const TILE64: Self = Self::new(64.0, 64.0);
506
507    /// Round each component to the nearest integer.
508    #[inline]
509    pub fn round(self) -> Self {
510        Self::new(self.width.round(), self.height.round())
511    }
512
513    /// Round each component up to the nearest integer.
514    #[inline]
515    pub fn ceil(self) -> Self {
516        Self::new(self.width.ceil(), self.height.ceil())
517    }
518
519    /// Round each component down to the nearest integer.
520    #[inline]
521    pub fn floor(self) -> Self {
522        Self::new(self.width.floor(), self.height.floor())
523    }
524
525    /// Scale the size by another vector.
526    #[inline]
527    pub fn scale(self, scale: Vec2<f32>) -> Self {
528        Self::new(self.width * scale.x, self.height * scale.y)
529    }
530}
531
532#[macro_export]
533macro_rules! rect {
534    (x: $x:expr, y: $y:expr, w: $w:expr, h: $h:expr $(,)?) => {
535        $crate::math::Rect::new($x, $y, $w, $h)
536    };
537    (x: $x:expr, y: $y:expr, width: $w:expr, height: $h:expr $(,)?) => {
538        $crate::math::Rect::new($x, $y, $w, $h)
539    };
540    (pos: $p:expr, size: $s:expr $(,)?) => {
541        $crate::math::Rect::from_point_and_size($p, $s)
542    };
543    () => {
544        $crate::math::Rect::default()
545    };
546}
547
548#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
549pub struct Rect<T> {
550    pub x: T,
551    pub y: T,
552    pub width: T,
553    pub height: T,
554}
555
556impl<T> Rect<T> {
557    /// Create a new rectangle.
558    #[inline]
559    pub const fn new(x: T, y: T, width: T, height: T) -> Self {
560        Self {
561            x,
562            y,
563            width,
564            height,
565        }
566    }
567
568    /// Create a new rectangle from a position and a size.
569    #[inline]
570    pub fn from_pos_and_size(pos: Vec2<T>, size: Size<T>) -> Self {
571        Self {
572            x: pos.x,
573            y: pos.y,
574            width: size.width,
575            height: size.height,
576        }
577    }
578
579    /// Get the top-left corner of the rectangle.
580    #[inline]
581    pub fn pos(&self) -> Vec2<T>
582    where
583        T: Clone,
584    {
585        Vec2::new(self.x.clone(), self.y.clone())
586    }
587
588    /// Get the size of the rectangle.
589    #[inline]
590    pub fn size(&self) -> Size<T>
591    where
592        T: Clone,
593    {
594        Size::new(self.width.clone(), self.height.clone())
595    }
596
597    /// Type conversion.
598    #[inline]
599    pub fn cast<U: NumCast>(self) -> Rect<U>
600    where
601        T: NumCast,
602    {
603        self.try_cast().unwrap()
604    }
605
606    /// Type conversion.
607    #[inline]
608    pub fn try_cast<U: NumCast>(self) -> Option<Rect<U>>
609    where
610        T: NumCast,
611    {
612        match (
613            NumCast::from(self.x),
614            NumCast::from(self.y),
615            NumCast::from(self.width),
616            NumCast::from(self.height),
617        ) {
618            (Some(x), Some(y), Some(w), Some(h)) => Some(Rect::new(x, y, w, h)),
619            _ => None,
620        }
621    }
622
623    /// Returns the area of the rectangle.
624    #[inline]
625    pub fn area(&self) -> T
626    where
627        T: Mul<Output = T> + Clone,
628    {
629        self.width.clone() * self.height.clone()
630    }
631
632    /// Returns the aspect ratio of the rectangle.
633    #[inline]
634    pub fn aspect_ratio(&self) -> f32
635    where
636        T: Into<f32> + Clone,
637    {
638        let width = self.width.clone().into();
639        let height = self.height.clone().into();
640        width / height
641    }
642
643    /// Get the intersection of two rectangles.
644    #[inline]
645    pub fn intersection(&self, other: &Self) -> Option<Self>
646    where
647        T: PartialOrd + Add<T, Output = T> + Sub<T, Output = T> + Default + Clone + Copy,
648    {
649        let min = |a: T, b: T| if a < b { a } else { b };
650        let max = |a: T, b: T| if a > b { a } else { b };
651        let x = max(self.x, other.x);
652        let y = max(self.y, other.y);
653        let width = min(self.x + self.width, other.x + other.width) - x;
654        let height = min(self.y + self.height, other.y + other.height) - y;
655        if width <= T::default() || height <= T::default() {
656            None
657        } else {
658            Some(Self::new(x, y, width, height))
659        }
660    }
661}
662
663impl Rect<f32> {
664    /// Round each component to the nearest integer.
665    #[inline]
666    pub fn round(self) -> Self {
667        Self::new(
668            self.x.round(),
669            self.y.round(),
670            self.width.round(),
671            self.height.round(),
672        )
673    }
674
675    /// Round each component up to the nearest integer.
676    #[inline]
677    pub fn ceil(self) -> Self {
678        Self::new(
679            self.x.ceil(),
680            self.y.ceil(),
681            self.width.ceil(),
682            self.height.ceil(),
683        )
684    }
685
686    /// Round each component down to the nearest integer.
687    #[inline]
688    pub fn floor(self) -> Self {
689        Self::new(
690            self.x.floor(),
691            self.y.floor(),
692            self.width.floor(),
693            self.height.floor(),
694        )
695    }
696
697    /// Scale the rectangle size by another vector.
698    #[inline]
699    pub fn scale(self, scale: Vec2<f32>) -> Self {
700        Self::new(self.x, self.y, self.width * scale.x, self.height * scale.y)
701    }
702
703    /// Translate the rectangle by another vector.
704    #[inline]
705    pub fn translate(self, delta: Vec2<f32>) -> Self {
706        Self::new(self.x + delta.x, self.y + delta.y, self.width, self.height)
707    }
708
709    /// Returns the center of the rectangle.
710    #[inline]
711    pub fn center(&self) -> Vec2<f32> {
712        Vec2::new(self.x + self.width / 2.0, self.y + self.height / 2.0)
713    }
714
715    /// Check if the rectangle intersects another rectangle.
716    #[inline]
717    pub fn intersects(&self, other: &Self) -> bool {
718        self.x < other.x + other.width
719            && self.x + self.width > other.x
720            && self.y < other.y + other.height
721            && self.y + self.height > other.y
722    }
723
724    /// Check if the rectangle contains a point.
725    #[inline]
726    pub fn contains_point(&self, point: Vec2<f32>) -> bool {
727        point.x >= self.x
728            && point.x <= self.x + self.width
729            && point.y >= self.y
730            && point.y <= self.y + self.height
731    }
732
733    /// Check if the rectangle contains another rectangle.
734    #[inline]
735    pub fn contains_rect(&self, other: &Self) -> bool {
736        self.x <= other.x
737            && self.x + self.width >= other.x + other.width
738            && self.y <= other.y
739            && self.y + self.height >= other.y + other.height
740    }
741}
742
743impl From<Rect<f32>> for sys::PDRect {
744    #[inline]
745    fn from(val: Rect<f32>) -> Self {
746        sys::PDRect {
747            x: val.x,
748            y: val.y,
749            width: val.width,
750            height: val.height,
751        }
752    }
753}
754
755impl From<sys::PDRect> for Rect<f32> {
756    #[inline]
757    fn from(rect: sys::PDRect) -> Self {
758        Self {
759            x: rect.x,
760            y: rect.y,
761            width: rect.width,
762            height: rect.height,
763        }
764    }
765}
766
767impl From<sys::CollisionPoint> for Vec2<f32> {
768    #[inline]
769    fn from(v: sys::CollisionPoint) -> Self {
770        Self { x: v.x, y: v.y }
771    }
772}
773
774impl From<sys::CollisionVector> for Vec2<i32> {
775    #[inline]
776    fn from(v: sys::CollisionVector) -> Self {
777        Self { x: v.x, y: v.y }
778    }
779}
780
781pub struct SideOffsets<T> {
782    pub left: T,
783    pub right: T,
784    pub top: T,
785    pub bottom: T,
786}
787
788impl<T> SideOffsets<T> {
789    #[inline]
790    pub fn new(left: T, right: T, top: T, bottom: T) -> Self {
791        Self {
792            left,
793            right,
794            top,
795            bottom,
796        }
797    }
798
799    #[inline]
800    pub fn splat(v: T) -> Self
801    where
802        T: Clone,
803    {
804        Self {
805            left: v.clone(),
806            right: v.clone(),
807            top: v.clone(),
808            bottom: v,
809        }
810    }
811
812    /// Type conversion.
813    #[inline]
814    pub fn cast<U: NumCast>(self) -> SideOffsets<U>
815    where
816        T: NumCast,
817    {
818        self.try_cast().unwrap()
819    }
820
821    /// Type conversion.
822    #[inline]
823    pub fn try_cast<U: NumCast>(self) -> Option<SideOffsets<U>>
824    where
825        T: NumCast,
826    {
827        match (
828            NumCast::from(self.left),
829            NumCast::from(self.right),
830            NumCast::from(self.top),
831            NumCast::from(self.bottom),
832        ) {
833            (Some(l), Some(r), Some(t), Some(b)) => Some(SideOffsets::new(l, r, t, b)),
834            _ => None,
835        }
836    }
837}
838
839impl SideOffsets<i32> {
840    pub const ZERO: Self = Self {
841        left: 0,
842        right: 0,
843        top: 0,
844        bottom: 0,
845    };
846}
847
848impl From<SideOffsets<i32>> for sys::LCDRect {
849    #[inline]
850    fn from(val: SideOffsets<i32>) -> Self {
851        sys::LCDRect {
852            left: val.left,
853            right: val.right,
854            top: val.top,
855            bottom: val.bottom,
856        }
857    }
858}