Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_math/src/rects/rect.rs
6596 views
1
use crate::{IRect, URect, Vec2};
2
3
#[cfg(feature = "bevy_reflect")]
4
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
5
#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
6
use 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
)]
28
pub 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
35
impl 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)]
373
mod 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
}
496
497