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