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,
9 pub y: f32,
11 pub width: f32,
13 pub height: f32,
15}
16
17impl Rect {
18 pub const fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
20 Self {
21 x,
22 y,
23 width,
24 height,
25 }
26 }
27
28 pub fn from_ltrb(l: f32, t: f32, r: f32, b: f32) -> Self {
30 Self {
31 x: l,
32 y: t,
33 width: r - l,
34 height: b - t,
35 }
36 }
37
38 pub fn from_origin_size(origin: Point, size: Size) -> Self {
40 Self {
41 x: origin.x,
42 y: origin.y,
43 width: size.width,
44 height: size.height,
45 }
46 }
47
48 pub fn right(&self) -> f32 {
50 self.x + self.width
51 }
52
53 pub fn bottom(&self) -> f32 {
55 self.y + self.height
56 }
57
58 pub fn origin(&self) -> Point {
60 Point::new(self.x, self.y)
61 }
62
63 pub fn size(&self) -> Size {
65 Size::new(self.width, self.height)
66 }
67
68 pub fn is_empty(&self) -> bool {
70 self.width <= 0.0 || self.height <= 0.0
71 }
72
73 pub fn union(&self, other: &Rect) -> Rect {
77 if self.is_empty() {
78 return *other;
79 }
80 if other.is_empty() {
81 return *self;
82 }
83 Rect::from_ltrb(
84 self.x.min(other.x),
85 self.y.min(other.y),
86 self.right().max(other.right()),
87 self.bottom().max(other.bottom()),
88 )
89 }
90
91 pub fn intersect(&self, other: &Rect) -> Option<Rect> {
93 let r = Rect::from_ltrb(
94 self.x.max(other.x),
95 self.y.max(other.y),
96 self.right().min(other.right()),
97 self.bottom().min(other.bottom()),
98 );
99 (!r.is_empty()).then_some(r)
100 }
101
102 pub fn intersects(&self, other: &Rect) -> bool {
104 !self.is_empty()
105 && !other.is_empty()
106 && self.x < other.right()
107 && other.x < self.right()
108 && self.y < other.bottom()
109 && other.y < self.bottom()
110 }
111
112 pub fn contains(&self, p: Point) -> bool {
116 p.x >= self.x && p.x < self.right() && p.y >= self.y && p.y < self.bottom()
117 }
118
119 pub fn contains_inclusive(&self, p: Point) -> bool {
121 p.x >= self.x && p.x <= self.right() && p.y >= self.y && p.y <= self.bottom()
122 }
123
124 pub fn expand(&self, d: f32) -> Rect {
128 Rect::new(
129 self.x - d,
130 self.y - d,
131 self.width + 2.0 * d,
132 self.height + 2.0 * d,
133 )
134 }
135
136 pub const EVERYTHING: Rect = Rect {
138 x: -1.0e9,
139 y: -1.0e9,
140 width: 2.0e9,
141 height: 2.0e9,
142 };
143
144 pub fn corners(&self) -> [Point; 4] {
146 [
147 Point::new(self.x, self.y),
148 Point::new(self.right(), self.y),
149 Point::new(self.right(), self.bottom()),
150 Point::new(self.x, self.bottom()),
151 ]
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 #[test]
160 fn union_ignores_empty() {
161 let a = Rect::new(0.0, 0.0, 10.0, 10.0);
162 assert_eq!(Rect::default().union(&a), a);
163 assert_eq!(a.union(&Rect::default()), a);
164 }
165
166 #[test]
167 fn intersect_disjoint_is_none() {
168 let a = Rect::new(0.0, 0.0, 10.0, 10.0);
169 let b = Rect::new(20.0, 0.0, 10.0, 10.0);
170 assert_eq!(a.intersect(&b), None);
171 assert!(!a.intersects(&b));
172 }
173
174 #[test]
175 fn intersect_overlap() {
176 let a = Rect::new(0.0, 0.0, 10.0, 10.0);
177 let b = Rect::new(5.0, 5.0, 10.0, 10.0);
178 assert_eq!(a.intersect(&b), Some(Rect::new(5.0, 5.0, 5.0, 5.0)));
179 }
180}