use crate::{IRect, Rect, UVec2};
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
#[repr(C)]
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Debug, PartialEq, Hash, Default, Clone)
)]
#[cfg_attr(
all(feature = "serialize", feature = "bevy_reflect"),
reflect(Serialize, Deserialize)
)]
pub struct URect {
pub min: UVec2,
pub max: UVec2,
}
impl URect {
pub const EMPTY: Self = Self {
max: UVec2::MIN,
min: UVec2::MAX,
};
#[inline]
pub fn new(x0: u32, y0: u32, x1: u32, y1: u32) -> Self {
Self::from_corners(UVec2::new(x0, y0), UVec2::new(x1, y1))
}
#[inline]
pub fn from_corners(p0: UVec2, p1: UVec2) -> Self {
Self {
min: p0.min(p1),
max: p0.max(p1),
}
}
#[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)
}
#[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,
}
}
#[inline]
pub fn is_empty(&self) -> bool {
self.min.cmpge(self.max).any()
}
#[inline]
pub const fn width(&self) -> u32 {
self.max.x - self.min.x
}
#[inline]
pub const fn height(&self) -> u32 {
self.max.y - self.min.y
}
#[inline]
pub fn size(&self) -> UVec2 {
self.max - self.min
}
#[inline]
pub fn half_size(&self) -> UVec2 {
self.size() / 2
}
#[inline]
pub fn center(&self) -> UVec2 {
(self.min + self.max) / 2
}
#[inline]
pub fn contains(&self, point: UVec2) -> bool {
(point.cmpge(self.min) & point.cmple(self.max)).all()
}
#[inline]
pub fn union(&self, other: Self) -> Self {
Self {
min: self.min.min(other.min),
max: self.max.max(other.max),
}
}
#[inline]
pub fn union_point(&self, other: UVec2) -> Self {
Self {
min: self.min.min(other),
max: self.max.max(other),
}
}
#[inline]
pub fn intersect(&self, other: Self) -> Self {
let mut r = Self {
min: self.min.max(other.min),
max: self.max.min(other.max),
};
r.min = r.min.min(r.max);
r
}
#[inline]
pub fn inflate(&self, expansion: i32) -> Self {
let mut r = Self {
min: UVec2::new(
self.min.x.saturating_add_signed(-expansion),
self.min.y.saturating_add_signed(-expansion),
),
max: UVec2::new(
self.max.x.saturating_add_signed(expansion),
self.max.y.saturating_add_signed(expansion),
),
};
r.min = r.min.min(r.max);
r
}
#[inline]
pub fn as_rect(&self) -> Rect {
Rect::from_corners(self.min.as_vec2(), self.max.as_vec2())
}
#[inline]
pub fn as_irect(&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));
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));
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));
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);
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));
let v = UVec2::new(2, 5);
let u = r.union_point(v);
assert_eq!(u.min, r.min);
assert_eq!(u.max, r.max);
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));
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));
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);
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);
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_inflate() {
let r = URect::from_center_size(UVec2::splat(6), UVec2::splat(6));
let r2 = r.inflate(2);
assert_eq!(r2.min, UVec2::new(1, 1));
assert_eq!(r2.max, UVec2::new(11, 11));
}
}