1use std::ops::{Add, AddAssign, Sub, SubAssign};
2
3use crate::traits::{IntoSigned, IntoUnsigned, Ranged, StdNumOps};
4use crate::{FloatConversion, IntoComponents, Point, Round, Size, Zero};
5
6#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub struct Rect<Unit> {
10 pub origin: Point<Unit>,
12 pub size: Size<Unit>,
14}
15
16impl<Unit> Rect<Unit> {
17 pub const fn new(origin: Point<Unit>, size: Size<Unit>) -> Self {
19 Self { origin, size }
20 }
21
22 pub fn from_extents(p1: Point<Unit>, p2: Point<Unit>) -> Self
28 where
29 Unit: crate::Unit,
30 {
31 let min_x = p1.x.min(p2.x);
32 let min_y = p1.y.min(p2.y);
33 let max_x = p1.x.max(p2.x);
34 let max_y = p1.y.max(p2.y);
35 Self {
36 origin: Point { x: min_x, y: min_y },
37 size: Size {
38 width: max_x - min_x,
39 height: max_y - min_y,
40 },
41 }
42 }
43
44 #[must_use]
48 pub fn expand_rounded(self) -> Self
49 where
50 Unit: Round + crate::Unit,
51 {
52 let (tl, br) = self.extents();
53
54 Self::from_extents(tl.floor(), br.ceil())
55 }
56
57 #[must_use]
60 pub fn map<NewUnit>(self, mut map: impl FnMut(Unit) -> NewUnit) -> Rect<NewUnit> {
61 Rect {
62 origin: self.origin.map(&mut map),
63 size: self.size.map(map),
64 }
65 }
66
67 #[must_use]
69 pub fn inset(mut self, amount: impl Into<Unit>) -> Self
70 where
71 Unit: Add<Unit, Output = Unit> + AddAssign<Unit> + SubAssign<Unit> + Copy,
72 {
73 let amount = amount.into();
74 let double_amount = amount + amount;
75 self.origin.x += amount;
76 self.origin.y += amount;
77 self.size.width -= double_amount;
78 self.size.height -= double_amount;
79 self
80 }
81
82 pub fn cast<NewUnit>(self) -> Rect<NewUnit>
84 where
85 NewUnit: From<Unit>,
86 {
87 Rect {
88 origin: self.origin.cast(),
89 size: self.size.cast(),
90 }
91 }
92
93 pub fn try_cast<NewUnit>(self) -> Result<Rect<NewUnit>, NewUnit::Error>
100 where
101 NewUnit: TryFrom<Unit>,
102 {
103 Ok(Rect {
104 origin: self.origin.try_cast()?,
105 size: self.size.try_cast()?,
106 })
107 }
108
109 pub fn contains(&self, point: Point<Unit>) -> bool
111 where
112 Unit: crate::Unit,
113 {
114 let (p1, p2) = self.extents();
115 p1.x <= point.x && p1.y <= point.y && p2.x > point.x && p2.y > point.y
116 }
117
118 pub fn intersects(&self, other: &Self) -> bool
132 where
133 Unit: Add<Output = Unit> + Ord + Copy,
134 {
135 let (
136 Point {
137 x: r1_left,
138 y: r1_top,
139 },
140 Point {
141 x: r1_right,
142 y: r1_bottom,
143 },
144 ) = self.extents();
145 let (
146 Point {
147 x: r2_left,
148 y: r2_top,
149 },
150 Point {
151 x: r2_right,
152 y: r2_bottom,
153 },
154 ) = other.extents();
155 !(r1_right <= r2_left || r2_right <= r1_left || r1_bottom <= r2_top || r1_top >= r2_bottom)
156 }
157
158 pub fn intersection(&self, other: &Self) -> Option<Rect<Unit>>
174 where
175 Unit: crate::Unit,
176 {
177 let (a1, a2) = self.extents();
178 let (b1, b2) = other.extents();
179 let x1 = a1.x.max(b1.x);
180 let x2 = a2.x.min(b2.x);
181 if x2 > x1 {
182 let y1 = a1.y.max(b1.y);
183 let y2 = a2.y.min(b2.y);
184 if y2 > y1 {
185 return Some(Rect::from_extents(Point::new(x1, y1), Point::new(x2, y2)));
186 }
187 }
188 None
189 }
190
191 pub fn extent(&self) -> Point<Unit>
193 where
194 Unit: crate::Unit,
195 {
196 self.origin + self.size
197 }
198}
199
200impl<Unit> Rect<Unit>
201where
202 Unit: Add<Output = Unit> + Ord + Copy,
203{
204 pub fn extents(&self) -> (Point<Unit>, Point<Unit>) {
208 let extent = self.origin + self.size;
209 (
210 Point::new(self.origin.x.min(extent.x), self.origin.y.min(extent.y)),
211 Point::new(self.origin.x.max(extent.x), self.origin.y.max(extent.y)),
212 )
213 }
214}
215
216impl<Unit> Rect<Unit>
217where
218 Unit: StdNumOps + Ord + Copy,
219{
220 pub fn saturating_extents(&self) -> (Point<Unit>, Point<Unit>) {
227 let extent = self.origin.saturating_add(self.size.to_vec());
228 (
229 Point::new(self.origin.x.min(extent.x), self.origin.y.min(extent.y)),
230 Point::new(self.origin.x.max(extent.x), self.origin.y.max(extent.y)),
231 )
232 }
233}
234
235impl<Unit> Default for Rect<Unit>
236where
237 Unit: Default,
238{
239 fn default() -> Self {
240 Self {
241 origin: Point::default(),
242 size: Size::default(),
243 }
244 }
245}
246
247impl<Unit> IntoUnsigned for Rect<Unit>
248where
249 Unit: IntoUnsigned,
250{
251 type Unsigned = Rect<Unit::Unsigned>;
252
253 fn into_unsigned(self) -> Self::Unsigned {
254 Rect {
255 origin: self.origin.into_unsigned(),
256 size: self.size.into_unsigned(),
257 }
258 }
259}
260
261impl<Unit> IntoSigned for Rect<Unit>
262where
263 Unit: IntoSigned,
264{
265 type Signed = Rect<Unit::Signed>;
266
267 fn into_signed(self) -> Self::Signed {
268 Rect {
269 origin: self.origin.into_signed(),
270 size: self.size.into_signed(),
271 }
272 }
273}
274
275impl<Unit> From<Size<Unit>> for Rect<Unit>
276where
277 Unit: Default,
278{
279 fn from(size: Size<Unit>) -> Self {
280 Self::new(Point::default(), size)
281 }
282}
283
284impl<Unit> Add<Point<Unit>> for Rect<Unit>
285where
286 Unit: Add<Output = Unit>,
287{
288 type Output = Self;
289
290 fn add(self, rhs: Point<Unit>) -> Self::Output {
291 Self::new(self.origin + rhs, self.size)
292 }
293}
294
295impl<Unit> Sub<Point<Unit>> for Rect<Unit>
296where
297 Unit: Sub<Output = Unit>,
298{
299 type Output = Self;
300
301 fn sub(self, rhs: Point<Unit>) -> Self::Output {
302 Self::new(self.origin - rhs, self.size)
303 }
304}
305
306impl<Unit> Ranged for Rect<Unit>
307where
308 Unit: Ranged,
309{
310 const MAX: Self = Self::new(Point::MAX, Size::MAX);
311 const MIN: Self = Self::new(Point::MIN, Size::MIN);
312}
313
314impl<Unit> Zero for Rect<Unit>
315where
316 Unit: Zero,
317{
318 const ZERO: Self = Self {
319 origin: Point::ZERO,
320 size: Size::ZERO,
321 };
322
323 fn is_zero(&self) -> bool {
324 self.origin.is_zero() && self.size.is_zero()
325 }
326}
327
328impl<Unit> FloatConversion for Rect<Unit>
329where
330 Unit: FloatConversion,
331{
332 type Float = Rect<Unit::Float>;
333
334 fn into_float(self) -> Self::Float {
335 self.map(FloatConversion::into_float)
336 }
337
338 fn from_float(float: Self::Float) -> Self {
339 float.map(FloatConversion::from_float)
340 }
341}
342
343#[test]
344fn intersection() {
345 assert_eq!(
346 Rect::<i32>::new(Point::new(1, 1,), Size::new(3, 3))
347 .intersection(&Rect::new(Point::new(2, 2,), Size::new(3, 3))),
348 Some(Rect::new(Point::new(2, 2,), Size::new(2, 2)))
349 );
350}