Skip to main content

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 const 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 const fn from_corners(p0: Vec2, p1: Vec2) -> Self {
78        Self {
79            min: Vec2::new(p0.x.min(p1.x), p0.y.min(p1.y)),
80            max: Vec2::new(p0.x.max(p1.x), p0.y.max(p1.y)),
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 const fn from_center_size(origin: Vec2, size: Vec2) -> Self {
100        assert!(0. <= size.x && 0. <= size.y, "Rect size must be positive");
101        Self::from_center_half_size(origin, Vec2::new(0.5 * size.x, 0.5 * size.y))
102    }
103
104    /// Create a new rectangle from its center and half-size.
105    ///
106    /// # Panics
107    ///
108    /// This method panics if any of the components of the half-size is negative.
109    ///
110    /// # Examples
111    ///
112    /// ```
113    /// # use bevy_math::{Rect, Vec2};
114    /// let r = Rect::from_center_half_size(Vec2::ZERO, Vec2::ONE); // w=2 h=2
115    /// assert!(r.min.abs_diff_eq(Vec2::splat(-1.), 1e-5));
116    /// assert!(r.max.abs_diff_eq(Vec2::splat(1.), 1e-5));
117    /// ```
118    #[inline]
119    pub const fn from_center_half_size(origin: Vec2, half_size: Vec2) -> Self {
120        assert!(
121            0. <= half_size.x && 0. <= half_size.y,
122            "Rect half_size must be positive"
123        );
124        Self {
125            min: Vec2::new(origin.x - half_size.x, origin.y - half_size.y),
126            max: Vec2::new(origin.x + half_size.x, origin.y + half_size.y),
127        }
128    }
129
130    /// Check if the rectangle is empty.
131    ///
132    /// # Examples
133    ///
134    /// ```
135    /// # use bevy_math::{Rect, Vec2};
136    /// let r = Rect::from_corners(Vec2::ZERO, Vec2::new(0., 1.)); // w=0 h=1
137    /// assert!(r.is_empty());
138    /// ```
139    #[inline]
140    pub const fn is_empty(&self) -> bool {
141        self.min.x >= self.max.x || self.min.y >= self.max.y
142    }
143
144    /// Rectangle width (max.x - min.x).
145    ///
146    /// # Examples
147    ///
148    /// ```
149    /// # use bevy_math::Rect;
150    /// let r = Rect::new(0., 0., 5., 1.); // w=5 h=1
151    /// assert!((r.width() - 5.).abs() <= 1e-5);
152    /// ```
153    #[inline]
154    pub const fn width(&self) -> f32 {
155        self.max.x - self.min.x
156    }
157
158    /// Rectangle height (max.y - min.y).
159    ///
160    /// # Examples
161    ///
162    /// ```
163    /// # use bevy_math::Rect;
164    /// let r = Rect::new(0., 0., 5., 1.); // w=5 h=1
165    /// assert!((r.height() - 1.).abs() <= 1e-5);
166    /// ```
167    #[inline]
168    pub const fn height(&self) -> f32 {
169        self.max.y - self.min.y
170    }
171
172    /// Rectangle size.
173    ///
174    /// # Examples
175    ///
176    /// ```
177    /// # use bevy_math::{Rect, Vec2};
178    /// let r = Rect::new(0., 0., 5., 1.); // w=5 h=1
179    /// assert!(r.size().abs_diff_eq(Vec2::new(5., 1.), 1e-5));
180    /// ```
181    #[inline]
182    pub const fn size(&self) -> Vec2 {
183        Vec2::new(self.max.x - self.min.x, self.max.y - self.min.y)
184    }
185
186    /// Rectangle half-size.
187    ///
188    /// # Examples
189    ///
190    /// ```
191    /// # use bevy_math::{Rect, Vec2};
192    /// let r = Rect::new(0., 0., 5., 1.); // w=5 h=1
193    /// assert!(r.half_size().abs_diff_eq(Vec2::new(2.5, 0.5), 1e-5));
194    /// ```
195    #[inline]
196    pub const fn half_size(&self) -> Vec2 {
197        let size = self.size();
198        Vec2::new(0.5 * size.x, 0.5 * size.y)
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 const fn center(&self) -> Vec2 {
212        Vec2::new(
213            0.5 * (self.min.x + self.max.x),
214            0.5 * (self.min.y + self.max.y),
215        )
216    }
217
218    /// Returns the rectangle translated by the given offset.
219    ///
220    /// # Examples
221    ///
222    /// ```
223    /// # use bevy_math::{Rect, Vec2};
224    /// let r = Rect::new(0., 0., 5., 1.); // w=5 h=1
225    /// let r2 = r.translate(Vec2::new(2., -3.));
226    /// assert!(r2.min.abs_diff_eq(Vec2::new(2., -3.), 1e-5));
227    /// assert!(r2.max.abs_diff_eq(Vec2::new(7., -2.), 1e-5));
228    /// ```
229    #[inline]
230    pub const fn translate(&self, offset: Vec2) -> Self {
231        Self {
232            min: Vec2::new(self.min.x + offset.x, self.min.y + offset.y),
233            max: Vec2::new(self.max.x + offset.x, self.max.y + offset.y),
234        }
235    }
236
237    /// Check if a point lies within this rectangle, inclusive of its edges.
238    ///
239    /// # Examples
240    ///
241    /// ```
242    /// # use bevy_math::Rect;
243    /// let r = Rect::new(0., 0., 5., 1.); // w=5 h=1
244    /// assert!(r.contains(r.center()));
245    /// assert!(r.contains(r.min));
246    /// assert!(r.contains(r.max));
247    /// ```
248    #[inline]
249    pub const fn contains(&self, point: Vec2) -> bool {
250        self.min.x <= point.x
251            && point.x <= self.max.x
252            && self.min.y <= point.y
253            && point.y <= self.max.y
254    }
255
256    /// Build a new rectangle formed of the union of this rectangle and another rectangle.
257    ///
258    /// The union is the smallest rectangle enclosing both rectangles.
259    ///
260    /// # Examples
261    ///
262    /// ```
263    /// # use bevy_math::{Rect, Vec2};
264    /// let r1 = Rect::new(0., 0., 5., 1.); // w=5 h=1
265    /// let r2 = Rect::new(1., -1., 3., 3.); // w=2 h=4
266    /// let r = r1.union(r2);
267    /// assert!(r.min.abs_diff_eq(Vec2::new(0., -1.), 1e-5));
268    /// assert!(r.max.abs_diff_eq(Vec2::new(5., 3.), 1e-5));
269    /// ```
270    #[inline]
271    pub const fn union(&self, other: Self) -> Self {
272        let min_x = self.min.x.min(other.min.x);
273        let min_y = self.min.y.min(other.min.y);
274        let max_x = self.max.x.max(other.max.x);
275        let max_y = self.max.y.max(other.max.y);
276
277        Self {
278            min: Vec2::new(min_x, min_y),
279            max: Vec2::new(max_x, max_y),
280        }
281    }
282
283    /// Build a new rectangle formed of the union of this rectangle and a point.
284    ///
285    /// The union is the smallest rectangle enclosing both the rectangle and the point. If the
286    /// point is already inside the rectangle, this method returns a copy of the rectangle.
287    ///
288    /// # Examples
289    ///
290    /// ```
291    /// # use bevy_math::{Rect, Vec2};
292    /// let r = Rect::new(0., 0., 5., 1.); // w=5 h=1
293    /// let u = r.union_point(Vec2::new(3., 6.));
294    /// assert!(u.min.abs_diff_eq(Vec2::ZERO, 1e-5));
295    /// assert!(u.max.abs_diff_eq(Vec2::new(5., 6.), 1e-5));
296    /// ```
297    #[inline]
298    pub const fn union_point(&self, other: Vec2) -> Self {
299        let min_x = self.min.x.min(other.x);
300        let min_y = self.min.y.min(other.y);
301        let max_x = self.max.x.max(other.x);
302        let max_y = self.max.y.max(other.y);
303
304        Self {
305            min: Vec2::new(min_x, min_y),
306            max: Vec2::new(max_x, max_y),
307        }
308    }
309
310    /// Build a new rectangle formed of the intersection of this rectangle and another rectangle.
311    ///
312    /// The intersection is the largest rectangle enclosed in both rectangles. If the intersection
313    /// is empty, this method returns an empty rectangle ([`Rect::is_empty()`] returns `true`), but
314    /// the actual values of [`Rect::min`] and [`Rect::max`] are implementation-dependent.
315    ///
316    /// # Examples
317    ///
318    /// ```
319    /// # use bevy_math::{Rect, Vec2};
320    /// let r1 = Rect::new(0., 0., 5., 1.); // w=5 h=1
321    /// let r2 = Rect::new(1., -1., 3., 3.); // w=2 h=4
322    /// let r = r1.intersect(r2);
323    /// assert!(r.min.abs_diff_eq(Vec2::new(1., 0.), 1e-5));
324    /// assert!(r.max.abs_diff_eq(Vec2::new(3., 1.), 1e-5));
325    /// ```
326    #[inline]
327    pub fn intersect(&self, other: Self) -> Self {
328        let min_x = self.min.x.max(other.min.x);
329        let min_y = self.min.y.max(other.min.y);
330        let max_x = self.max.x.min(other.max.x);
331        let max_y = self.max.y.min(other.max.y);
332        // Collapse min over max to enforce invariants and ensure e.g. width() or
333        // height() never return a negative value.
334        let collapsed_min_x = min_x.min(max_x);
335        let collapsed_min_y = min_y.min(max_y);
336        Self {
337            min: Vec2::new(collapsed_min_x, collapsed_min_y),
338            max: Vec2::new(max_x, max_y),
339        }
340    }
341
342    /// Create a new rectangle by expanding it evenly on all sides.
343    ///
344    /// A positive expansion value produces a larger rectangle,
345    /// while a negative expansion value produces a smaller rectangle.
346    /// If this would result in zero or negative width or height, [`Rect::EMPTY`] is returned instead.
347    ///
348    /// # Examples
349    ///
350    /// ```
351    /// # use bevy_math::{Rect, Vec2};
352    /// let r = Rect::new(0., 0., 5., 1.); // w=5 h=1
353    /// let r2 = r.inflate(3.); // w=11 h=7
354    /// assert!(r2.min.abs_diff_eq(Vec2::splat(-3.), 1e-5));
355    /// assert!(r2.max.abs_diff_eq(Vec2::new(8., 4.), 1e-5));
356    ///
357    /// let r = Rect::new(0., -1., 6., 7.); // w=6 h=8
358    /// let r2 = r.inflate(-2.); // w=11 h=7
359    /// assert!(r2.min.abs_diff_eq(Vec2::new(2., 1.), 1e-5));
360    /// assert!(r2.max.abs_diff_eq(Vec2::new(4., 5.), 1e-5));
361    /// ```
362    #[inline]
363    pub const fn inflate(&self, expansion: f32) -> Self {
364        let min_x = self.min.x - expansion;
365        let min_y = self.min.y - expansion;
366        let max_x = self.max.x + expansion;
367        let max_y = self.max.y + expansion;
368        // Collapse min over max to enforce invariants and ensure e.g. width() or
369        // height() never return a negative value.
370        let collapsed_min_x = min_x.min(max_x);
371        let collapsed_min_y = min_y.min(max_y);
372        Self {
373            min: Vec2::new(collapsed_min_x, collapsed_min_y),
374            max: Vec2::new(max_x, max_y),
375        }
376    }
377
378    /// Build a new rectangle from this one with its coordinates expressed
379    /// relative to `other` in a normalized ([0..1] x [0..1]) coordinate system.
380    ///
381    /// # Examples
382    ///
383    /// ```
384    /// # use bevy_math::{Rect, Vec2};
385    /// let r = Rect::new(2., 3., 4., 6.);
386    /// let s = Rect::new(0., 0., 10., 10.);
387    /// let n = r.normalize(s);
388    ///
389    /// assert_eq!(n.min.x, 0.2);
390    /// assert_eq!(n.min.y, 0.3);
391    /// assert_eq!(n.max.x, 0.4);
392    /// assert_eq!(n.max.y, 0.6);
393    /// ```
394    pub const fn normalize(&self, other: Self) -> Self {
395        let outer_size = other.size();
396        let min_x = (self.min.x - other.min.x) / outer_size.x;
397        let min_y = (self.min.y - other.min.y) / outer_size.y;
398        let max_x = (self.max.x - other.min.x) / outer_size.x;
399        let max_y = (self.max.y - other.min.y) / outer_size.y;
400
401        Self {
402            min: Vec2::new(min_x, min_y),
403            max: Vec2::new(max_x, max_y),
404        }
405    }
406
407    /// Return the area of this rectangle.
408    ///
409    /// # Examples
410    ///
411    /// ```
412    /// # use bevy_math::Rect;
413    /// let r = Rect::new(0., 0., 10., 10.); // w=10 h=10
414    /// assert_eq!(r.area(), 100.0);
415    /// ```
416    #[inline]
417    pub const fn area(&self) -> f32 {
418        self.width() * self.height()
419    }
420
421    /// Returns self as [`IRect`] (i32)
422    #[inline]
423    pub fn as_irect(&self) -> IRect {
424        IRect::from_corners(self.min.as_ivec2(), self.max.as_ivec2())
425    }
426
427    /// Returns self as [`URect`] (u32)
428    #[inline]
429    pub fn as_urect(&self) -> URect {
430        URect::from_corners(self.min.as_uvec2(), self.max.as_uvec2())
431    }
432}
433
434#[cfg(test)]
435mod tests {
436    use crate::ops;
437
438    use super::*;
439
440    #[test]
441    fn well_formed() {
442        let r = Rect::from_center_size(Vec2::new(3., -5.), Vec2::new(8., 11.));
443
444        assert!(r.min.abs_diff_eq(Vec2::new(-1., -10.5), 1e-5));
445        assert!(r.max.abs_diff_eq(Vec2::new(7., 0.5), 1e-5));
446
447        assert!(r.center().abs_diff_eq(Vec2::new(3., -5.), 1e-5));
448
449        assert!(ops::abs(r.width() - 8.) <= 1e-5);
450        assert!(ops::abs(r.height() - 11.) <= 1e-5);
451        assert!(r.size().abs_diff_eq(Vec2::new(8., 11.), 1e-5));
452        assert!(r.half_size().abs_diff_eq(Vec2::new(4., 5.5), 1e-5));
453
454        assert!(r.contains(Vec2::new(3., -5.)));
455        assert!(r.contains(Vec2::new(-1., -10.5)));
456        assert!(r.contains(Vec2::new(-1., 0.5)));
457        assert!(r.contains(Vec2::new(7., -10.5)));
458        assert!(r.contains(Vec2::new(7., 0.5)));
459        assert!(!r.contains(Vec2::new(50., -5.)));
460    }
461
462    #[test]
463    fn rect_union() {
464        let r = Rect::from_center_size(Vec2::ZERO, Vec2::ONE); // [-0.5,-0.5] - [0.5,0.5]
465
466        // overlapping
467        let r2 = Rect {
468            min: Vec2::new(-0.8, 0.3),
469            max: Vec2::new(0.1, 0.7),
470        };
471        let u = r.union(r2);
472        assert!(u.min.abs_diff_eq(Vec2::new(-0.8, -0.5), 1e-5));
473        assert!(u.max.abs_diff_eq(Vec2::new(0.5, 0.7), 1e-5));
474
475        // disjoint
476        let r2 = Rect {
477            min: Vec2::new(-1.8, -0.5),
478            max: Vec2::new(-1.5, 0.3),
479        };
480        let u = r.union(r2);
481        assert!(u.min.abs_diff_eq(Vec2::new(-1.8, -0.5), 1e-5));
482        assert!(u.max.abs_diff_eq(Vec2::new(0.5, 0.5), 1e-5));
483
484        // included
485        let r2 = Rect::from_center_size(Vec2::ZERO, Vec2::splat(0.5));
486        let u = r.union(r2);
487        assert!(u.min.abs_diff_eq(r.min, 1e-5));
488        assert!(u.max.abs_diff_eq(r.max, 1e-5));
489
490        // including
491        let r2 = Rect::from_center_size(Vec2::ZERO, Vec2::splat(1.5));
492        let u = r.union(r2);
493        assert!(u.min.abs_diff_eq(r2.min, 1e-5));
494        assert!(u.max.abs_diff_eq(r2.max, 1e-5));
495    }
496
497    #[test]
498    fn rect_union_pt() {
499        let r = Rect::from_center_size(Vec2::ZERO, Vec2::ONE); // [-0.5,-0.5] - [0.5,0.5]
500
501        // inside
502        let v = Vec2::new(0.3, -0.2);
503        let u = r.union_point(v);
504        assert!(u.min.abs_diff_eq(r.min, 1e-5));
505        assert!(u.max.abs_diff_eq(r.max, 1e-5));
506
507        // outside
508        let v = Vec2::new(10., -3.);
509        let u = r.union_point(v);
510        assert!(u.min.abs_diff_eq(Vec2::new(-0.5, -3.), 1e-5));
511        assert!(u.max.abs_diff_eq(Vec2::new(10., 0.5), 1e-5));
512    }
513
514    #[test]
515    fn rect_intersect() {
516        let r = Rect::from_center_size(Vec2::ZERO, Vec2::ONE); // [-0.5,-0.5] - [0.5,0.5]
517
518        // overlapping
519        let r2 = Rect {
520            min: Vec2::new(-0.8, 0.3),
521            max: Vec2::new(0.1, 0.7),
522        };
523        let u = r.intersect(r2);
524        assert!(u.min.abs_diff_eq(Vec2::new(-0.5, 0.3), 1e-5));
525        assert!(u.max.abs_diff_eq(Vec2::new(0.1, 0.5), 1e-5));
526
527        // disjoint
528        let r2 = Rect {
529            min: Vec2::new(-1.8, -0.5),
530            max: Vec2::new(-1.5, 0.3),
531        };
532        let u = r.intersect(r2);
533        assert!(u.is_empty());
534        assert!(u.width() <= 1e-5);
535
536        // included
537        let r2 = Rect::from_center_size(Vec2::ZERO, Vec2::splat(0.5));
538        let u = r.intersect(r2);
539        assert!(u.min.abs_diff_eq(r2.min, 1e-5));
540        assert!(u.max.abs_diff_eq(r2.max, 1e-5));
541
542        // including
543        let r2 = Rect::from_center_size(Vec2::ZERO, Vec2::splat(1.5));
544        let u = r.intersect(r2);
545        assert!(u.min.abs_diff_eq(r.min, 1e-5));
546        assert!(u.max.abs_diff_eq(r.max, 1e-5));
547    }
548
549    #[test]
550    fn rect_inflate() {
551        let r = Rect::from_center_size(Vec2::ZERO, Vec2::ONE); // [-0.5,-0.5] - [0.5,0.5]
552
553        let r2 = r.inflate(0.3);
554        assert!(r2.min.abs_diff_eq(Vec2::new(-0.8, -0.8), 1e-5));
555        assert!(r2.max.abs_diff_eq(Vec2::new(0.8, 0.8), 1e-5));
556    }
557
558    #[test]
559    fn rect_translate() {
560        let r = Rect::new(0., 1., 4., 3.);
561        let r2 = r.translate(Vec2::new(2., -5.));
562
563        assert!(r2.min.abs_diff_eq(Vec2::new(2., -4.), 1e-5));
564        assert!(r2.max.abs_diff_eq(Vec2::new(6., -2.), 1e-5));
565        assert!(r2.size().abs_diff_eq(r.size(), 1e-5));
566    }
567}