bevy_math/rects/
rect.rs

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