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