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, Clone)
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 crate::ops;
375
376    use super::*;
377
378    #[test]
379    fn well_formed() {
380        let r = Rect::from_center_size(Vec2::new(3., -5.), Vec2::new(8., 11.));
381
382        assert!(r.min.abs_diff_eq(Vec2::new(-1., -10.5), 1e-5));
383        assert!(r.max.abs_diff_eq(Vec2::new(7., 0.5), 1e-5));
384
385        assert!(r.center().abs_diff_eq(Vec2::new(3., -5.), 1e-5));
386
387        assert!(ops::abs(r.width() - 8.) <= 1e-5);
388        assert!(ops::abs(r.height() - 11.) <= 1e-5);
389        assert!(r.size().abs_diff_eq(Vec2::new(8., 11.), 1e-5));
390        assert!(r.half_size().abs_diff_eq(Vec2::new(4., 5.5), 1e-5));
391
392        assert!(r.contains(Vec2::new(3., -5.)));
393        assert!(r.contains(Vec2::new(-1., -10.5)));
394        assert!(r.contains(Vec2::new(-1., 0.5)));
395        assert!(r.contains(Vec2::new(7., -10.5)));
396        assert!(r.contains(Vec2::new(7., 0.5)));
397        assert!(!r.contains(Vec2::new(50., -5.)));
398    }
399
400    #[test]
401    fn rect_union() {
402        let r = Rect::from_center_size(Vec2::ZERO, Vec2::ONE); // [-0.5,-0.5] - [0.5,0.5]
403
404        // overlapping
405        let r2 = Rect {
406            min: Vec2::new(-0.8, 0.3),
407            max: Vec2::new(0.1, 0.7),
408        };
409        let u = r.union(r2);
410        assert!(u.min.abs_diff_eq(Vec2::new(-0.8, -0.5), 1e-5));
411        assert!(u.max.abs_diff_eq(Vec2::new(0.5, 0.7), 1e-5));
412
413        // disjoint
414        let r2 = Rect {
415            min: Vec2::new(-1.8, -0.5),
416            max: Vec2::new(-1.5, 0.3),
417        };
418        let u = r.union(r2);
419        assert!(u.min.abs_diff_eq(Vec2::new(-1.8, -0.5), 1e-5));
420        assert!(u.max.abs_diff_eq(Vec2::new(0.5, 0.5), 1e-5));
421
422        // included
423        let r2 = Rect::from_center_size(Vec2::ZERO, Vec2::splat(0.5));
424        let u = r.union(r2);
425        assert!(u.min.abs_diff_eq(r.min, 1e-5));
426        assert!(u.max.abs_diff_eq(r.max, 1e-5));
427
428        // including
429        let r2 = Rect::from_center_size(Vec2::ZERO, Vec2::splat(1.5));
430        let u = r.union(r2);
431        assert!(u.min.abs_diff_eq(r2.min, 1e-5));
432        assert!(u.max.abs_diff_eq(r2.max, 1e-5));
433    }
434
435    #[test]
436    fn rect_union_pt() {
437        let r = Rect::from_center_size(Vec2::ZERO, Vec2::ONE); // [-0.5,-0.5] - [0.5,0.5]
438
439        // inside
440        let v = Vec2::new(0.3, -0.2);
441        let u = r.union_point(v);
442        assert!(u.min.abs_diff_eq(r.min, 1e-5));
443        assert!(u.max.abs_diff_eq(r.max, 1e-5));
444
445        // outside
446        let v = Vec2::new(10., -3.);
447        let u = r.union_point(v);
448        assert!(u.min.abs_diff_eq(Vec2::new(-0.5, -3.), 1e-5));
449        assert!(u.max.abs_diff_eq(Vec2::new(10., 0.5), 1e-5));
450    }
451
452    #[test]
453    fn rect_intersect() {
454        let r = Rect::from_center_size(Vec2::ZERO, Vec2::ONE); // [-0.5,-0.5] - [0.5,0.5]
455
456        // overlapping
457        let r2 = Rect {
458            min: Vec2::new(-0.8, 0.3),
459            max: Vec2::new(0.1, 0.7),
460        };
461        let u = r.intersect(r2);
462        assert!(u.min.abs_diff_eq(Vec2::new(-0.5, 0.3), 1e-5));
463        assert!(u.max.abs_diff_eq(Vec2::new(0.1, 0.5), 1e-5));
464
465        // disjoint
466        let r2 = Rect {
467            min: Vec2::new(-1.8, -0.5),
468            max: Vec2::new(-1.5, 0.3),
469        };
470        let u = r.intersect(r2);
471        assert!(u.is_empty());
472        assert!(u.width() <= 1e-5);
473
474        // included
475        let r2 = Rect::from_center_size(Vec2::ZERO, Vec2::splat(0.5));
476        let u = r.intersect(r2);
477        assert!(u.min.abs_diff_eq(r2.min, 1e-5));
478        assert!(u.max.abs_diff_eq(r2.max, 1e-5));
479
480        // including
481        let r2 = Rect::from_center_size(Vec2::ZERO, Vec2::splat(1.5));
482        let u = r.intersect(r2);
483        assert!(u.min.abs_diff_eq(r.min, 1e-5));
484        assert!(u.max.abs_diff_eq(r.max, 1e-5));
485    }
486
487    #[test]
488    fn rect_inflate() {
489        let r = Rect::from_center_size(Vec2::ZERO, Vec2::ONE); // [-0.5,-0.5] - [0.5,0.5]
490
491        let r2 = r.inflate(0.3);
492        assert!(r2.min.abs_diff_eq(Vec2::new(-0.8, -0.8), 1e-5));
493        assert!(r2.max.abs_diff_eq(Vec2::new(0.8, 0.8), 1e-5));
494    }
495}