1use core::ops::{Add, Mul, Sub};
7
8#[derive(Clone, Copy, Debug, Default, PartialEq)]
10pub struct Point {
11 pub x: f32,
13 pub y: f32,
15}
16
17impl Point {
18 pub const ZERO: Point = Point { x: 0.0, y: 0.0 };
20
21 pub const fn new(x: f32, y: f32) -> Self {
23 Self { x, y }
24 }
25
26 pub fn distance(self, other: Point) -> f32 {
28 let dx = self.x - other.x;
29 let dy = self.y - other.y;
30 (dx * dx + dy * dy).sqrt()
31 }
32}
33
34impl Add for Point {
35 type Output = Point;
36 fn add(self, rhs: Point) -> Point {
37 Point::new(self.x + rhs.x, self.y + rhs.y)
38 }
39}
40
41impl Sub for Point {
42 type Output = Point;
43 fn sub(self, rhs: Point) -> Point {
44 Point::new(self.x - rhs.x, self.y - rhs.y)
45 }
46}
47
48#[derive(Clone, Copy, Debug, Default, PartialEq)]
50pub struct Size {
51 pub width: f32,
53 pub height: f32,
55}
56
57impl Size {
58 pub const ZERO: Size = Size {
60 width: 0.0,
61 height: 0.0,
62 };
63
64 pub const fn new(width: f32, height: f32) -> Self {
66 Self { width, height }
67 }
68
69 pub fn area(self) -> f32 {
71 self.width * self.height
72 }
73
74 pub fn is_empty(self) -> bool {
76 self.width <= 0.0 || self.height <= 0.0
77 }
78
79 pub fn clamp(self, min: Size, max: Size) -> Size {
81 Size::new(
82 self.width.clamp(min.width, max.width),
83 self.height.clamp(min.height, max.height),
84 )
85 }
86}
87
88impl Mul<f32> for Size {
89 type Output = Size;
90 fn mul(self, rhs: f32) -> Size {
91 Size::new(self.width * rhs, self.height * rhs)
92 }
93}
94
95#[derive(Clone, Copy, Debug, Default, PartialEq)]
97pub struct Insets {
98 pub top: f32,
100 pub right: f32,
102 pub bottom: f32,
104 pub left: f32,
106}
107
108impl Insets {
109 pub const ZERO: Insets = Insets {
111 top: 0.0,
112 right: 0.0,
113 bottom: 0.0,
114 left: 0.0,
115 };
116
117 pub const fn new(top: f32, right: f32, bottom: f32, left: f32) -> Self {
119 Self {
120 top,
121 right,
122 bottom,
123 left,
124 }
125 }
126
127 pub const fn all(v: f32) -> Self {
129 Self {
130 top: v,
131 right: v,
132 bottom: v,
133 left: v,
134 }
135 }
136
137 pub const fn symmetric(vertical: f32, horizontal: f32) -> Self {
139 Self {
140 top: vertical,
141 right: horizontal,
142 bottom: vertical,
143 left: horizontal,
144 }
145 }
146
147 pub fn horizontal(self) -> f32 {
149 self.left + self.right
150 }
151
152 pub fn vertical(self) -> f32 {
154 self.top + self.bottom
155 }
156}
157
158#[derive(Clone, Copy, Debug, Default, PartialEq)]
160pub struct Rect {
161 pub origin: Point,
163 pub size: Size,
165}
166
167impl Rect {
168 pub const ZERO: Rect = Rect {
170 origin: Point::ZERO,
171 size: Size::ZERO,
172 };
173
174 pub const fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
176 Self {
177 origin: Point::new(x, y),
178 size: Size::new(width, height),
179 }
180 }
181
182 pub const fn from_origin_size(origin: Point, size: Size) -> Self {
184 Self { origin, size }
185 }
186
187 pub fn left(&self) -> f32 {
189 self.origin.x
190 }
191
192 pub fn top(&self) -> f32 {
194 self.origin.y
195 }
196
197 pub fn right(&self) -> f32 {
199 self.origin.x + self.size.width
200 }
201
202 pub fn bottom(&self) -> f32 {
204 self.origin.y + self.size.height
205 }
206
207 pub fn width(&self) -> f32 {
209 self.size.width
210 }
211
212 pub fn height(&self) -> f32 {
214 self.size.height
215 }
216
217 pub fn center(&self) -> Point {
219 Point::new(
220 self.origin.x + self.size.width * 0.5,
221 self.origin.y + self.size.height * 0.5,
222 )
223 }
224
225 pub fn contains(&self, p: Point) -> bool {
228 p.x >= self.left() && p.x < self.right() && p.y >= self.top() && p.y < self.bottom()
229 }
230
231 pub fn intersects(&self, other: &Rect) -> bool {
233 self.left() < other.right()
234 && other.left() < self.right()
235 && self.top() < other.bottom()
236 && other.top() < self.bottom()
237 }
238
239 pub fn intersection(&self, other: &Rect) -> Option<Rect> {
241 let left = self.left().max(other.left());
242 let top = self.top().max(other.top());
243 let right = self.right().min(other.right());
244 let bottom = self.bottom().min(other.bottom());
245 if right > left && bottom > top {
246 Some(Rect::new(left, top, right - left, bottom - top))
247 } else {
248 None
249 }
250 }
251
252 pub fn union(&self, other: &Rect) -> Rect {
254 let left = self.left().min(other.left());
255 let top = self.top().min(other.top());
256 let right = self.right().max(other.right());
257 let bottom = self.bottom().max(other.bottom());
258 Rect::new(left, top, right - left, bottom - top)
259 }
260
261 pub fn deflate(&self, insets: Insets) -> Rect {
263 let w = (self.size.width - insets.horizontal()).max(0.0);
264 let h = (self.size.height - insets.vertical()).max(0.0);
265 Rect::new(self.left() + insets.left, self.top() + insets.top, w, h)
266 }
267
268 pub fn inflate(&self, insets: Insets) -> Rect {
270 Rect::new(
271 self.left() - insets.left,
272 self.top() - insets.top,
273 self.size.width + insets.horizontal(),
274 self.size.height + insets.vertical(),
275 )
276 }
277
278 pub fn is_empty(&self) -> bool {
280 self.size.is_empty()
281 }
282}
283
284#[derive(Clone, Copy, Debug, PartialEq)]
286pub struct Constraints {
287 pub min: Size,
289 pub max: Size,
291}
292
293impl Constraints {
294 pub const fn new(min: Size, max: Size) -> Self {
296 Self { min, max }
297 }
298
299 pub fn tight(size: Size) -> Self {
301 Self {
302 min: size,
303 max: size,
304 }
305 }
306
307 pub fn loose(max: Size) -> Self {
309 Self {
310 min: Size::ZERO,
311 max,
312 }
313 }
314
315 pub fn unbounded() -> Self {
317 Self {
318 min: Size::ZERO,
319 max: Size::new(f32::INFINITY, f32::INFINITY),
320 }
321 }
322
323 pub fn constrain(&self, size: Size) -> Size {
325 size.clamp(self.min, self.max)
326 }
327
328 pub fn is_tight(&self) -> bool {
330 self.min.width == self.max.width && self.min.height == self.max.height
331 }
332}
333
334impl Default for Constraints {
335 fn default() -> Self {
336 Self::unbounded()
337 }
338}
339
340#[cfg(test)]
341mod tests {
342 use super::*;
343
344 #[test]
345 fn rect_contains_and_edges() {
346 let r = Rect::new(10.0, 20.0, 100.0, 50.0);
347 assert_eq!(r.right(), 110.0);
348 assert_eq!(r.bottom(), 70.0);
349 assert!(r.contains(Point::new(10.0, 20.0)));
350 assert!(r.contains(Point::new(109.9, 69.9)));
351 assert!(!r.contains(Point::new(110.0, 70.0))); assert!(!r.contains(Point::new(9.9, 20.0)));
353 assert_eq!(r.center(), Point::new(60.0, 45.0));
354 }
355
356 #[test]
357 fn rect_intersection_and_union() {
358 let a = Rect::new(0.0, 0.0, 10.0, 10.0);
359 let b = Rect::new(5.0, 5.0, 10.0, 10.0);
360 assert!(a.intersects(&b));
361 let i = a.intersection(&b).expect("rects overlap");
362 assert_eq!(i, Rect::new(5.0, 5.0, 5.0, 5.0));
363 let u = a.union(&b);
364 assert_eq!(u, Rect::new(0.0, 0.0, 15.0, 15.0));
365
366 let c = Rect::new(100.0, 100.0, 5.0, 5.0);
367 assert!(!a.intersects(&c));
368 assert!(a.intersection(&c).is_none());
369 }
370
371 #[test]
372 fn rect_deflate_inflate() {
373 let r = Rect::new(0.0, 0.0, 100.0, 100.0);
374 let d = r.deflate(Insets::all(10.0));
375 assert_eq!(d, Rect::new(10.0, 10.0, 80.0, 80.0));
376 let i = d.inflate(Insets::all(10.0));
377 assert_eq!(i, r);
378
379 let tiny = Rect::new(0.0, 0.0, 5.0, 5.0);
381 let clamped = tiny.deflate(Insets::all(10.0));
382 assert_eq!(clamped.width(), 0.0);
383 assert_eq!(clamped.height(), 0.0);
384 }
385
386 #[test]
387 fn constraints_constrain() {
388 let c = Constraints::new(Size::new(10.0, 10.0), Size::new(100.0, 100.0));
389 assert_eq!(c.constrain(Size::new(5.0, 200.0)), Size::new(10.0, 100.0));
390 assert!(Constraints::tight(Size::new(50.0, 50.0)).is_tight());
391 assert!(!c.is_tight());
392 }
393
394 #[test]
395 fn point_arithmetic() {
396 let p = Point::new(3.0, 4.0) + Point::new(1.0, 1.0);
397 assert_eq!(p, Point::new(4.0, 5.0));
398 assert_eq!((p - Point::new(4.0, 5.0)), Point::ZERO);
399 assert_eq!(Point::ZERO.distance(Point::new(3.0, 4.0)), 5.0);
400 }
401
402 #[test]
403 fn insets_totals() {
404 let i = Insets::symmetric(8.0, 16.0);
405 assert_eq!(i.vertical(), 16.0);
406 assert_eq!(i.horizontal(), 32.0);
407 }
408}