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