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}