1#[derive(Clone, Copy, Debug, Default, PartialEq)]
9pub struct Rect {
10 pub x: f32,
11 pub y: f32,
12 pub w: f32,
13 pub h: f32,
14}
15
16impl Rect {
17 pub const fn new(x: f32, y: f32, w: f32, h: f32) -> Self {
18 Self { x, y, w, h }
19 }
20
21 pub fn right(self) -> f32 {
22 self.x + self.w
23 }
24
25 pub fn bottom(self) -> f32 {
26 self.y + self.h
27 }
28
29 pub fn center_x(self) -> f32 {
30 self.x + self.w * 0.5
31 }
32
33 pub fn center_y(self) -> f32 {
34 self.y + self.h * 0.5
35 }
36
37 pub fn contains(self, x: f32, y: f32) -> bool {
38 x >= self.x && x < self.right() && y >= self.y && y < self.bottom()
39 }
40
41 pub fn intersect(self, other: Rect) -> Option<Rect> {
42 let x1 = self.x.max(other.x);
43 let y1 = self.y.max(other.y);
44 let x2 = self.right().min(other.right());
45 let y2 = self.bottom().min(other.bottom());
46 if x2 <= x1 {
47 return None;
48 }
49 if y2 <= y1 {
50 return None;
51 }
52 Some(Rect::new(x1, y1, x2 - x1, y2 - y1))
53 }
54
55 pub fn inset(self, p: Sides) -> Self {
56 Self::new(
57 self.x + p.left,
58 self.y + p.top,
59 (self.w - p.left - p.right).max(0.0),
60 (self.h - p.top - p.bottom).max(0.0),
61 )
62 }
63
64 pub fn outset(self, p: Sides) -> Self {
66 Self::new(
67 self.x - p.left,
68 self.y - p.top,
69 self.w + p.left + p.right,
70 self.h + p.top + p.bottom,
71 )
72 }
73}
74
75#[derive(Clone, Copy, Debug, Default, PartialEq)]
77pub struct Sides {
78 pub left: f32,
79 pub right: f32,
80 pub top: f32,
81 pub bottom: f32,
82}
83
84impl Sides {
85 pub const fn all(v: f32) -> Self {
86 Self {
87 left: v,
88 right: v,
89 top: v,
90 bottom: v,
91 }
92 }
93
94 pub const fn xy(x: f32, y: f32) -> Self {
95 Self {
96 left: x,
97 right: x,
98 top: y,
99 bottom: y,
100 }
101 }
102
103 pub const fn x(v: f32) -> Self {
106 Self {
107 left: v,
108 right: v,
109 top: 0.0,
110 bottom: 0.0,
111 }
112 }
113
114 pub const fn y(v: f32) -> Self {
117 Self {
118 left: 0.0,
119 right: 0.0,
120 top: v,
121 bottom: v,
122 }
123 }
124
125 pub const fn left(v: f32) -> Self {
128 Self {
129 left: v,
130 right: 0.0,
131 top: 0.0,
132 bottom: 0.0,
133 }
134 }
135
136 pub const fn right(v: f32) -> Self {
139 Self {
140 left: 0.0,
141 right: v,
142 top: 0.0,
143 bottom: 0.0,
144 }
145 }
146
147 pub const fn top(v: f32) -> Self {
150 Self {
151 left: 0.0,
152 right: 0.0,
153 top: v,
154 bottom: 0.0,
155 }
156 }
157
158 pub const fn bottom(v: f32) -> Self {
161 Self {
162 left: 0.0,
163 right: 0.0,
164 top: 0.0,
165 bottom: v,
166 }
167 }
168
169 pub const fn zero() -> Self {
170 Self::all(0.0)
171 }
172}
173
174impl From<f32> for Sides {
175 fn from(v: f32) -> Self {
176 Sides::all(v)
177 }
178}
179
180#[derive(Clone, Copy, Debug, Default, PartialEq)]
189pub struct Corners {
190 pub tl: f32,
191 pub tr: f32,
192 pub br: f32,
193 pub bl: f32,
194}
195
196impl Corners {
197 pub const ZERO: Self = Self::all(0.0);
198
199 pub const fn all(r: f32) -> Self {
200 Self {
201 tl: r,
202 tr: r,
203 br: r,
204 bl: r,
205 }
206 }
207
208 pub const fn top(r: f32) -> Self {
212 Self {
213 tl: r,
214 tr: r,
215 br: 0.0,
216 bl: 0.0,
217 }
218 }
219
220 pub const fn bottom(r: f32) -> Self {
222 Self {
223 tl: 0.0,
224 tr: 0.0,
225 br: r,
226 bl: r,
227 }
228 }
229
230 pub const fn left(r: f32) -> Self {
232 Self {
233 tl: r,
234 tr: 0.0,
235 br: 0.0,
236 bl: r,
237 }
238 }
239
240 pub const fn right(r: f32) -> Self {
242 Self {
243 tl: 0.0,
244 tr: r,
245 br: r,
246 bl: 0.0,
247 }
248 }
249
250 pub fn is_uniform(self) -> bool {
254 self.tl == self.tr && self.tr == self.br && self.br == self.bl
255 }
256
257 pub fn any_nonzero(self) -> bool {
259 self.tl > 0.0 || self.tr > 0.0 || self.br > 0.0 || self.bl > 0.0
260 }
261
262 pub fn max(self) -> f32 {
266 self.tl.max(self.tr).max(self.br).max(self.bl)
267 }
268
269 pub fn to_array(self) -> [f32; 4] {
272 [self.tl, self.tr, self.br, self.bl]
273 }
274}
275
276impl From<f32> for Corners {
277 fn from(r: f32) -> Self {
278 Corners::all(r)
279 }
280}
281
282#[cfg(test)]
283mod corners_tests {
284 use super::*;
285
286 #[test]
287 fn shorthand_constructors_only_round_their_named_corners() {
288 let top = Corners::top(8.0);
289 assert_eq!(
290 top,
291 Corners {
292 tl: 8.0,
293 tr: 8.0,
294 br: 0.0,
295 bl: 0.0
296 }
297 );
298
299 let bottom = Corners::bottom(8.0);
300 assert_eq!(
301 bottom,
302 Corners {
303 tl: 0.0,
304 tr: 0.0,
305 br: 8.0,
306 bl: 8.0
307 }
308 );
309
310 let left = Corners::left(8.0);
311 assert_eq!(
312 left,
313 Corners {
314 tl: 8.0,
315 tr: 0.0,
316 br: 0.0,
317 bl: 8.0
318 }
319 );
320
321 let right = Corners::right(8.0);
322 assert_eq!(
323 right,
324 Corners {
325 tl: 0.0,
326 tr: 8.0,
327 br: 8.0,
328 bl: 0.0
329 }
330 );
331 }
332
333 #[test]
334 fn is_uniform_is_true_only_when_all_four_corners_match() {
335 assert!(Corners::all(8.0).is_uniform());
336 assert!(Corners::ZERO.is_uniform());
337 assert!(!Corners::top(8.0).is_uniform());
338 }
339
340 #[test]
341 fn from_f32_produces_uniform_corners_for_back_compat() {
342 let c: Corners = 12.0_f32.into();
346 assert_eq!(c, Corners::all(12.0));
347 }
348}