1use crate::{Point, Size};
2
3#[derive(Clone, Copy, Debug, Default, PartialEq)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6pub struct Rect {
7 pub x: f32,
8 pub y: f32,
9 pub width: f32,
10 pub height: f32,
11}
12
13impl Rect {
14 pub const fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
15 Self {
16 x,
17 y,
18 width,
19 height,
20 }
21 }
22
23 pub fn from_ltrb(l: f32, t: f32, r: f32, b: f32) -> Self {
24 Self {
25 x: l,
26 y: t,
27 width: r - l,
28 height: b - t,
29 }
30 }
31
32 pub fn from_origin_size(origin: Point, size: Size) -> Self {
33 Self {
34 x: origin.x,
35 y: origin.y,
36 width: size.width,
37 height: size.height,
38 }
39 }
40
41 pub fn right(&self) -> f32 {
42 self.x + self.width
43 }
44
45 pub fn bottom(&self) -> f32 {
46 self.y + self.height
47 }
48
49 pub fn origin(&self) -> Point {
50 Point::new(self.x, self.y)
51 }
52
53 pub fn size(&self) -> Size {
54 Size::new(self.width, self.height)
55 }
56
57 pub fn is_empty(&self) -> bool {
58 self.width <= 0.0 || self.height <= 0.0
59 }
60
61 pub fn union(&self, other: &Rect) -> Rect {
63 if self.is_empty() {
64 return *other;
65 }
66 if other.is_empty() {
67 return *self;
68 }
69 Rect::from_ltrb(
70 self.x.min(other.x),
71 self.y.min(other.y),
72 self.right().max(other.right()),
73 self.bottom().max(other.bottom()),
74 )
75 }
76
77 pub fn intersect(&self, other: &Rect) -> Option<Rect> {
79 let r = Rect::from_ltrb(
80 self.x.max(other.x),
81 self.y.max(other.y),
82 self.right().min(other.right()),
83 self.bottom().min(other.bottom()),
84 );
85 (!r.is_empty()).then_some(r)
86 }
87
88 pub fn intersects(&self, other: &Rect) -> bool {
89 !self.is_empty()
90 && !other.is_empty()
91 && self.x < other.right()
92 && other.x < self.right()
93 && self.y < other.bottom()
94 && other.y < self.bottom()
95 }
96
97 pub fn contains(&self, p: Point) -> bool {
98 p.x >= self.x && p.x < self.right() && p.y >= self.y && p.y < self.bottom()
99 }
100
101 pub fn contains_inclusive(&self, p: Point) -> bool {
105 p.x >= self.x && p.x <= self.right() && p.y >= self.y && p.y <= self.bottom()
106 }
107
108 pub fn expand(&self, d: f32) -> Rect {
109 Rect::new(
110 self.x - d,
111 self.y - d,
112 self.width + 2.0 * d,
113 self.height + 2.0 * d,
114 )
115 }
116
117 pub const EVERYTHING: Rect = Rect {
121 x: -1.0e9,
122 y: -1.0e9,
123 width: 2.0e9,
124 height: 2.0e9,
125 };
126
127 pub fn corners(&self) -> [Point; 4] {
128 [
129 Point::new(self.x, self.y),
130 Point::new(self.right(), self.y),
131 Point::new(self.right(), self.bottom()),
132 Point::new(self.x, self.bottom()),
133 ]
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[test]
142 fn union_ignores_empty() {
143 let a = Rect::new(0.0, 0.0, 10.0, 10.0);
144 assert_eq!(Rect::default().union(&a), a);
145 assert_eq!(a.union(&Rect::default()), a);
146 }
147
148 #[test]
149 fn intersect_disjoint_is_none() {
150 let a = Rect::new(0.0, 0.0, 10.0, 10.0);
151 let b = Rect::new(20.0, 0.0, 10.0, 10.0);
152 assert_eq!(a.intersect(&b), None);
153 assert!(!a.intersects(&b));
154 }
155
156 #[test]
157 fn intersect_overlap() {
158 let a = Rect::new(0.0, 0.0, 10.0, 10.0);
159 let b = Rect::new(5.0, 5.0, 10.0, 10.0);
160 assert_eq!(a.intersect(&b), Some(Rect::new(5.0, 5.0, 5.0, 5.0)));
161 }
162}