use crate::{IRect, Rect, UVec2};12#[cfg(feature = "bevy_reflect")]3use bevy_reflect::{std_traits::ReflectDefault, Reflect};4#[cfg(all(feature = "serialize", feature = "bevy_reflect"))]5use bevy_reflect::{ReflectDeserialize, ReflectSerialize};67/// A rectangle defined by two opposite corners.8///9/// The rectangle is axis aligned, and defined by its minimum and maximum coordinates,10/// stored in `URect::min` and `URect::max`, respectively. The minimum/maximum invariant11/// must be upheld by the user when directly assigning the fields, otherwise some methods12/// produce invalid results. It is generally recommended to use one of the constructor13/// methods instead, which will ensure this invariant is met, unless you already have14/// the minimum and maximum corners.15#[repr(C)]16#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Hash)]17#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]18#[cfg_attr(19feature = "bevy_reflect",20derive(Reflect),21reflect(Debug, PartialEq, Hash, Default, Clone)22)]23#[cfg_attr(24all(feature = "serialize", feature = "bevy_reflect"),25reflect(Serialize, Deserialize)26)]27pub struct URect {28/// The minimum corner point of the rect.29pub min: UVec2,30/// The maximum corner point of the rect.31pub max: UVec2,32}3334impl URect {35/// An empty `URect`, represented by maximum and minimum corner points36/// with `max == UVec2::MIN` and `min == UVec2::MAX`, so the37/// rect has an extremely large negative size.38/// This is useful, because when taking a union B of a non-empty `URect` A and39/// this empty `URect`, B will simply equal A.40pub const EMPTY: Self = Self {41max: UVec2::MIN,42min: UVec2::MAX,43};44/// Create a new rectangle from two corner points.45///46/// The two points do not need to be the minimum and/or maximum corners.47/// They only need to be two opposite corners.48///49/// # Examples50///51/// ```52/// # use bevy_math::URect;53/// let r = URect::new(0, 4, 10, 6); // w=10 h=254/// let r = URect::new(2, 4, 5, 0); // w=3 h=455/// ```56#[inline]57pub fn new(x0: u32, y0: u32, x1: u32, y1: u32) -> Self {58Self::from_corners(UVec2::new(x0, y0), UVec2::new(x1, y1))59}6061/// Create a new rectangle from two corner points.62///63/// The two points do not need to be the minimum and/or maximum corners.64/// They only need to be two opposite corners.65///66/// # Examples67///68/// ```69/// # use bevy_math::{URect, UVec2};70/// // Unit rect from [0,0] to [1,1]71/// let r = URect::from_corners(UVec2::ZERO, UVec2::ONE); // w=1 h=172/// // Same; the points do not need to be ordered73/// let r = URect::from_corners(UVec2::ONE, UVec2::ZERO); // w=1 h=174/// ```75#[inline]76pub fn from_corners(p0: UVec2, p1: UVec2) -> Self {77Self {78min: p0.min(p1),79max: p0.max(p1),80}81}8283/// Create a new rectangle from its center and size.84///85/// # Rounding Behavior86///87/// If the size contains odd numbers they will be rounded down to the nearest whole number.88///89/// # Panics90///91/// This method panics if any of the components of the size is negative or if `origin - (size / 2)` results in any negatives.92///93/// # Examples94///95/// ```96/// # use bevy_math::{URect, UVec2};97/// let r = URect::from_center_size(UVec2::ONE, UVec2::splat(2)); // w=2 h=298/// assert_eq!(r.min, UVec2::splat(0));99/// assert_eq!(r.max, UVec2::splat(2));100/// ```101#[inline]102pub fn from_center_size(origin: UVec2, size: UVec2) -> Self {103assert!(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}");104let half_size = size / 2;105Self::from_center_half_size(origin, half_size)106}107108/// Create a new rectangle from its center and half-size.109///110/// # Panics111///112/// This method panics if any of the components of the half-size is negative or if `origin - half_size` results in any negatives.113///114/// # Examples115///116/// ```117/// # use bevy_math::{URect, UVec2};118/// let r = URect::from_center_half_size(UVec2::ONE, UVec2::ONE); // w=2 h=2119/// assert_eq!(r.min, UVec2::splat(0));120/// assert_eq!(r.max, UVec2::splat(2));121/// ```122#[inline]123pub fn from_center_half_size(origin: UVec2, half_size: UVec2) -> Self {124assert!(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}");125Self {126min: origin - half_size,127max: origin + half_size,128}129}130131/// Check if the rectangle is empty.132///133/// # Examples134///135/// ```136/// # use bevy_math::{URect, UVec2};137/// let r = URect::from_corners(UVec2::ZERO, UVec2::new(0, 1)); // w=0 h=1138/// assert!(r.is_empty());139/// ```140#[inline]141pub fn is_empty(&self) -> bool {142self.min.cmpge(self.max).any()143}144145/// Rectangle width (max.x - min.x).146///147/// # Examples148///149/// ```150/// # use bevy_math::URect;151/// let r = URect::new(0, 0, 5, 1); // w=5 h=1152/// assert_eq!(r.width(), 5);153/// ```154#[inline]155pub const fn width(&self) -> u32 {156self.max.x - self.min.x157}158159/// Rectangle height (max.y - min.y).160///161/// # Examples162///163/// ```164/// # use bevy_math::URect;165/// let r = URect::new(0, 0, 5, 1); // w=5 h=1166/// assert_eq!(r.height(), 1);167/// ```168#[inline]169pub const fn height(&self) -> u32 {170self.max.y - self.min.y171}172173/// Rectangle size.174///175/// # Examples176///177/// ```178/// # use bevy_math::{URect, UVec2};179/// let r = URect::new(0, 0, 5, 1); // w=5 h=1180/// assert_eq!(r.size(), UVec2::new(5, 1));181/// ```182#[inline]183pub fn size(&self) -> UVec2 {184self.max - self.min185}186187/// Rectangle half-size.188///189/// # Rounding Behavior190///191/// If the full size contains odd numbers they will be rounded down to the nearest whole number when calculating the half size.192///193/// # Examples194///195/// ```196/// # use bevy_math::{URect, UVec2};197/// let r = URect::new(0, 0, 4, 2); // w=4 h=2198/// assert_eq!(r.half_size(), UVec2::new(2, 1));199/// ```200#[inline]201pub fn half_size(&self) -> UVec2 {202self.size() / 2203}204205/// The center point of the rectangle.206///207/// # Rounding Behavior208///209/// If the (min + max) contains odd numbers they will be rounded down to the nearest whole number when calculating the center.210///211/// # Examples212///213/// ```214/// # use bevy_math::{URect, UVec2};215/// let r = URect::new(0, 0, 4, 2); // w=4 h=2216/// assert_eq!(r.center(), UVec2::new(2, 1));217/// ```218#[inline]219pub fn center(&self) -> UVec2 {220(self.min + self.max) / 2221}222223/// Check if a point lies within this rectangle, inclusive of its edges.224///225/// # Examples226///227/// ```228/// # use bevy_math::URect;229/// let r = URect::new(0, 0, 5, 1); // w=5 h=1230/// assert!(r.contains(r.center()));231/// assert!(r.contains(r.min));232/// assert!(r.contains(r.max));233/// ```234#[inline]235pub fn contains(&self, point: UVec2) -> bool {236(point.cmpge(self.min) & point.cmple(self.max)).all()237}238239/// Build a new rectangle formed of the union of this rectangle and another rectangle.240///241/// The union is the smallest rectangle enclosing both rectangles.242///243/// # Examples244///245/// ```246/// # use bevy_math::{URect, UVec2};247/// let r1 = URect::new(0, 0, 5, 1); // w=5 h=1248/// let r2 = URect::new(1, 0, 3, 8); // w=2 h=4249/// let r = r1.union(r2);250/// assert_eq!(r.min, UVec2::new(0, 0));251/// assert_eq!(r.max, UVec2::new(5, 8));252/// ```253#[inline]254pub fn union(&self, other: Self) -> Self {255Self {256min: self.min.min(other.min),257max: self.max.max(other.max),258}259}260261/// Build a new rectangle formed of the union of this rectangle and a point.262///263/// The union is the smallest rectangle enclosing both the rectangle and the point. If the264/// point is already inside the rectangle, this method returns a copy of the rectangle.265///266/// # Examples267///268/// ```269/// # use bevy_math::{URect, UVec2};270/// let r = URect::new(0, 0, 5, 1); // w=5 h=1271/// let u = r.union_point(UVec2::new(3, 6));272/// assert_eq!(u.min, UVec2::ZERO);273/// assert_eq!(u.max, UVec2::new(5, 6));274/// ```275#[inline]276pub fn union_point(&self, other: UVec2) -> Self {277Self {278min: self.min.min(other),279max: self.max.max(other),280}281}282283/// Build a new rectangle formed of the intersection of this rectangle and another rectangle.284///285/// The intersection is the largest rectangle enclosed in both rectangles. If the intersection286/// is empty, this method returns an empty rectangle ([`URect::is_empty()`] returns `true`), but287/// the actual values of [`URect::min`] and [`URect::max`] are implementation-dependent.288///289/// # Examples290///291/// ```292/// # use bevy_math::{URect, UVec2};293/// let r1 = URect::new(0, 0, 2, 2); // w=2 h=2294/// let r2 = URect::new(1, 1, 3, 3); // w=2 h=2295/// let r = r1.intersect(r2);296/// assert_eq!(r.min, UVec2::new(1, 1));297/// assert_eq!(r.max, UVec2::new(2, 2));298/// ```299#[inline]300pub fn intersect(&self, other: Self) -> Self {301let mut r = Self {302min: self.min.max(other.min),303max: self.max.min(other.max),304};305// Collapse min over max to enforce invariants and ensure e.g. width() or306// height() never return a negative value.307r.min = r.min.min(r.max);308r309}310311/// Create a new rectangle by expanding it evenly on all sides.312///313/// A positive expansion value produces a larger rectangle,314/// while a negative expansion value produces a smaller rectangle.315/// If this would result in zero width or height, [`URect::EMPTY`] is returned instead.316///317/// # Examples318///319/// ```320/// # use bevy_math::{URect, UVec2};321/// let r = URect::new(4, 4, 6, 6); // w=2 h=2322/// let r2 = r.inflate(1); // w=4 h=4323/// assert_eq!(r2.min, UVec2::splat(3));324/// assert_eq!(r2.max, UVec2::splat(7));325///326/// let r = URect::new(4, 4, 8, 8); // w=4 h=4327/// let r2 = r.inflate(-1); // w=2 h=2328/// assert_eq!(r2.min, UVec2::splat(5));329/// assert_eq!(r2.max, UVec2::splat(7));330/// ```331#[inline]332pub fn inflate(&self, expansion: i32) -> Self {333let mut r = Self {334min: UVec2::new(335self.min.x.saturating_add_signed(-expansion),336self.min.y.saturating_add_signed(-expansion),337),338max: UVec2::new(339self.max.x.saturating_add_signed(expansion),340self.max.y.saturating_add_signed(expansion),341),342};343// Collapse min over max to enforce invariants and ensure e.g. width() or344// height() never return a negative value.345r.min = r.min.min(r.max);346r347}348349/// Returns self as [`Rect`] (f32)350#[inline]351pub fn as_rect(&self) -> Rect {352Rect::from_corners(self.min.as_vec2(), self.max.as_vec2())353}354355/// Returns self as [`IRect`] (i32)356#[inline]357pub fn as_irect(&self) -> IRect {358IRect::from_corners(self.min.as_ivec2(), self.max.as_ivec2())359}360}361362#[cfg(test)]363mod tests {364use super::*;365366#[test]367fn well_formed() {368let r = URect::from_center_size(UVec2::new(10, 16), UVec2::new(8, 12));369370assert_eq!(r.min, UVec2::new(6, 10));371assert_eq!(r.max, UVec2::new(14, 22));372373assert_eq!(r.center(), UVec2::new(10, 16));374375assert_eq!(r.width(), 8);376assert_eq!(r.height(), 12);377assert_eq!(r.size(), UVec2::new(8, 12));378assert_eq!(r.half_size(), UVec2::new(4, 6));379380assert!(r.contains(UVec2::new(7, 10)));381assert!(r.contains(UVec2::new(14, 10)));382assert!(r.contains(UVec2::new(10, 22)));383assert!(r.contains(UVec2::new(6, 22)));384assert!(r.contains(UVec2::new(14, 22)));385assert!(!r.contains(UVec2::new(50, 5)));386}387388#[test]389fn rect_union() {390let r = URect::from_center_size(UVec2::splat(4), UVec2::splat(4)); // [2, 2] - [6, 6]391392// overlapping393let r2 = URect {394min: UVec2::new(0, 0),395max: UVec2::new(3, 3),396};397let u = r.union(r2);398assert_eq!(u.min, UVec2::new(0, 0));399assert_eq!(u.max, UVec2::new(6, 6));400401// disjoint402let r2 = URect {403min: UVec2::new(4, 7),404max: UVec2::new(8, 8),405};406let u = r.union(r2);407assert_eq!(u.min, UVec2::new(2, 2));408assert_eq!(u.max, UVec2::new(8, 8));409410// included411let r2 = URect::from_center_size(UVec2::splat(4), UVec2::splat(2));412let u = r.union(r2);413assert_eq!(u.min, r.min);414assert_eq!(u.max, r.max);415416// including417let r2 = URect::from_center_size(UVec2::splat(4), UVec2::splat(6));418let u = r.union(r2);419assert_eq!(u.min, r2.min);420assert_eq!(u.min, r2.min);421}422423#[test]424fn rect_union_pt() {425let r = URect::from_center_size(UVec2::splat(4), UVec2::splat(4)); // [2, 2] - [6, 6]426427// inside428let v = UVec2::new(2, 5);429let u = r.union_point(v);430assert_eq!(u.min, r.min);431assert_eq!(u.max, r.max);432433// outside434let v = UVec2::new(10, 5);435let u = r.union_point(v);436assert_eq!(u.min, UVec2::new(2, 2));437assert_eq!(u.max, UVec2::new(10, 6));438}439440#[test]441fn rect_intersect() {442let r = URect::from_center_size(UVec2::splat(6), UVec2::splat(8)); // [2, 2] - [10, 10]443444// overlapping445let r2 = URect {446min: UVec2::new(8, 8),447max: UVec2::new(12, 12),448};449let u = r.intersect(r2);450assert_eq!(u.min, UVec2::new(8, 8));451assert_eq!(u.max, UVec2::new(10, 10));452453// disjoint454let r2 = URect {455min: UVec2::new(12, 12),456max: UVec2::new(14, 18),457};458let u = r.intersect(r2);459assert!(u.is_empty());460assert_eq!(u.width(), 0);461462// included463let r2 = URect::from_center_size(UVec2::splat(6), UVec2::splat(2));464let u = r.intersect(r2);465assert_eq!(u.min, r2.min);466assert_eq!(u.max, r2.max);467468// including469let r2 = URect::from_center_size(UVec2::splat(6), UVec2::splat(10));470let u = r.intersect(r2);471assert_eq!(u.min, r.min);472assert_eq!(u.max, r.max);473}474475#[test]476fn rect_inflate() {477let r = URect::from_center_size(UVec2::splat(6), UVec2::splat(6)); // [3, 3] - [9, 9]478479let r2 = r.inflate(2);480assert_eq!(r2.min, UVec2::new(1, 1));481assert_eq!(r2.max, UVec2::new(11, 11));482}483}484485486