bevy_math/rects/urect.rs
1use crate::{IRect, Rect, UVec2};
2
3#[cfg(feature = "bevy_reflect")]
4use bevy_reflect::{std_traits::ReflectDefault, Reflect};
5#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
6use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
7
8/// A rectangle defined by two opposite corners.
9///
10/// The rectangle is axis aligned, and defined by its minimum and maximum coordinates,
11/// stored in `URect::min` and `URect::max`, respectively. The minimum/maximum invariant
12/// must be upheld by the user when directly assigning the fields, otherwise some methods
13/// produce invalid results. It is generally recommended to use one of the constructor
14/// methods instead, which will ensure this invariant is met, unless you already have
15/// the minimum and maximum corners.
16#[repr(C)]
17#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)]
18#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
19#[cfg_attr(
20 feature = "bevy_reflect",
21 derive(Reflect),
22 reflect(Debug, PartialEq, Hash, Default)
23)]
24#[cfg_attr(
25 all(feature = "serialize", feature = "bevy_reflect"),
26 reflect(Serialize, Deserialize)
27)]
28pub struct URect {
29 /// The minimum corner point of the rect.
30 pub min: UVec2,
31 /// The maximum corner point of the rect.
32 pub max: UVec2,
33}
34
35impl URect {
36 /// An empty `URect`, represented by maximum and minimum corner points
37 /// with `max == UVec2::MIN` and `min == UVec2::MAX`, so the
38 /// rect has an extremely large negative size.
39 /// This is useful, because when taking a union B of a non-empty `URect` A and
40 /// this empty `URect`, B will simply equal A.
41 pub const EMPTY: Self = Self {
42 max: UVec2::MIN,
43 min: UVec2::MAX,
44 };
45 /// Create a new rectangle from two corner points.
46 ///
47 /// The two points do not need to be the minimum and/or maximum corners.
48 /// They only need to be two opposite corners.
49 ///
50 /// # Examples
51 ///
52 /// ```
53 /// # use bevy_math::URect;
54 /// let r = URect::new(0, 4, 10, 6); // w=10 h=2
55 /// let r = URect::new(2, 4, 5, 0); // w=3 h=4
56 /// ```
57 #[inline]
58 pub fn new(x0: u32, y0: u32, x1: u32, y1: u32) -> Self {
59 Self::from_corners(UVec2::new(x0, y0), UVec2::new(x1, y1))
60 }
61
62 /// Create a new rectangle from two corner points.
63 ///
64 /// The two points do not need to be the minimum and/or maximum corners.
65 /// They only need to be two opposite corners.
66 ///
67 /// # Examples
68 ///
69 /// ```
70 /// # use bevy_math::{URect, UVec2};
71 /// // Unit rect from [0,0] to [1,1]
72 /// let r = URect::from_corners(UVec2::ZERO, UVec2::ONE); // w=1 h=1
73 /// // Same; the points do not need to be ordered
74 /// let r = URect::from_corners(UVec2::ONE, UVec2::ZERO); // w=1 h=1
75 /// ```
76 #[inline]
77 pub fn from_corners(p0: UVec2, p1: UVec2) -> Self {
78 Self {
79 min: p0.min(p1),
80 max: p0.max(p1),
81 }
82 }
83
84 /// Create a new rectangle from its center and size.
85 ///
86 /// # Rounding Behavior
87 ///
88 /// If the size contains odd numbers they will be rounded down to the nearest whole number.
89 ///
90 /// # Panics
91 ///
92 /// This method panics if any of the components of the size is negative or if `origin - (size / 2)` results in any negatives.
93 ///
94 /// # Examples
95 ///
96 /// ```
97 /// # use bevy_math::{URect, UVec2};
98 /// let r = URect::from_center_size(UVec2::ONE, UVec2::splat(2)); // w=2 h=2
99 /// assert_eq!(r.min, UVec2::splat(0));
100 /// assert_eq!(r.max, UVec2::splat(2));
101 /// ```
102 #[inline]
103 pub fn from_center_size(origin: UVec2, size: UVec2) -> Self {
104 assert!(origin.cmpge(size / 2).all(), "Origin must always be greater than or equal to (size / 2) otherwise the rectangle is undefined! Origin was {origin} and size was {size}");
105 let half_size = size / 2;
106 Self::from_center_half_size(origin, half_size)
107 }
108
109 /// Create a new rectangle from its center and half-size.
110 ///
111 /// # Panics
112 ///
113 /// This method panics if any of the components of the half-size is negative or if `origin - half_size` results in any negatives.
114 ///
115 /// # Examples
116 ///
117 /// ```
118 /// # use bevy_math::{URect, UVec2};
119 /// let r = URect::from_center_half_size(UVec2::ONE, UVec2::ONE); // w=2 h=2
120 /// assert_eq!(r.min, UVec2::splat(0));
121 /// assert_eq!(r.max, UVec2::splat(2));
122 /// ```
123 #[inline]
124 pub fn from_center_half_size(origin: UVec2, half_size: UVec2) -> Self {
125 assert!(origin.cmpge(half_size).all(), "Origin must always be greater than or equal to half_size otherwise the rectangle is undefined! Origin was {origin} and half_size was {half_size}");
126 Self {
127 min: origin - half_size,
128 max: origin + half_size,
129 }
130 }
131
132 /// Check if the rectangle is empty.
133 ///
134 /// # Examples
135 ///
136 /// ```
137 /// # use bevy_math::{URect, UVec2};
138 /// let r = URect::from_corners(UVec2::ZERO, UVec2::new(0, 1)); // w=0 h=1
139 /// assert!(r.is_empty());
140 /// ```
141 #[inline]
142 pub fn is_empty(&self) -> bool {
143 self.min.cmpge(self.max).any()
144 }
145
146 /// Rectangle width (max.x - min.x).
147 ///
148 /// # Examples
149 ///
150 /// ```
151 /// # use bevy_math::URect;
152 /// let r = URect::new(0, 0, 5, 1); // w=5 h=1
153 /// assert_eq!(r.width(), 5);
154 /// ```
155 #[inline]
156 pub const fn width(&self) -> u32 {
157 self.max.x - self.min.x
158 }
159
160 /// Rectangle height (max.y - min.y).
161 ///
162 /// # Examples
163 ///
164 /// ```
165 /// # use bevy_math::URect;
166 /// let r = URect::new(0, 0, 5, 1); // w=5 h=1
167 /// assert_eq!(r.height(), 1);
168 /// ```
169 #[inline]
170 pub const fn height(&self) -> u32 {
171 self.max.y - self.min.y
172 }
173
174 /// Rectangle size.
175 ///
176 /// # Examples
177 ///
178 /// ```
179 /// # use bevy_math::{URect, UVec2};
180 /// let r = URect::new(0, 0, 5, 1); // w=5 h=1
181 /// assert_eq!(r.size(), UVec2::new(5, 1));
182 /// ```
183 #[inline]
184 pub fn size(&self) -> UVec2 {
185 self.max - self.min
186 }
187
188 /// Rectangle half-size.
189 ///
190 /// # Rounding Behavior
191 ///
192 /// If the full size contains odd numbers they will be rounded down to the nearest whole number when calculating the half size.
193 ///
194 /// # Examples
195 ///
196 /// ```
197 /// # use bevy_math::{URect, UVec2};
198 /// let r = URect::new(0, 0, 4, 2); // w=4 h=2
199 /// assert_eq!(r.half_size(), UVec2::new(2, 1));
200 /// ```
201 #[inline]
202 pub fn half_size(&self) -> UVec2 {
203 self.size() / 2
204 }
205
206 /// The center point of the rectangle.
207 ///
208 /// # Rounding Behavior
209 ///
210 /// If the (min + max) contains odd numbers they will be rounded down to the nearest whole number when calculating the center.
211 ///
212 /// # Examples
213 ///
214 /// ```
215 /// # use bevy_math::{URect, UVec2};
216 /// let r = URect::new(0, 0, 4, 2); // w=4 h=2
217 /// assert_eq!(r.center(), UVec2::new(2, 1));
218 /// ```
219 #[inline]
220 pub fn center(&self) -> UVec2 {
221 (self.min + self.max) / 2
222 }
223
224 /// Check if a point lies within this rectangle, inclusive of its edges.
225 ///
226 /// # Examples
227 ///
228 /// ```
229 /// # use bevy_math::URect;
230 /// let r = URect::new(0, 0, 5, 1); // w=5 h=1
231 /// assert!(r.contains(r.center()));
232 /// assert!(r.contains(r.min));
233 /// assert!(r.contains(r.max));
234 /// ```
235 #[inline]
236 pub fn contains(&self, point: UVec2) -> bool {
237 (point.cmpge(self.min) & point.cmple(self.max)).all()
238 }
239
240 /// Build a new rectangle formed of the union of this rectangle and another rectangle.
241 ///
242 /// The union is the smallest rectangle enclosing both rectangles.
243 ///
244 /// # Examples
245 ///
246 /// ```
247 /// # use bevy_math::{URect, UVec2};
248 /// let r1 = URect::new(0, 0, 5, 1); // w=5 h=1
249 /// let r2 = URect::new(1, 0, 3, 8); // w=2 h=4
250 /// let r = r1.union(r2);
251 /// assert_eq!(r.min, UVec2::new(0, 0));
252 /// assert_eq!(r.max, UVec2::new(5, 8));
253 /// ```
254 #[inline]
255 pub fn union(&self, other: Self) -> Self {
256 Self {
257 min: self.min.min(other.min),
258 max: self.max.max(other.max),
259 }
260 }
261
262 /// Build a new rectangle formed of the union of this rectangle and a point.
263 ///
264 /// The union is the smallest rectangle enclosing both the rectangle and the point. If the
265 /// point is already inside the rectangle, this method returns a copy of the rectangle.
266 ///
267 /// # Examples
268 ///
269 /// ```
270 /// # use bevy_math::{URect, UVec2};
271 /// let r = URect::new(0, 0, 5, 1); // w=5 h=1
272 /// let u = r.union_point(UVec2::new(3, 6));
273 /// assert_eq!(u.min, UVec2::ZERO);
274 /// assert_eq!(u.max, UVec2::new(5, 6));
275 /// ```
276 #[inline]
277 pub fn union_point(&self, other: UVec2) -> Self {
278 Self {
279 min: self.min.min(other),
280 max: self.max.max(other),
281 }
282 }
283
284 /// Build a new rectangle formed of the intersection of this rectangle and another rectangle.
285 ///
286 /// The intersection is the largest rectangle enclosed in both rectangles. If the intersection
287 /// is empty, this method returns an empty rectangle ([`URect::is_empty()`] returns `true`), but
288 /// the actual values of [`URect::min`] and [`URect::max`] are implementation-dependent.
289 ///
290 /// # Examples
291 ///
292 /// ```
293 /// # use bevy_math::{URect, UVec2};
294 /// let r1 = URect::new(0, 0, 2, 2); // w=2 h=2
295 /// let r2 = URect::new(1, 1, 3, 3); // w=2 h=2
296 /// let r = r1.intersect(r2);
297 /// assert_eq!(r.min, UVec2::new(1, 1));
298 /// assert_eq!(r.max, UVec2::new(2, 2));
299 /// ```
300 #[inline]
301 pub fn intersect(&self, other: Self) -> Self {
302 let mut r = Self {
303 min: self.min.max(other.min),
304 max: self.max.min(other.max),
305 };
306 // Collapse min over max to enforce invariants and ensure e.g. width() or
307 // height() never return a negative value.
308 r.min = r.min.min(r.max);
309 r
310 }
311
312 /// Create a new rectangle by expanding it evenly on all sides.
313 ///
314 /// A positive expansion value produces a larger rectangle,
315 /// while a negative expansion value produces a smaller rectangle.
316 /// If this would result in zero width or height, [`URect::EMPTY`] is returned instead.
317 ///
318 /// # Examples
319 ///
320 /// ```
321 /// # use bevy_math::{URect, UVec2};
322 /// let r = URect::new(4, 4, 6, 6); // w=2 h=2
323 /// let r2 = r.inflate(1); // w=4 h=4
324 /// assert_eq!(r2.min, UVec2::splat(3));
325 /// assert_eq!(r2.max, UVec2::splat(7));
326 ///
327 /// let r = URect::new(4, 4, 8, 8); // w=4 h=4
328 /// let r2 = r.inflate(-1); // w=2 h=2
329 /// assert_eq!(r2.min, UVec2::splat(5));
330 /// assert_eq!(r2.max, UVec2::splat(7));
331 /// ```
332 #[inline]
333 pub fn inflate(&self, expansion: i32) -> Self {
334 let mut r = Self {
335 min: UVec2::new(
336 self.min.x.saturating_add_signed(-expansion),
337 self.min.y.saturating_add_signed(-expansion),
338 ),
339 max: UVec2::new(
340 self.max.x.saturating_add_signed(expansion),
341 self.max.y.saturating_add_signed(expansion),
342 ),
343 };
344 // Collapse min over max to enforce invariants and ensure e.g. width() or
345 // height() never return a negative value.
346 r.min = r.min.min(r.max);
347 r
348 }
349
350 /// Returns self as [`Rect`] (f32)
351 #[inline]
352 pub fn as_rect(&self) -> Rect {
353 Rect::from_corners(self.min.as_vec2(), self.max.as_vec2())
354 }
355
356 /// Returns self as [`IRect`] (i32)
357 #[inline]
358 pub fn as_irect(&self) -> IRect {
359 IRect::from_corners(self.min.as_ivec2(), self.max.as_ivec2())
360 }
361}
362
363#[cfg(test)]
364mod tests {
365 use super::*;
366
367 #[test]
368 fn well_formed() {
369 let r = URect::from_center_size(UVec2::new(10, 16), UVec2::new(8, 12));
370
371 assert_eq!(r.min, UVec2::new(6, 10));
372 assert_eq!(r.max, UVec2::new(14, 22));
373
374 assert_eq!(r.center(), UVec2::new(10, 16));
375
376 assert_eq!(r.width(), 8);
377 assert_eq!(r.height(), 12);
378 assert_eq!(r.size(), UVec2::new(8, 12));
379 assert_eq!(r.half_size(), UVec2::new(4, 6));
380
381 assert!(r.contains(UVec2::new(7, 10)));
382 assert!(r.contains(UVec2::new(14, 10)));
383 assert!(r.contains(UVec2::new(10, 22)));
384 assert!(r.contains(UVec2::new(6, 22)));
385 assert!(r.contains(UVec2::new(14, 22)));
386 assert!(!r.contains(UVec2::new(50, 5)));
387 }
388
389 #[test]
390 fn rect_union() {
391 let r = URect::from_center_size(UVec2::splat(4), UVec2::splat(4)); // [2, 2] - [6, 6]
392
393 // overlapping
394 let r2 = URect {
395 min: UVec2::new(0, 0),
396 max: UVec2::new(3, 3),
397 };
398 let u = r.union(r2);
399 assert_eq!(u.min, UVec2::new(0, 0));
400 assert_eq!(u.max, UVec2::new(6, 6));
401
402 // disjoint
403 let r2 = URect {
404 min: UVec2::new(4, 7),
405 max: UVec2::new(8, 8),
406 };
407 let u = r.union(r2);
408 assert_eq!(u.min, UVec2::new(2, 2));
409 assert_eq!(u.max, UVec2::new(8, 8));
410
411 // included
412 let r2 = URect::from_center_size(UVec2::splat(4), UVec2::splat(2));
413 let u = r.union(r2);
414 assert_eq!(u.min, r.min);
415 assert_eq!(u.max, r.max);
416
417 // including
418 let r2 = URect::from_center_size(UVec2::splat(4), UVec2::splat(6));
419 let u = r.union(r2);
420 assert_eq!(u.min, r2.min);
421 assert_eq!(u.min, r2.min);
422 }
423
424 #[test]
425 fn rect_union_pt() {
426 let r = URect::from_center_size(UVec2::splat(4), UVec2::splat(4)); // [2, 2] - [6, 6]
427
428 // inside
429 let v = UVec2::new(2, 5);
430 let u = r.union_point(v);
431 assert_eq!(u.min, r.min);
432 assert_eq!(u.max, r.max);
433
434 // outside
435 let v = UVec2::new(10, 5);
436 let u = r.union_point(v);
437 assert_eq!(u.min, UVec2::new(2, 2));
438 assert_eq!(u.max, UVec2::new(10, 6));
439 }
440
441 #[test]
442 fn rect_intersect() {
443 let r = URect::from_center_size(UVec2::splat(6), UVec2::splat(8)); // [2, 2] - [10, 10]
444
445 // overlapping
446 let r2 = URect {
447 min: UVec2::new(8, 8),
448 max: UVec2::new(12, 12),
449 };
450 let u = r.intersect(r2);
451 assert_eq!(u.min, UVec2::new(8, 8));
452 assert_eq!(u.max, UVec2::new(10, 10));
453
454 // disjoint
455 let r2 = URect {
456 min: UVec2::new(12, 12),
457 max: UVec2::new(14, 18),
458 };
459 let u = r.intersect(r2);
460 assert!(u.is_empty());
461 assert_eq!(u.width(), 0);
462
463 // included
464 let r2 = URect::from_center_size(UVec2::splat(6), UVec2::splat(2));
465 let u = r.intersect(r2);
466 assert_eq!(u.min, r2.min);
467 assert_eq!(u.max, r2.max);
468
469 // including
470 let r2 = URect::from_center_size(UVec2::splat(6), UVec2::splat(10));
471 let u = r.intersect(r2);
472 assert_eq!(u.min, r.min);
473 assert_eq!(u.max, r.max);
474 }
475
476 #[test]
477 fn rect_inflate() {
478 let r = URect::from_center_size(UVec2::splat(6), UVec2::splat(6)); // [3, 3] - [9, 9]
479
480 let r2 = r.inflate(2);
481 assert_eq!(r2.min, UVec2::new(1, 1));
482 assert_eq!(r2.max, UVec2::new(11, 11));
483 }
484}