lunar_math/
screen_rect.rs1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct ScreenRect {
16 pub x1: i16,
17 pub y1: i16,
18 pub x2: i16,
19 pub y2: i16,
20}
21
22impl ScreenRect {
23 pub const EMPTY: Self = Self {
25 x1: i16::MAX,
26 y1: i16::MAX,
27 x2: i16::MIN,
28 y2: i16::MIN,
29 };
30
31 #[must_use]
33 pub const fn full(width: u16, height: u16) -> Self {
34 Self {
35 x1: 0,
36 y1: 0,
37 x2: width as i16 - 1,
38 y2: height as i16 - 1,
39 }
40 }
41
42 #[must_use]
43 pub const fn new(x1: i16, y1: i16, x2: i16, y2: i16) -> Self {
44 Self { x1, y1, x2, y2 }
45 }
46
47 #[must_use]
49 pub const fn is_empty(self) -> bool {
50 self.x1 > self.x2 || self.y1 > self.y2
51 }
52
53 pub fn add_point(&mut self, x: i16, y: i16) {
55 if x < self.x1 {
56 self.x1 = x;
57 }
58 if x > self.x2 {
59 self.x2 = x;
60 }
61 if y < self.y1 {
62 self.y1 = y;
63 }
64 if y > self.y2 {
65 self.y2 = y;
66 }
67 }
68
69 #[must_use]
71 pub const fn union(self, other: Self) -> Self {
72 Self {
73 x1: if self.x1 < other.x1 {
74 self.x1
75 } else {
76 other.x1
77 },
78 y1: if self.y1 < other.y1 {
79 self.y1
80 } else {
81 other.y1
82 },
83 x2: if self.x2 > other.x2 {
84 self.x2
85 } else {
86 other.x2
87 },
88 y2: if self.y2 > other.y2 {
89 self.y2
90 } else {
91 other.y2
92 },
93 }
94 }
95
96 #[must_use]
98 pub const fn intersect(self, other: Self) -> Self {
99 Self {
100 x1: if self.x1 > other.x1 {
101 self.x1
102 } else {
103 other.x1
104 },
105 y1: if self.y1 > other.y1 {
106 self.y1
107 } else {
108 other.y1
109 },
110 x2: if self.x2 < other.x2 {
111 self.x2
112 } else {
113 other.x2
114 },
115 y2: if self.y2 < other.y2 {
116 self.y2
117 } else {
118 other.y2
119 },
120 }
121 }
122
123 #[must_use]
125 pub const fn width(self) -> u16 {
126 if self.x2 >= self.x1 {
127 (self.x2 - self.x1 + 1) as u16
128 } else {
129 0
130 }
131 }
132
133 #[must_use]
135 pub const fn height(self) -> u16 {
136 if self.y2 >= self.y1 {
137 (self.y2 - self.y1 + 1) as u16
138 } else {
139 0
140 }
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn empty_has_no_area() {
150 assert!(ScreenRect::EMPTY.is_empty());
151 }
152
153 #[test]
154 fn full_covers_screen() {
155 let r = ScreenRect::full(1920, 1080);
156 assert_eq!(r.width(), 1920);
157 assert_eq!(r.height(), 1080);
158 assert!(!r.is_empty());
159 }
160
161 #[test]
162 fn add_point_expands() {
163 let mut r = ScreenRect::EMPTY;
164 r.add_point(10, 20);
165 r.add_point(50, 80);
166 assert_eq!(r.x1, 10);
167 assert_eq!(r.y1, 20);
168 assert_eq!(r.x2, 50);
169 assert_eq!(r.y2, 80);
170 }
171
172 #[test]
173 fn union_encloses_both() {
174 let a = ScreenRect::new(0, 0, 10, 10);
175 let b = ScreenRect::new(5, 5, 20, 20);
176 let u = a.union(b);
177 assert_eq!(u, ScreenRect::new(0, 0, 20, 20));
178 }
179
180 #[test]
181 fn intersect_clips_to_overlap() {
182 let a = ScreenRect::new(0, 0, 20, 20);
183 let b = ScreenRect::new(10, 10, 30, 30);
184 let i = a.intersect(b);
185 assert_eq!(i, ScreenRect::new(10, 10, 20, 20));
186 }
187
188 #[test]
189 fn intersect_non_overlapping_is_empty() {
190 let a = ScreenRect::new(0, 0, 5, 5);
191 let b = ScreenRect::new(10, 10, 20, 20);
192 assert!(a.intersect(b).is_empty());
193 }
194}