mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 15:14:50 +00:00
Add integer equivalents for Rect
(#7984)
## Objective Add integer equivalents for the `Rect` type. Closes #7967 ## Solution - Add `IRect` and `URect` ## Changelog Added `IRect` and `URect` types.
This commit is contained in:
parent
6fc619d34a
commit
942766c485
5 changed files with 960 additions and 14 deletions
|
@ -9,10 +9,10 @@
|
||||||
|
|
||||||
pub mod cubic_splines;
|
pub mod cubic_splines;
|
||||||
mod ray;
|
mod ray;
|
||||||
mod rect;
|
mod rects;
|
||||||
|
|
||||||
pub use ray::Ray;
|
pub use ray::Ray;
|
||||||
pub use rect::Rect;
|
pub use rects::*;
|
||||||
|
|
||||||
/// The `bevy_math` prelude.
|
/// The `bevy_math` prelude.
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
|
|
458
crates/bevy_math/src/rects/irect.rs
Normal file
458
crates/bevy_math/src/rects/irect.rs
Normal file
|
@ -0,0 +1,458 @@
|
||||||
|
use crate::{IVec2, Rect, URect};
|
||||||
|
|
||||||
|
/// A rectangle defined by two opposite corners.
|
||||||
|
///
|
||||||
|
/// The rectangle is axis aligned, and defined by its minimum and maximum coordinates,
|
||||||
|
/// stored in `IRect::min` and `IRect::max`, respectively. The minimum/maximum invariant
|
||||||
|
/// must be upheld by the user when directly assigning the fields, otherwise some methods
|
||||||
|
/// produce invalid results. It is generally recommended to use one of the constructor
|
||||||
|
/// methods instead, which will ensure this invariant is met, unless you already have
|
||||||
|
/// the minimum and maximum corners.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default, Clone, Copy, Debug, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
pub struct IRect {
|
||||||
|
/// The minimum corner point of the rect.
|
||||||
|
pub min: IVec2,
|
||||||
|
/// The maximum corner point of the rect.
|
||||||
|
pub max: IVec2,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IRect {
|
||||||
|
/// Create a new rectangle from two corner points.
|
||||||
|
///
|
||||||
|
/// The two points do not need to be the minimum and/or maximum corners.
|
||||||
|
/// They only need to be two opposite corners.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::IRect;
|
||||||
|
/// let r = IRect::new(0, 4, 10, 6); // w=10 h=2
|
||||||
|
/// let r = IRect::new(2, 3, 5, -1); // w=3 h=4
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn new(x0: i32, y0: i32, x1: i32, y1: i32) -> Self {
|
||||||
|
Self::from_corners(IVec2::new(x0, y0), IVec2::new(x1, y1))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new rectangle from two corner points.
|
||||||
|
///
|
||||||
|
/// The two points do not need to be the minimum and/or maximum corners.
|
||||||
|
/// They only need to be two opposite corners.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::{IRect, IVec2};
|
||||||
|
/// // Unit rect from [0,0] to [1,1]
|
||||||
|
/// let r = IRect::from_corners(IVec2::ZERO, IVec2::ONE); // w=1 h=1
|
||||||
|
/// // Same; the points do not need to be ordered
|
||||||
|
/// let r = IRect::from_corners(IVec2::ONE, IVec2::ZERO); // w=1 h=1
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn from_corners(p0: IVec2, p1: IVec2) -> Self {
|
||||||
|
Self {
|
||||||
|
min: p0.min(p1),
|
||||||
|
max: p0.max(p1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new rectangle from its center and size.
|
||||||
|
///
|
||||||
|
/// # Rounding Behaviour
|
||||||
|
///
|
||||||
|
/// If the size contains odd numbers they will be rounded down to the nearest whole number.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// This method panics if any of the components of the size is negative.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::{IRect, IVec2};
|
||||||
|
/// let r = IRect::from_center_size(IVec2::ZERO, IVec2::new(3, 2)); // w=2 h=2
|
||||||
|
/// assert_eq!(r.min, IVec2::splat(-1));
|
||||||
|
/// assert_eq!(r.max, IVec2::splat(1));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn from_center_size(origin: IVec2, size: IVec2) -> Self {
|
||||||
|
debug_assert!(size.cmpge(IVec2::ZERO).all(), "IRect size must be positive");
|
||||||
|
let half_size = size / 2;
|
||||||
|
Self::from_center_half_size(origin, half_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new rectangle from its center and half-size.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// This method panics if any of the components of the half-size is negative.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::{IRect, IVec2};
|
||||||
|
/// let r = IRect::from_center_half_size(IVec2::ZERO, IVec2::ONE); // w=2 h=2
|
||||||
|
/// assert_eq!(r.min, IVec2::splat(-1));
|
||||||
|
/// assert_eq!(r.max, IVec2::splat(1));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn from_center_half_size(origin: IVec2, half_size: IVec2) -> Self {
|
||||||
|
assert!(
|
||||||
|
half_size.cmpge(IVec2::ZERO).all(),
|
||||||
|
"IRect half_size must be positive"
|
||||||
|
);
|
||||||
|
Self {
|
||||||
|
min: origin - half_size,
|
||||||
|
max: origin + half_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the rectangle is empty.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::{IRect, IVec2};
|
||||||
|
/// let r = IRect::from_corners(IVec2::ZERO, IVec2::new(0, 1)); // w=0 h=1
|
||||||
|
/// assert!(r.is_empty());
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.min.cmpge(self.max).any()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rectangle width (max.x - min.x).
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::IRect;
|
||||||
|
/// let r = IRect::new(0, 0, 5, 1); // w=5 h=1
|
||||||
|
/// assert_eq!(r.width(), 5);
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn width(&self) -> i32 {
|
||||||
|
self.max.x - self.min.x
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rectangle height (max.y - min.y).
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::IRect;
|
||||||
|
/// let r = IRect::new(0, 0, 5, 1); // w=5 h=1
|
||||||
|
/// assert_eq!(r.height(), 1);
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn height(&self) -> i32 {
|
||||||
|
self.max.y - self.min.y
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rectangle size.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::{IRect, IVec2};
|
||||||
|
/// let r = IRect::new(0, 0, 5, 1); // w=5 h=1
|
||||||
|
/// assert_eq!(r.size(), IVec2::new(5, 1));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn size(&self) -> IVec2 {
|
||||||
|
self.max - self.min
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rectangle half-size.
|
||||||
|
///
|
||||||
|
/// # Rounding Behaviour
|
||||||
|
///
|
||||||
|
/// If the full size contains odd numbers they will be rounded down to the nearest whole number when calculating the half size.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::{IRect, IVec2};
|
||||||
|
/// let r = IRect::new(0, 0, 4, 3); // w=4 h=3
|
||||||
|
/// assert_eq!(r.half_size(), IVec2::new(2, 1));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn half_size(&self) -> IVec2 {
|
||||||
|
self.size() / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The center point of the rectangle.
|
||||||
|
///
|
||||||
|
/// # Rounding Behaviour
|
||||||
|
///
|
||||||
|
/// If the (min + max) contains odd numbers they will be rounded down to the nearest whole number when calculating the center.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::{IRect, IVec2};
|
||||||
|
/// let r = IRect::new(0, 0, 5, 2); // w=5 h=2
|
||||||
|
/// assert_eq!(r.center(), IVec2::new(2, 1));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn center(&self) -> IVec2 {
|
||||||
|
(self.min + self.max) / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a point lies within this rectangle, inclusive of its edges.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::IRect;
|
||||||
|
/// let r = IRect::new(0, 0, 5, 1); // w=5 h=1
|
||||||
|
/// assert!(r.contains(r.center()));
|
||||||
|
/// assert!(r.contains(r.min));
|
||||||
|
/// assert!(r.contains(r.max));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn contains(&self, point: IVec2) -> bool {
|
||||||
|
(point.cmpge(self.min) & point.cmple(self.max)).all()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build a new rectangle formed of the union of this rectangle and another rectangle.
|
||||||
|
///
|
||||||
|
/// The union is the smallest rectangle enclosing both rectangles.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::{IRect, IVec2};
|
||||||
|
/// let r1 = IRect::new(0, 0, 5, 1); // w=5 h=1
|
||||||
|
/// let r2 = IRect::new(1, -1, 3, 3); // w=2 h=4
|
||||||
|
/// let r = r1.union(r2);
|
||||||
|
/// assert_eq!(r.min, IVec2::new(0, -1));
|
||||||
|
/// assert_eq!(r.max, IVec2::new(5, 3));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn union(&self, other: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
min: self.min.min(other.min),
|
||||||
|
max: self.max.max(other.max),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build a new rectangle formed of the union of this rectangle and a point.
|
||||||
|
///
|
||||||
|
/// The union is the smallest rectangle enclosing both the rectangle and the point. If the
|
||||||
|
/// point is already inside the rectangle, this method returns a copy of the rectangle.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::{IRect, IVec2};
|
||||||
|
/// let r = IRect::new(0, 0, 5, 1); // w=5 h=1
|
||||||
|
/// let u = r.union_point(IVec2::new(3, 6));
|
||||||
|
/// assert_eq!(u.min, IVec2::ZERO);
|
||||||
|
/// assert_eq!(u.max, IVec2::new(5, 6));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn union_point(&self, other: IVec2) -> Self {
|
||||||
|
Self {
|
||||||
|
min: self.min.min(other),
|
||||||
|
max: self.max.max(other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build a new rectangle formed of the intersection of this rectangle and another rectangle.
|
||||||
|
///
|
||||||
|
/// The intersection is the largest rectangle enclosed in both rectangles. If the intersection
|
||||||
|
/// is empty, this method returns an empty rectangle ([`IRect::is_empty()`] returns `true`), but
|
||||||
|
/// the actual values of [`IRect::min`] and [`IRect::max`] are implementation-dependent.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::{IRect, IVec2};
|
||||||
|
/// let r1 = IRect::new(0, 0, 5, 1); // w=5 h=1
|
||||||
|
/// let r2 = IRect::new(1, -1, 3, 3); // w=2 h=4
|
||||||
|
/// let r = r1.intersect(r2);
|
||||||
|
/// assert_eq!(r.min, IVec2::new(1, 0));
|
||||||
|
/// assert_eq!(r.max, IVec2::new(3, 1));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn intersect(&self, other: Self) -> Self {
|
||||||
|
let mut r = Self {
|
||||||
|
min: self.min.max(other.min),
|
||||||
|
max: self.max.min(other.max),
|
||||||
|
};
|
||||||
|
// Collapse min over max to enforce invariants and ensure e.g. width() or
|
||||||
|
// height() never return a negative value.
|
||||||
|
r.min = r.min.min(r.max);
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new rectangle with a constant inset.
|
||||||
|
///
|
||||||
|
/// The inset is the extra border on all sides. A positive inset produces a larger rectangle,
|
||||||
|
/// while a negative inset is allowed and produces a smaller rectangle. If the inset is negative
|
||||||
|
/// and its absolute value is larger than the rectangle half-size, the created rectangle is empty.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::{IRect, IVec2};
|
||||||
|
/// let r = IRect::new(0, 0, 5, 1); // w=5 h=1
|
||||||
|
/// let r2 = r.inset(3); // w=11 h=7
|
||||||
|
/// assert_eq!(r2.min, IVec2::splat(-3));
|
||||||
|
/// assert_eq!(r2.max, IVec2::new(8, 4));
|
||||||
|
///
|
||||||
|
/// let r = IRect::new(0, -1, 4, 3); // w=4 h=4
|
||||||
|
/// let r2 = r.inset(-1); // w=2 h=2
|
||||||
|
/// assert_eq!(r2.min, IVec2::new(1, 0));
|
||||||
|
/// assert_eq!(r2.max, IVec2::new(3, 2));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn inset(&self, inset: i32) -> Self {
|
||||||
|
let mut r = Self {
|
||||||
|
min: self.min - inset,
|
||||||
|
max: self.max + inset,
|
||||||
|
};
|
||||||
|
// Collapse min over max to enforce invariants and ensure e.g. width() or
|
||||||
|
// height() never return a negative value.
|
||||||
|
r.min = r.min.min(r.max);
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns self as [`Rect`] (f32)
|
||||||
|
#[inline]
|
||||||
|
pub fn as_rect(&self) -> Rect {
|
||||||
|
Rect::from_corners(self.min.as_vec2(), self.max.as_vec2())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns self as [`URect`] (u32)
|
||||||
|
#[inline]
|
||||||
|
pub fn as_urect(&self) -> URect {
|
||||||
|
URect::from_corners(self.min.as_uvec2(), self.max.as_uvec2())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn well_formed() {
|
||||||
|
let r = IRect::from_center_size(IVec2::new(3, -5), IVec2::new(8, 12));
|
||||||
|
|
||||||
|
assert_eq!(r.min, IVec2::new(-1, -11));
|
||||||
|
assert_eq!(r.max, IVec2::new(7, 1));
|
||||||
|
|
||||||
|
assert_eq!(r.center(), IVec2::new(3, -5));
|
||||||
|
|
||||||
|
assert_eq!(r.width().abs(), 8);
|
||||||
|
assert_eq!(r.height().abs(), 12);
|
||||||
|
assert_eq!(r.size(), IVec2::new(8, 12));
|
||||||
|
assert_eq!(r.half_size(), IVec2::new(4, 6));
|
||||||
|
|
||||||
|
assert!(r.contains(IVec2::new(3, -5)));
|
||||||
|
assert!(r.contains(IVec2::new(-1, -10)));
|
||||||
|
assert!(r.contains(IVec2::new(-1, 0)));
|
||||||
|
assert!(r.contains(IVec2::new(7, -10)));
|
||||||
|
assert!(r.contains(IVec2::new(7, 0)));
|
||||||
|
assert!(!r.contains(IVec2::new(50, -5)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rect_union() {
|
||||||
|
let r = IRect::from_center_size(IVec2::ZERO, IVec2::splat(4)); // [-2, -2] - [2, 2]
|
||||||
|
|
||||||
|
// overlapping
|
||||||
|
let r2 = IRect {
|
||||||
|
min: IVec2::new(1, 1),
|
||||||
|
max: IVec2::new(3, 3),
|
||||||
|
};
|
||||||
|
let u = r.union(r2);
|
||||||
|
assert_eq!(u.min, IVec2::new(-2, -2));
|
||||||
|
assert_eq!(u.max, IVec2::new(3, 3));
|
||||||
|
|
||||||
|
// disjoint
|
||||||
|
let r2 = IRect {
|
||||||
|
min: IVec2::new(1, 4),
|
||||||
|
max: IVec2::new(4, 6),
|
||||||
|
};
|
||||||
|
let u = r.union(r2);
|
||||||
|
assert_eq!(u.min, IVec2::new(-2, -2));
|
||||||
|
assert_eq!(u.max, IVec2::new(4, 6));
|
||||||
|
|
||||||
|
// included
|
||||||
|
let r2 = IRect::from_center_size(IVec2::ZERO, IVec2::splat(2));
|
||||||
|
let u = r.union(r2);
|
||||||
|
assert_eq!(u.min, r.min);
|
||||||
|
assert_eq!(u.max, r.max);
|
||||||
|
|
||||||
|
// including
|
||||||
|
let r2 = IRect::from_center_size(IVec2::ZERO, IVec2::splat(6));
|
||||||
|
let u = r.union(r2);
|
||||||
|
assert_eq!(u.min, r2.min);
|
||||||
|
assert_eq!(u.min, r2.min);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rect_union_pt() {
|
||||||
|
let r = IRect::from_center_size(IVec2::ZERO, IVec2::splat(4)); // [-2,-2] - [2,2]
|
||||||
|
|
||||||
|
// inside
|
||||||
|
let v = IVec2::new(1, -1);
|
||||||
|
let u = r.union_point(v);
|
||||||
|
assert_eq!(u.min, r.min);
|
||||||
|
assert_eq!(u.max, r.max);
|
||||||
|
|
||||||
|
// outside
|
||||||
|
let v = IVec2::new(10, -3);
|
||||||
|
let u = r.union_point(v);
|
||||||
|
assert_eq!(u.min, IVec2::new(-2, -3));
|
||||||
|
assert_eq!(u.max, IVec2::new(10, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rect_intersect() {
|
||||||
|
let r = IRect::from_center_size(IVec2::ZERO, IVec2::splat(8)); // [-4,-4] - [4,4]
|
||||||
|
|
||||||
|
// overlapping
|
||||||
|
let r2 = IRect {
|
||||||
|
min: IVec2::new(2, 2),
|
||||||
|
max: IVec2::new(6, 6),
|
||||||
|
};
|
||||||
|
let u = r.intersect(r2);
|
||||||
|
assert_eq!(u.min, IVec2::new(2, 2));
|
||||||
|
assert_eq!(u.max, IVec2::new(4, 4));
|
||||||
|
|
||||||
|
// disjoint
|
||||||
|
let r2 = IRect {
|
||||||
|
min: IVec2::new(-8, -2),
|
||||||
|
max: IVec2::new(-6, 2),
|
||||||
|
};
|
||||||
|
let u = r.intersect(r2);
|
||||||
|
assert!(u.is_empty());
|
||||||
|
assert_eq!(u.width(), 0);
|
||||||
|
|
||||||
|
// included
|
||||||
|
let r2 = IRect::from_center_size(IVec2::ZERO, IVec2::splat(2));
|
||||||
|
let u = r.intersect(r2);
|
||||||
|
assert_eq!(u.min, r2.min);
|
||||||
|
assert_eq!(u.max, r2.max);
|
||||||
|
|
||||||
|
// including
|
||||||
|
let r2 = IRect::from_center_size(IVec2::ZERO, IVec2::splat(10));
|
||||||
|
let u = r.intersect(r2);
|
||||||
|
assert_eq!(u.min, r.min);
|
||||||
|
assert_eq!(u.max, r.max);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rect_inset() {
|
||||||
|
let r = IRect::from_center_size(IVec2::ZERO, IVec2::splat(4)); // [-2,-2] - [2,2]
|
||||||
|
|
||||||
|
let r2 = r.inset(2);
|
||||||
|
assert_eq!(r2.min, IVec2::new(-4, -4));
|
||||||
|
assert_eq!(r2.max, IVec2::new(4, 4));
|
||||||
|
}
|
||||||
|
}
|
7
crates/bevy_math/src/rects/mod.rs
Normal file
7
crates/bevy_math/src/rects/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
mod irect;
|
||||||
|
mod rect;
|
||||||
|
mod urect;
|
||||||
|
|
||||||
|
pub use irect::IRect;
|
||||||
|
pub use rect::Rect;
|
||||||
|
pub use urect::URect;
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::Vec2;
|
use crate::{IRect, URect, Vec2};
|
||||||
|
|
||||||
/// A rectangle defined by two opposite corners.
|
/// A rectangle defined by two opposite corners.
|
||||||
///
|
///
|
||||||
|
@ -52,7 +52,7 @@ impl Rect {
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_corners(p0: Vec2, p1: Vec2) -> Self {
|
pub fn from_corners(p0: Vec2, p1: Vec2) -> Self {
|
||||||
Rect {
|
Self {
|
||||||
min: p0.min(p1),
|
min: p0.min(p1),
|
||||||
max: p0.max(p1),
|
max: p0.max(p1),
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ impl Rect {
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_center_size(origin: Vec2, size: Vec2) -> Self {
|
pub fn from_center_size(origin: Vec2, size: Vec2) -> Self {
|
||||||
assert!(size.cmpge(Vec2::ZERO).all());
|
assert!(size.cmpge(Vec2::ZERO).all(), "Rect size must be positive");
|
||||||
let half_size = size / 2.;
|
let half_size = size / 2.;
|
||||||
Self::from_center_half_size(origin, half_size)
|
Self::from_center_half_size(origin, half_size)
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,10 @@ impl Rect {
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_center_half_size(origin: Vec2, half_size: Vec2) -> Self {
|
pub fn from_center_half_size(origin: Vec2, half_size: Vec2) -> Self {
|
||||||
assert!(half_size.cmpge(Vec2::ZERO).all());
|
assert!(
|
||||||
|
half_size.cmpge(Vec2::ZERO).all(),
|
||||||
|
"Rect half_size must be positive"
|
||||||
|
);
|
||||||
Self {
|
Self {
|
||||||
min: origin - half_size,
|
min: origin - half_size,
|
||||||
max: origin + half_size,
|
max: origin + half_size,
|
||||||
|
@ -217,8 +220,8 @@ impl Rect {
|
||||||
/// assert!(r.max.abs_diff_eq(Vec2::new(5., 3.), 1e-5));
|
/// assert!(r.max.abs_diff_eq(Vec2::new(5., 3.), 1e-5));
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn union(&self, other: Rect) -> Rect {
|
pub fn union(&self, other: Self) -> Self {
|
||||||
Rect {
|
Self {
|
||||||
min: self.min.min(other.min),
|
min: self.min.min(other.min),
|
||||||
max: self.max.max(other.max),
|
max: self.max.max(other.max),
|
||||||
}
|
}
|
||||||
|
@ -239,8 +242,8 @@ impl Rect {
|
||||||
/// assert!(u.max.abs_diff_eq(Vec2::new(5., 6.), 1e-5));
|
/// assert!(u.max.abs_diff_eq(Vec2::new(5., 6.), 1e-5));
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn union_point(&self, other: Vec2) -> Rect {
|
pub fn union_point(&self, other: Vec2) -> Self {
|
||||||
Rect {
|
Self {
|
||||||
min: self.min.min(other),
|
min: self.min.min(other),
|
||||||
max: self.max.max(other),
|
max: self.max.max(other),
|
||||||
}
|
}
|
||||||
|
@ -263,8 +266,8 @@ impl Rect {
|
||||||
/// assert!(r.max.abs_diff_eq(Vec2::new(3., 1.), 1e-5));
|
/// assert!(r.max.abs_diff_eq(Vec2::new(3., 1.), 1e-5));
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn intersect(&self, other: Rect) -> Rect {
|
pub fn intersect(&self, other: Self) -> Self {
|
||||||
let mut r = Rect {
|
let mut r = Self {
|
||||||
min: self.min.max(other.min),
|
min: self.min.max(other.min),
|
||||||
max: self.max.min(other.max),
|
max: self.max.min(other.max),
|
||||||
};
|
};
|
||||||
|
@ -288,10 +291,15 @@ impl Rect {
|
||||||
/// let r2 = r.inset(3.); // w=11 h=7
|
/// let r2 = r.inset(3.); // w=11 h=7
|
||||||
/// assert!(r2.min.abs_diff_eq(Vec2::splat(-3.), 1e-5));
|
/// assert!(r2.min.abs_diff_eq(Vec2::splat(-3.), 1e-5));
|
||||||
/// assert!(r2.max.abs_diff_eq(Vec2::new(8., 4.), 1e-5));
|
/// assert!(r2.max.abs_diff_eq(Vec2::new(8., 4.), 1e-5));
|
||||||
|
///
|
||||||
|
/// let r = Rect::new(0., -1., 6., 7.); // w=6 h=8
|
||||||
|
/// let r2 = r.inset(-2.); // w=11 h=7
|
||||||
|
/// assert!(r2.min.abs_diff_eq(Vec2::new(2., 1.), 1e-5));
|
||||||
|
/// assert!(r2.max.abs_diff_eq(Vec2::new(4., 5.), 1e-5));
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn inset(&self, inset: f32) -> Rect {
|
pub fn inset(&self, inset: f32) -> Self {
|
||||||
let mut r = Rect {
|
let mut r = Self {
|
||||||
min: self.min - inset,
|
min: self.min - inset,
|
||||||
max: self.max + inset,
|
max: self.max + inset,
|
||||||
};
|
};
|
||||||
|
@ -300,6 +308,18 @@ impl Rect {
|
||||||
r.min = r.min.min(r.max);
|
r.min = r.min.min(r.max);
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns self as [`IRect`] (i32)
|
||||||
|
#[inline]
|
||||||
|
pub fn as_urect(&self) -> IRect {
|
||||||
|
IRect::from_corners(self.min.as_ivec2(), self.max.as_ivec2())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns self as [`URect`] (u32)
|
||||||
|
#[inline]
|
||||||
|
pub fn as_rect(&self) -> URect {
|
||||||
|
URect::from_corners(self.min.as_uvec2(), self.max.as_uvec2())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
461
crates/bevy_math/src/rects/urect.rs
Normal file
461
crates/bevy_math/src/rects/urect.rs
Normal file
|
@ -0,0 +1,461 @@
|
||||||
|
use crate::{IRect, Rect, UVec2};
|
||||||
|
|
||||||
|
/// A rectangle defined by two opposite corners.
|
||||||
|
///
|
||||||
|
/// The rectangle is axis aligned, and defined by its minimum and maximum coordinates,
|
||||||
|
/// stored in `URect::min` and `URect::max`, respectively. The minimum/maximum invariant
|
||||||
|
/// must be upheld by the user when directly assigning the fields, otherwise some methods
|
||||||
|
/// produce invalid results. It is generally recommended to use one of the constructor
|
||||||
|
/// methods instead, which will ensure this invariant is met, unless you already have
|
||||||
|
/// the minimum and maximum corners.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default, Clone, Copy, Debug, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
pub struct URect {
|
||||||
|
/// The minimum corner point of the rect.
|
||||||
|
pub min: UVec2,
|
||||||
|
/// The maximum corner point of the rect.
|
||||||
|
pub max: UVec2,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl URect {
|
||||||
|
/// Create a new rectangle from two corner points.
|
||||||
|
///
|
||||||
|
/// The two points do not need to be the minimum and/or maximum corners.
|
||||||
|
/// They only need to be two opposite corners.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::URect;
|
||||||
|
/// let r = URect::new(0, 4, 10, 6); // w=10 h=2
|
||||||
|
/// let r = URect::new(2, 4, 5, 0); // w=3 h=4
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn new(x0: u32, y0: u32, x1: u32, y1: u32) -> Self {
|
||||||
|
Self::from_corners(UVec2::new(x0, y0), UVec2::new(x1, y1))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new rectangle from two corner points.
|
||||||
|
///
|
||||||
|
/// The two points do not need to be the minimum and/or maximum corners.
|
||||||
|
/// They only need to be two opposite corners.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::{URect, UVec2};
|
||||||
|
/// // Unit rect from [0,0] to [1,1]
|
||||||
|
/// let r = URect::from_corners(UVec2::ZERO, UVec2::ONE); // w=1 h=1
|
||||||
|
/// // Same; the points do not need to be ordered
|
||||||
|
/// let r = URect::from_corners(UVec2::ONE, UVec2::ZERO); // w=1 h=1
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn from_corners(p0: UVec2, p1: UVec2) -> Self {
|
||||||
|
Self {
|
||||||
|
min: p0.min(p1),
|
||||||
|
max: p0.max(p1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new rectangle from its center and size.
|
||||||
|
///
|
||||||
|
/// # Rounding Behaviour
|
||||||
|
///
|
||||||
|
/// If the size contains odd numbers they will be rounded down to the nearest whole number.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// This method panics if any of the components of the size is negative or if `origin - (size / 2)` results in any negatives.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::{URect, UVec2};
|
||||||
|
/// let r = URect::from_center_size(UVec2::ONE, UVec2::splat(2)); // w=2 h=2
|
||||||
|
/// assert_eq!(r.min, UVec2::splat(0));
|
||||||
|
/// assert_eq!(r.max, UVec2::splat(2));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn from_center_size(origin: UVec2, size: UVec2) -> Self {
|
||||||
|
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}");
|
||||||
|
let half_size = size / 2;
|
||||||
|
Self::from_center_half_size(origin, half_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new rectangle from its center and half-size.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// This method panics if any of the components of the half-size is negative or if `origin - half_size` results in any negatives.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::{URect, UVec2};
|
||||||
|
/// let r = URect::from_center_half_size(UVec2::ONE, UVec2::ONE); // w=2 h=2
|
||||||
|
/// assert_eq!(r.min, UVec2::splat(0));
|
||||||
|
/// assert_eq!(r.max, UVec2::splat(2));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn from_center_half_size(origin: UVec2, half_size: UVec2) -> Self {
|
||||||
|
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}");
|
||||||
|
Self {
|
||||||
|
min: origin - half_size,
|
||||||
|
max: origin + half_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the rectangle is empty.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::{URect, UVec2};
|
||||||
|
/// let r = URect::from_corners(UVec2::ZERO, UVec2::new(0, 1)); // w=0 h=1
|
||||||
|
/// assert!(r.is_empty());
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.min.cmpge(self.max).any()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rectangle width (max.x - min.x).
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::URect;
|
||||||
|
/// let r = URect::new(0, 0, 5, 1); // w=5 h=1
|
||||||
|
/// assert_eq!(r.width(), 5);
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn width(&self) -> u32 {
|
||||||
|
self.max.x - self.min.x
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rectangle height (max.y - min.y).
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::URect;
|
||||||
|
/// let r = URect::new(0, 0, 5, 1); // w=5 h=1
|
||||||
|
/// assert_eq!(r.height(), 1);
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn height(&self) -> u32 {
|
||||||
|
self.max.y - self.min.y
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rectangle size.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::{URect, UVec2};
|
||||||
|
/// let r = URect::new(0, 0, 5, 1); // w=5 h=1
|
||||||
|
/// assert_eq!(r.size(), UVec2::new(5, 1));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn size(&self) -> UVec2 {
|
||||||
|
self.max - self.min
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rectangle half-size.
|
||||||
|
///
|
||||||
|
/// # Rounding Behaviour
|
||||||
|
///
|
||||||
|
/// If the full size contains odd numbers they will be rounded down to the nearest whole number when calculating the half size.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::{URect, UVec2};
|
||||||
|
/// let r = URect::new(0, 0, 4, 2); // w=4 h=2
|
||||||
|
/// assert_eq!(r.half_size(), UVec2::new(2, 1));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn half_size(&self) -> UVec2 {
|
||||||
|
self.size() / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The center point of the rectangle.
|
||||||
|
///
|
||||||
|
/// # Rounding Behaviour
|
||||||
|
///
|
||||||
|
/// If the (min + max) contains odd numbers they will be rounded down to the nearest whole number when calculating the center.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::{URect, UVec2};
|
||||||
|
/// let r = URect::new(0, 0, 4, 2); // w=4 h=2
|
||||||
|
/// assert_eq!(r.center(), UVec2::new(2, 1));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn center(&self) -> UVec2 {
|
||||||
|
(self.min + self.max) / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if a point lies within this rectangle, inclusive of its edges.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::URect;
|
||||||
|
/// let r = URect::new(0, 0, 5, 1); // w=5 h=1
|
||||||
|
/// assert!(r.contains(r.center()));
|
||||||
|
/// assert!(r.contains(r.min));
|
||||||
|
/// assert!(r.contains(r.max));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn contains(&self, point: UVec2) -> bool {
|
||||||
|
(point.cmpge(self.min) & point.cmple(self.max)).all()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build a new rectangle formed of the union of this rectangle and another rectangle.
|
||||||
|
///
|
||||||
|
/// The union is the smallest rectangle enclosing both rectangles.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::{URect, UVec2};
|
||||||
|
/// let r1 = URect::new(0, 0, 5, 1); // w=5 h=1
|
||||||
|
/// let r2 = URect::new(1, 0, 3, 8); // w=2 h=4
|
||||||
|
/// let r = r1.union(r2);
|
||||||
|
/// assert_eq!(r.min, UVec2::new(0, 0));
|
||||||
|
/// assert_eq!(r.max, UVec2::new(5, 8));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn union(&self, other: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
min: self.min.min(other.min),
|
||||||
|
max: self.max.max(other.max),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build a new rectangle formed of the union of this rectangle and a point.
|
||||||
|
///
|
||||||
|
/// The union is the smallest rectangle enclosing both the rectangle and the point. If the
|
||||||
|
/// point is already inside the rectangle, this method returns a copy of the rectangle.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::{URect, UVec2};
|
||||||
|
/// let r = URect::new(0, 0, 5, 1); // w=5 h=1
|
||||||
|
/// let u = r.union_point(UVec2::new(3, 6));
|
||||||
|
/// assert_eq!(u.min, UVec2::ZERO);
|
||||||
|
/// assert_eq!(u.max, UVec2::new(5, 6));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn union_point(&self, other: UVec2) -> Self {
|
||||||
|
Self {
|
||||||
|
min: self.min.min(other),
|
||||||
|
max: self.max.max(other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build a new rectangle formed of the intersection of this rectangle and another rectangle.
|
||||||
|
///
|
||||||
|
/// The intersection is the largest rectangle enclosed in both rectangles. If the intersection
|
||||||
|
/// is empty, this method returns an empty rectangle ([`URect::is_empty()`] returns `true`), but
|
||||||
|
/// the actual values of [`URect::min`] and [`URect::max`] are implementation-dependent.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::{URect, UVec2};
|
||||||
|
/// let r1 = URect::new(0, 0, 2, 2); // w=2 h=2
|
||||||
|
/// let r2 = URect::new(1, 1, 3, 3); // w=2 h=2
|
||||||
|
/// let r = r1.intersect(r2);
|
||||||
|
/// assert_eq!(r.min, UVec2::new(1, 1));
|
||||||
|
/// assert_eq!(r.max, UVec2::new(2, 2));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn intersect(&self, other: Self) -> Self {
|
||||||
|
let mut r = Self {
|
||||||
|
min: self.min.max(other.min),
|
||||||
|
max: self.max.min(other.max),
|
||||||
|
};
|
||||||
|
// Collapse min over max to enforce invariants and ensure e.g. width() or
|
||||||
|
// height() never return a negative value.
|
||||||
|
r.min = r.min.min(r.max);
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new rectangle with a constant inset.
|
||||||
|
///
|
||||||
|
/// The inset is the extra border on all sides. A positive inset produces a larger rectangle,
|
||||||
|
/// while a negative inset is allowed and produces a smaller rectangle. If the inset is negative
|
||||||
|
/// and its absolute value is larger than the rectangle half-size, the created rectangle is empty.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use bevy_math::{URect, UVec2};
|
||||||
|
/// let r = URect::new(4, 4, 6, 6); // w=2 h=2
|
||||||
|
/// let r2 = r.inset(1); // w=4 h=4
|
||||||
|
/// assert_eq!(r2.min, UVec2::splat(3));
|
||||||
|
/// assert_eq!(r2.max, UVec2::splat(7));
|
||||||
|
///
|
||||||
|
/// let r = URect::new(4, 4, 8, 8); // w=4 h=4
|
||||||
|
/// let r2 = r.inset(-1); // w=2 h=2
|
||||||
|
/// assert_eq!(r2.min, UVec2::splat(5));
|
||||||
|
/// assert_eq!(r2.max, UVec2::splat(7));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn inset(&self, inset: i32) -> Self {
|
||||||
|
let mut r = Self {
|
||||||
|
min: UVec2::new(
|
||||||
|
self.min.x.saturating_add_signed(-inset),
|
||||||
|
self.min.y.saturating_add_signed(-inset),
|
||||||
|
),
|
||||||
|
max: UVec2::new(
|
||||||
|
self.max.x.saturating_add_signed(inset),
|
||||||
|
self.max.y.saturating_add_signed(inset),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
// Collapse min over max to enforce invariants and ensure e.g. width() or
|
||||||
|
// height() never return a negative value.
|
||||||
|
r.min = r.min.min(r.max);
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns self as [`Rect`] (f32)
|
||||||
|
#[inline]
|
||||||
|
pub fn as_rect(&self) -> Rect {
|
||||||
|
Rect::from_corners(self.min.as_vec2(), self.max.as_vec2())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns self as [`IRect`] (i32)
|
||||||
|
#[inline]
|
||||||
|
pub fn as_urect(&self) -> IRect {
|
||||||
|
IRect::from_corners(self.min.as_ivec2(), self.max.as_ivec2())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn well_formed() {
|
||||||
|
let r = URect::from_center_size(UVec2::new(10, 16), UVec2::new(8, 12));
|
||||||
|
|
||||||
|
assert_eq!(r.min, UVec2::new(6, 10));
|
||||||
|
assert_eq!(r.max, UVec2::new(14, 22));
|
||||||
|
|
||||||
|
assert_eq!(r.center(), UVec2::new(10, 16));
|
||||||
|
|
||||||
|
assert_eq!(r.width(), 8);
|
||||||
|
assert_eq!(r.height(), 12);
|
||||||
|
assert_eq!(r.size(), UVec2::new(8, 12));
|
||||||
|
assert_eq!(r.half_size(), UVec2::new(4, 6));
|
||||||
|
|
||||||
|
assert!(r.contains(UVec2::new(7, 10)));
|
||||||
|
assert!(r.contains(UVec2::new(14, 10)));
|
||||||
|
assert!(r.contains(UVec2::new(10, 22)));
|
||||||
|
assert!(r.contains(UVec2::new(6, 22)));
|
||||||
|
assert!(r.contains(UVec2::new(14, 22)));
|
||||||
|
assert!(!r.contains(UVec2::new(50, 5)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rect_union() {
|
||||||
|
let r = URect::from_center_size(UVec2::splat(4), UVec2::splat(4)); // [2, 2] - [6, 6]
|
||||||
|
|
||||||
|
// overlapping
|
||||||
|
let r2 = URect {
|
||||||
|
min: UVec2::new(0, 0),
|
||||||
|
max: UVec2::new(3, 3),
|
||||||
|
};
|
||||||
|
let u = r.union(r2);
|
||||||
|
assert_eq!(u.min, UVec2::new(0, 0));
|
||||||
|
assert_eq!(u.max, UVec2::new(6, 6));
|
||||||
|
|
||||||
|
// disjoint
|
||||||
|
let r2 = URect {
|
||||||
|
min: UVec2::new(4, 7),
|
||||||
|
max: UVec2::new(8, 8),
|
||||||
|
};
|
||||||
|
let u = r.union(r2);
|
||||||
|
assert_eq!(u.min, UVec2::new(2, 2));
|
||||||
|
assert_eq!(u.max, UVec2::new(8, 8));
|
||||||
|
|
||||||
|
// included
|
||||||
|
let r2 = URect::from_center_size(UVec2::splat(4), UVec2::splat(2));
|
||||||
|
let u = r.union(r2);
|
||||||
|
assert_eq!(u.min, r.min);
|
||||||
|
assert_eq!(u.max, r.max);
|
||||||
|
|
||||||
|
// including
|
||||||
|
let r2 = URect::from_center_size(UVec2::splat(4), UVec2::splat(6));
|
||||||
|
let u = r.union(r2);
|
||||||
|
assert_eq!(u.min, r2.min);
|
||||||
|
assert_eq!(u.min, r2.min);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rect_union_pt() {
|
||||||
|
let r = URect::from_center_size(UVec2::splat(4), UVec2::splat(4)); // [2, 2] - [6, 6]
|
||||||
|
|
||||||
|
// inside
|
||||||
|
let v = UVec2::new(2, 5);
|
||||||
|
let u = r.union_point(v);
|
||||||
|
assert_eq!(u.min, r.min);
|
||||||
|
assert_eq!(u.max, r.max);
|
||||||
|
|
||||||
|
// outside
|
||||||
|
let v = UVec2::new(10, 5);
|
||||||
|
let u = r.union_point(v);
|
||||||
|
assert_eq!(u.min, UVec2::new(2, 2));
|
||||||
|
assert_eq!(u.max, UVec2::new(10, 6));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rect_intersect() {
|
||||||
|
let r = URect::from_center_size(UVec2::splat(6), UVec2::splat(8)); // [2, 2] - [10, 10]
|
||||||
|
|
||||||
|
// overlapping
|
||||||
|
let r2 = URect {
|
||||||
|
min: UVec2::new(8, 8),
|
||||||
|
max: UVec2::new(12, 12),
|
||||||
|
};
|
||||||
|
let u = r.intersect(r2);
|
||||||
|
assert_eq!(u.min, UVec2::new(8, 8));
|
||||||
|
assert_eq!(u.max, UVec2::new(10, 10));
|
||||||
|
|
||||||
|
// disjoint
|
||||||
|
let r2 = URect {
|
||||||
|
min: UVec2::new(12, 12),
|
||||||
|
max: UVec2::new(14, 18),
|
||||||
|
};
|
||||||
|
let u = r.intersect(r2);
|
||||||
|
assert!(u.is_empty());
|
||||||
|
assert_eq!(u.width(), 0);
|
||||||
|
|
||||||
|
// included
|
||||||
|
let r2 = URect::from_center_size(UVec2::splat(6), UVec2::splat(2));
|
||||||
|
let u = r.intersect(r2);
|
||||||
|
assert_eq!(u.min, r2.min);
|
||||||
|
assert_eq!(u.max, r2.max);
|
||||||
|
|
||||||
|
// including
|
||||||
|
let r2 = URect::from_center_size(UVec2::splat(6), UVec2::splat(10));
|
||||||
|
let u = r.intersect(r2);
|
||||||
|
assert_eq!(u.min, r.min);
|
||||||
|
assert_eq!(u.max, r.max);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rect_inset() {
|
||||||
|
let r = URect::from_center_size(UVec2::splat(6), UVec2::splat(6)); // [3, 3] - [9, 9]
|
||||||
|
|
||||||
|
let r2 = r.inset(2);
|
||||||
|
assert_eq!(r2.min, UVec2::new(1, 1));
|
||||||
|
assert_eq!(r2.max, UVec2::new(11, 11));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue