1use crate::{Padding, Point, Radians, Size, Vector};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
5pub struct Rectangle<T = f32> {
6 pub x: T,
8
9 pub y: T,
11
12 pub width: T,
14
15 pub height: T,
17}
18
19impl<T> Rectangle<T>
20where
21 T: Default,
22{
23 pub fn with_size(size: Size<T>) -> Self {
26 Self {
27 x: T::default(),
28 y: T::default(),
29 width: size.width,
30 height: size.height,
31 }
32 }
33}
34
35impl Rectangle<f32> {
36 pub const INFINITE: Self = Self::new(Point::ORIGIN, Size::INFINITY);
38
39 pub const fn new(top_left: Point, size: Size) -> Self {
42 Self {
43 x: top_left.x,
44 y: top_left.y,
45 width: size.width,
46 height: size.height,
47 }
48 }
49
50 pub fn with_radius(radius: f32) -> Self {
53 Self {
54 x: -radius,
55 y: -radius,
56 width: radius * 2.0,
57 height: radius * 2.0,
58 }
59 }
60
61 pub fn with_vertices(
65 top_left: Point,
66 top_right: Point,
67 bottom_left: Point,
68 ) -> (Rectangle, Radians) {
69 let width = (top_right.x - top_left.x).hypot(top_right.y - top_left.y);
70
71 let height =
72 (bottom_left.x - top_left.x).hypot(bottom_left.y - top_left.y);
73
74 let rotation =
75 (top_right.y - top_left.y).atan2(top_right.x - top_left.x);
76
77 let rotation = if rotation < 0.0 {
78 2.0 * std::f32::consts::PI + rotation
79 } else {
80 rotation
81 };
82
83 let position = {
84 let center = Point::new(
85 (top_right.x + bottom_left.x) / 2.0,
86 (top_right.y + bottom_left.y) / 2.0,
87 );
88
89 let rotation = -rotation - std::f32::consts::PI * 2.0;
90
91 Point::new(
92 center.x + (top_left.x - center.x) * rotation.cos()
93 - (top_left.y - center.y) * rotation.sin(),
94 center.y
95 + (top_left.x - center.x) * rotation.sin()
96 + (top_left.y - center.y) * rotation.cos(),
97 )
98 };
99
100 (
101 Rectangle::new(position, Size::new(width, height)),
102 Radians(rotation),
103 )
104 }
105
106 pub fn center(&self) -> Point {
108 Point::new(self.center_x(), self.center_y())
109 }
110
111 pub fn center_x(&self) -> f32 {
114 self.x + self.width / 2.0
115 }
116
117 pub fn center_y(&self) -> f32 {
120 self.y + self.height / 2.0
121 }
122
123 pub fn position(&self) -> Point {
125 Point::new(self.x, self.y)
126 }
127
128 pub fn size(&self) -> Size {
130 Size::new(self.width, self.height)
131 }
132
133 pub fn area(&self) -> f32 {
135 self.width * self.height
136 }
137
138 pub fn contains(&self, point: Point) -> bool {
140 self.x <= point.x
141 && point.x < self.x + self.width
142 && self.y <= point.y
143 && point.y < self.y + self.height
144 }
145
146 pub fn contains_strict(&self, point: Point) -> bool {
150 self.x < point.x
151 && point.x < self.x + self.width
152 && self.y < point.y
153 && point.y < self.y + self.height
154 }
155
156 pub fn is_within(&self, container: &Rectangle) -> bool {
159 container.contains(self.position())
160 && container.contains(
161 self.position() + Vector::new(self.width, self.height),
162 )
163 }
164
165 pub fn is_within_strict(&self, container: &Rectangle) -> bool {
169 container.contains_strict(self.position())
170 && container.contains_strict(
171 self.position() + Vector::new(self.width, self.height),
172 )
173 }
174
175 pub fn intersection(
177 &self,
178 other: &Rectangle<f32>,
179 ) -> Option<Rectangle<f32>> {
180 let x = self.x.max(other.x);
181 let y = self.y.max(other.y);
182
183 let lower_right_x = (self.x + self.width).min(other.x + other.width);
184 let lower_right_y = (self.y + self.height).min(other.y + other.height);
185
186 let width = lower_right_x - x;
187 let height = lower_right_y - y;
188
189 if width > 0.0 && height > 0.0 {
190 Some(Rectangle {
191 x,
192 y,
193 width,
194 height,
195 })
196 } else {
197 None
198 }
199 }
200
201 pub fn intersects(&self, other: &Self) -> bool {
203 self.intersection(other).is_some()
204 }
205
206 pub fn union(&self, other: &Self) -> Self {
208 let x = self.x.min(other.x);
209 let y = self.y.min(other.y);
210
211 let lower_right_x = (self.x + self.width).max(other.x + other.width);
212 let lower_right_y = (self.y + self.height).max(other.y + other.height);
213
214 let width = lower_right_x - x;
215 let height = lower_right_y - y;
216
217 Rectangle {
218 x,
219 y,
220 width,
221 height,
222 }
223 }
224
225 pub fn snap(self) -> Option<Rectangle<u32>> {
227 let width = self.width as u32;
228 let height = self.height as u32;
229
230 if width < 1 || height < 1 {
231 return None;
232 }
233
234 Some(Rectangle {
235 x: self.x as u32,
236 y: self.y as u32,
237 width,
238 height,
239 })
240 }
241
242 pub fn expand(self, padding: impl Into<Padding>) -> Self {
244 let padding = padding.into();
245
246 Self {
247 x: self.x - padding.left,
248 y: self.y - padding.top,
249 width: self.width + padding.horizontal(),
250 height: self.height + padding.vertical(),
251 }
252 }
253
254 pub fn shrink(self, padding: impl Into<Padding>) -> Self {
256 let padding = padding.into();
257
258 Self {
259 x: self.x + padding.left,
260 y: self.y + padding.top,
261 width: self.width - padding.horizontal(),
262 height: self.height - padding.vertical(),
263 }
264 }
265
266 pub fn rotate(self, rotation: Radians) -> Self {
269 let size = self.size().rotate(rotation);
270 let position = Point::new(
271 self.center_x() - size.width / 2.0,
272 self.center_y() - size.height / 2.0,
273 );
274
275 Self::new(position, size)
276 }
277}
278
279impl std::ops::Mul<f32> for Rectangle<f32> {
280 type Output = Self;
281
282 fn mul(self, scale: f32) -> Self {
283 Self {
284 x: self.x * scale,
285 y: self.y * scale,
286 width: self.width * scale,
287 height: self.height * scale,
288 }
289 }
290}
291
292impl From<Rectangle<u32>> for Rectangle<f32> {
293 fn from(rectangle: Rectangle<u32>) -> Rectangle<f32> {
294 Rectangle {
295 x: rectangle.x as f32,
296 y: rectangle.y as f32,
297 width: rectangle.width as f32,
298 height: rectangle.height as f32,
299 }
300 }
301}
302
303impl<T> std::ops::Add<Vector<T>> for Rectangle<T>
304where
305 T: std::ops::Add<Output = T>,
306{
307 type Output = Rectangle<T>;
308
309 fn add(self, translation: Vector<T>) -> Self {
310 Rectangle {
311 x: self.x + translation.x,
312 y: self.y + translation.y,
313 ..self
314 }
315 }
316}
317
318impl<T> std::ops::Sub<Vector<T>> for Rectangle<T>
319where
320 T: std::ops::Sub<Output = T>,
321{
322 type Output = Rectangle<T>;
323
324 fn sub(self, translation: Vector<T>) -> Self {
325 Rectangle {
326 x: self.x - translation.x,
327 y: self.y - translation.y,
328 ..self
329 }
330 }
331}
332
333impl<T> std::ops::Mul<Vector<T>> for Rectangle<T>
334where
335 T: std::ops::Mul<Output = T> + Copy,
336{
337 type Output = Rectangle<T>;
338
339 fn mul(self, scale: Vector<T>) -> Self {
340 Rectangle {
341 x: self.x * scale.x,
342 y: self.y * scale.y,
343 width: self.width * scale.x,
344 height: self.height * scale.y,
345 }
346 }
347}