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}