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 distance(&self, point: Point) -> f32 {
149 let center = self.center();
150
151 let distance_x =
152 ((point.x - center.x).abs() - self.width / 2.0).max(0.0);
153
154 let distance_y =
155 ((point.y - center.y).abs() - self.height / 2.0).max(0.0);
156
157 distance_x.hypot(distance_y)
158 }
159
160 pub fn is_within(&self, container: &Rectangle) -> bool {
163 container.contains(self.position())
164 && container.contains(
165 self.position() + Vector::new(self.width, self.height),
166 )
167 }
168
169 pub fn intersection(
171 &self,
172 other: &Rectangle<f32>,
173 ) -> Option<Rectangle<f32>> {
174 let x = self.x.max(other.x);
175 let y = self.y.max(other.y);
176
177 let lower_right_x = (self.x + self.width).min(other.x + other.width);
178 let lower_right_y = (self.y + self.height).min(other.y + other.height);
179
180 let width = lower_right_x - x;
181 let height = lower_right_y - y;
182
183 if width > 0.0 && height > 0.0 {
184 Some(Rectangle {
185 x,
186 y,
187 width,
188 height,
189 })
190 } else {
191 None
192 }
193 }
194
195 pub fn intersects(&self, other: &Self) -> bool {
197 self.intersection(other).is_some()
198 }
199
200 pub fn union(&self, other: &Self) -> Self {
202 let x = self.x.min(other.x);
203 let y = self.y.min(other.y);
204
205 let lower_right_x = (self.x + self.width).max(other.x + other.width);
206 let lower_right_y = (self.y + self.height).max(other.y + other.height);
207
208 let width = lower_right_x - x;
209 let height = lower_right_y - y;
210
211 Rectangle {
212 x,
213 y,
214 width,
215 height,
216 }
217 }
218
219 pub fn snap(self) -> Option<Rectangle<u32>> {
221 let width = self.width as u32;
222 let height = self.height as u32;
223
224 if width < 1 || height < 1 {
225 return None;
226 }
227
228 Some(Rectangle {
229 x: self.x as u32,
230 y: self.y as u32,
231 width,
232 height,
233 })
234 }
235
236 pub fn expand(self, padding: impl Into<Padding>) -> Self {
238 let padding = padding.into();
239
240 Self {
241 x: self.x - padding.left,
242 y: self.y - padding.top,
243 width: self.width + padding.horizontal(),
244 height: self.height + padding.vertical(),
245 }
246 }
247
248 pub fn shrink(self, padding: impl Into<Padding>) -> Self {
250 let padding = padding.into();
251
252 Self {
253 x: self.x + padding.left,
254 y: self.y + padding.top,
255 width: self.width - padding.horizontal(),
256 height: self.height - padding.vertical(),
257 }
258 }
259
260 pub fn rotate(self, rotation: Radians) -> Self {
263 let size = self.size().rotate(rotation);
264 let position = Point::new(
265 self.center_x() - size.width / 2.0,
266 self.center_y() - size.height / 2.0,
267 );
268
269 Self::new(position, size)
270 }
271}
272
273impl std::ops::Mul<f32> for Rectangle<f32> {
274 type Output = Self;
275
276 fn mul(self, scale: f32) -> Self {
277 Self {
278 x: self.x * scale,
279 y: self.y * scale,
280 width: self.width * scale,
281 height: self.height * scale,
282 }
283 }
284}
285
286impl From<Rectangle<u32>> for Rectangle<f32> {
287 fn from(rectangle: Rectangle<u32>) -> Rectangle<f32> {
288 Rectangle {
289 x: rectangle.x as f32,
290 y: rectangle.y as f32,
291 width: rectangle.width as f32,
292 height: rectangle.height as f32,
293 }
294 }
295}
296
297impl<T> std::ops::Add<Vector<T>> for Rectangle<T>
298where
299 T: std::ops::Add<Output = T>,
300{
301 type Output = Rectangle<T>;
302
303 fn add(self, translation: Vector<T>) -> Self {
304 Rectangle {
305 x: self.x + translation.x,
306 y: self.y + translation.y,
307 ..self
308 }
309 }
310}
311
312impl<T> std::ops::Sub<Vector<T>> for Rectangle<T>
313where
314 T: std::ops::Sub<Output = T>,
315{
316 type Output = Rectangle<T>;
317
318 fn sub(self, translation: Vector<T>) -> Self {
319 Rectangle {
320 x: self.x - translation.x,
321 y: self.y - translation.y,
322 ..self
323 }
324 }
325}
326
327impl<T> std::ops::Mul<Vector<T>> for Rectangle<T>
328where
329 T: std::ops::Mul<Output = T> + Copy,
330{
331 type Output = Rectangle<T>;
332
333 fn mul(self, scale: Vector<T>) -> Self {
334 Rectangle {
335 x: self.x * scale.x,
336 y: self.y * scale.y,
337 width: self.width * scale.x,
338 height: self.height * scale.y,
339 }
340 }
341}