use crate::{IVec2, Rect, URect};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 `IRect::min` and `IRect::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 IRect {28/// The minimum corner point of the rect.29pub min: IVec2,30/// The maximum corner point of the rect.31pub max: IVec2,32}3334impl IRect {35/// An empty `IRect`, represented by maximum and minimum corner points36/// with `max == IVec2::MIN` and `min == IVec2::MAX`, so the37/// rect has an extremely large negative size.38/// This is useful, because when taking a union B of a non-empty `IRect` A and39/// this empty `IRect`, B will simply equal A.40pub const EMPTY: Self = Self {41max: IVec2::MIN,42min: IVec2::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::IRect;53/// let r = IRect::new(0, 4, 10, 6); // w=10 h=254/// let r = IRect::new(2, 3, 5, -1); // w=3 h=455/// ```56#[inline]57pub fn new(x0: i32, y0: i32, x1: i32, y1: i32) -> Self {58Self::from_corners(IVec2::new(x0, y0), IVec2::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::{IRect, IVec2};70/// // Unit rect from [0,0] to [1,1]71/// let r = IRect::from_corners(IVec2::ZERO, IVec2::ONE); // w=1 h=172/// // Same; the points do not need to be ordered73/// let r = IRect::from_corners(IVec2::ONE, IVec2::ZERO); // w=1 h=174/// ```75#[inline]76pub fn from_corners(p0: IVec2, p1: IVec2) -> 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.92///93/// # Examples94///95/// ```96/// # use bevy_math::{IRect, IVec2};97/// let r = IRect::from_center_size(IVec2::ZERO, IVec2::new(3, 2)); // w=2 h=298/// assert_eq!(r.min, IVec2::splat(-1));99/// assert_eq!(r.max, IVec2::splat(1));100/// ```101#[inline]102pub fn from_center_size(origin: IVec2, size: IVec2) -> Self {103debug_assert!(size.cmpge(IVec2::ZERO).all(), "IRect size must be positive");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.113///114/// # Examples115///116/// ```117/// # use bevy_math::{IRect, IVec2};118/// let r = IRect::from_center_half_size(IVec2::ZERO, IVec2::ONE); // w=2 h=2119/// assert_eq!(r.min, IVec2::splat(-1));120/// assert_eq!(r.max, IVec2::splat(1));121/// ```122#[inline]123pub fn from_center_half_size(origin: IVec2, half_size: IVec2) -> Self {124assert!(125half_size.cmpge(IVec2::ZERO).all(),126"IRect half_size must be positive"127);128Self {129min: origin - half_size,130max: origin + half_size,131}132}133134/// Check if the rectangle is empty.135///136/// # Examples137///138/// ```139/// # use bevy_math::{IRect, IVec2};140/// let r = IRect::from_corners(IVec2::ZERO, IVec2::new(0, 1)); // w=0 h=1141/// assert!(r.is_empty());142/// ```143#[inline]144pub fn is_empty(&self) -> bool {145self.min.cmpge(self.max).any()146}147148/// Rectangle width (max.x - min.x).149///150/// # Examples151///152/// ```153/// # use bevy_math::IRect;154/// let r = IRect::new(0, 0, 5, 1); // w=5 h=1155/// assert_eq!(r.width(), 5);156/// ```157#[inline]158pub fn width(&self) -> i32 {159self.max.x - self.min.x160}161162/// Rectangle height (max.y - min.y).163///164/// # Examples165///166/// ```167/// # use bevy_math::IRect;168/// let r = IRect::new(0, 0, 5, 1); // w=5 h=1169/// assert_eq!(r.height(), 1);170/// ```171#[inline]172pub fn height(&self) -> i32 {173self.max.y - self.min.y174}175176/// Rectangle size.177///178/// # Examples179///180/// ```181/// # use bevy_math::{IRect, IVec2};182/// let r = IRect::new(0, 0, 5, 1); // w=5 h=1183/// assert_eq!(r.size(), IVec2::new(5, 1));184/// ```185#[inline]186pub fn size(&self) -> IVec2 {187self.max - self.min188}189190/// Rectangle half-size.191///192/// # Rounding Behavior193///194/// If the full size contains odd numbers they will be rounded down to the nearest whole number when calculating the half size.195///196/// # Examples197///198/// ```199/// # use bevy_math::{IRect, IVec2};200/// let r = IRect::new(0, 0, 4, 3); // w=4 h=3201/// assert_eq!(r.half_size(), IVec2::new(2, 1));202/// ```203#[inline]204pub fn half_size(&self) -> IVec2 {205self.size() / 2206}207208/// The center point of the rectangle.209///210/// # Rounding Behavior211///212/// If the (min + max) contains odd numbers they will be rounded down to the nearest whole number when calculating the center.213///214/// # Examples215///216/// ```217/// # use bevy_math::{IRect, IVec2};218/// let r = IRect::new(0, 0, 5, 2); // w=5 h=2219/// assert_eq!(r.center(), IVec2::new(2, 1));220/// ```221#[inline]222pub fn center(&self) -> IVec2 {223(self.min + self.max) / 2224}225226/// Check if a point lies within this rectangle, inclusive of its edges.227///228/// # Examples229///230/// ```231/// # use bevy_math::IRect;232/// let r = IRect::new(0, 0, 5, 1); // w=5 h=1233/// assert!(r.contains(r.center()));234/// assert!(r.contains(r.min));235/// assert!(r.contains(r.max));236/// ```237#[inline]238pub fn contains(&self, point: IVec2) -> bool {239(point.cmpge(self.min) & point.cmple(self.max)).all()240}241242/// Build a new rectangle formed of the union of this rectangle and another rectangle.243///244/// The union is the smallest rectangle enclosing both rectangles.245///246/// # Examples247///248/// ```249/// # use bevy_math::{IRect, IVec2};250/// let r1 = IRect::new(0, 0, 5, 1); // w=5 h=1251/// let r2 = IRect::new(1, -1, 3, 3); // w=2 h=4252/// let r = r1.union(r2);253/// assert_eq!(r.min, IVec2::new(0, -1));254/// assert_eq!(r.max, IVec2::new(5, 3));255/// ```256#[inline]257pub fn union(&self, other: Self) -> Self {258Self {259min: self.min.min(other.min),260max: self.max.max(other.max),261}262}263264/// Build a new rectangle formed of the union of this rectangle and a point.265///266/// The union is the smallest rectangle enclosing both the rectangle and the point. If the267/// point is already inside the rectangle, this method returns a copy of the rectangle.268///269/// # Examples270///271/// ```272/// # use bevy_math::{IRect, IVec2};273/// let r = IRect::new(0, 0, 5, 1); // w=5 h=1274/// let u = r.union_point(IVec2::new(3, 6));275/// assert_eq!(u.min, IVec2::ZERO);276/// assert_eq!(u.max, IVec2::new(5, 6));277/// ```278#[inline]279pub fn union_point(&self, other: IVec2) -> Self {280Self {281min: self.min.min(other),282max: self.max.max(other),283}284}285286/// Build a new rectangle formed of the intersection of this rectangle and another rectangle.287///288/// The intersection is the largest rectangle enclosed in both rectangles. If the intersection289/// is empty, this method returns an empty rectangle ([`IRect::is_empty()`] returns `true`), but290/// the actual values of [`IRect::min`] and [`IRect::max`] are implementation-dependent.291///292/// # Examples293///294/// ```295/// # use bevy_math::{IRect, IVec2};296/// let r1 = IRect::new(0, 0, 5, 1); // w=5 h=1297/// let r2 = IRect::new(1, -1, 3, 3); // w=2 h=4298/// let r = r1.intersect(r2);299/// assert_eq!(r.min, IVec2::new(1, 0));300/// assert_eq!(r.max, IVec2::new(3, 1));301/// ```302#[inline]303pub fn intersect(&self, other: Self) -> Self {304let mut r = Self {305min: self.min.max(other.min),306max: self.max.min(other.max),307};308// Collapse min over max to enforce invariants and ensure e.g. width() or309// height() never return a negative value.310r.min = r.min.min(r.max);311r312}313314/// Create a new rectangle by expanding it evenly on all sides.315///316/// A positive expansion value produces a larger rectangle,317/// while a negative expansion value produces a smaller rectangle.318/// If this would result in zero or negative width or height, [`IRect::EMPTY`] is returned instead.319///320/// # Examples321///322/// ```323/// # use bevy_math::{IRect, IVec2};324/// let r = IRect::new(0, 0, 5, 1); // w=5 h=1325/// let r2 = r.inflate(3); // w=11 h=7326/// assert_eq!(r2.min, IVec2::splat(-3));327/// assert_eq!(r2.max, IVec2::new(8, 4));328///329/// let r = IRect::new(0, -1, 4, 3); // w=4 h=4330/// let r2 = r.inflate(-1); // w=2 h=2331/// assert_eq!(r2.min, IVec2::new(1, 0));332/// assert_eq!(r2.max, IVec2::new(3, 2));333/// ```334#[inline]335pub fn inflate(&self, expansion: i32) -> Self {336let mut r = Self {337min: self.min - expansion,338max: self.max + expansion,339};340// Collapse min over max to enforce invariants and ensure e.g. width() or341// height() never return a negative value.342r.min = r.min.min(r.max);343r344}345346/// Returns self as [`Rect`] (f32)347#[inline]348pub fn as_rect(&self) -> Rect {349Rect::from_corners(self.min.as_vec2(), self.max.as_vec2())350}351352/// Returns self as [`URect`] (u32)353#[inline]354pub fn as_urect(&self) -> URect {355URect::from_corners(self.min.as_uvec2(), self.max.as_uvec2())356}357}358359#[cfg(test)]360mod tests {361use super::*;362363#[test]364fn well_formed() {365let r = IRect::from_center_size(IVec2::new(3, -5), IVec2::new(8, 12));366367assert_eq!(r.min, IVec2::new(-1, -11));368assert_eq!(r.max, IVec2::new(7, 1));369370assert_eq!(r.center(), IVec2::new(3, -5));371372assert_eq!(r.width().abs(), 8);373assert_eq!(r.height().abs(), 12);374assert_eq!(r.size(), IVec2::new(8, 12));375assert_eq!(r.half_size(), IVec2::new(4, 6));376377assert!(r.contains(IVec2::new(3, -5)));378assert!(r.contains(IVec2::new(-1, -10)));379assert!(r.contains(IVec2::new(-1, 0)));380assert!(r.contains(IVec2::new(7, -10)));381assert!(r.contains(IVec2::new(7, 0)));382assert!(!r.contains(IVec2::new(50, -5)));383}384385#[test]386fn rect_union() {387let r = IRect::from_center_size(IVec2::ZERO, IVec2::splat(4)); // [-2, -2] - [2, 2]388389// overlapping390let r2 = IRect {391min: IVec2::new(1, 1),392max: IVec2::new(3, 3),393};394let u = r.union(r2);395assert_eq!(u.min, IVec2::new(-2, -2));396assert_eq!(u.max, IVec2::new(3, 3));397398// disjoint399let r2 = IRect {400min: IVec2::new(1, 4),401max: IVec2::new(4, 6),402};403let u = r.union(r2);404assert_eq!(u.min, IVec2::new(-2, -2));405assert_eq!(u.max, IVec2::new(4, 6));406407// included408let r2 = IRect::from_center_size(IVec2::ZERO, IVec2::splat(2));409let u = r.union(r2);410assert_eq!(u.min, r.min);411assert_eq!(u.max, r.max);412413// including414let r2 = IRect::from_center_size(IVec2::ZERO, IVec2::splat(6));415let u = r.union(r2);416assert_eq!(u.min, r2.min);417assert_eq!(u.min, r2.min);418}419420#[test]421fn rect_union_pt() {422let r = IRect::from_center_size(IVec2::ZERO, IVec2::splat(4)); // [-2,-2] - [2,2]423424// inside425let v = IVec2::new(1, -1);426let u = r.union_point(v);427assert_eq!(u.min, r.min);428assert_eq!(u.max, r.max);429430// outside431let v = IVec2::new(10, -3);432let u = r.union_point(v);433assert_eq!(u.min, IVec2::new(-2, -3));434assert_eq!(u.max, IVec2::new(10, 2));435}436437#[test]438fn rect_intersect() {439let r = IRect::from_center_size(IVec2::ZERO, IVec2::splat(8)); // [-4,-4] - [4,4]440441// overlapping442let r2 = IRect {443min: IVec2::new(2, 2),444max: IVec2::new(6, 6),445};446let u = r.intersect(r2);447assert_eq!(u.min, IVec2::new(2, 2));448assert_eq!(u.max, IVec2::new(4, 4));449450// disjoint451let r2 = IRect {452min: IVec2::new(-8, -2),453max: IVec2::new(-6, 2),454};455let u = r.intersect(r2);456assert!(u.is_empty());457assert_eq!(u.width(), 0);458459// included460let r2 = IRect::from_center_size(IVec2::ZERO, IVec2::splat(2));461let u = r.intersect(r2);462assert_eq!(u.min, r2.min);463assert_eq!(u.max, r2.max);464465// including466let r2 = IRect::from_center_size(IVec2::ZERO, IVec2::splat(10));467let u = r.intersect(r2);468assert_eq!(u.min, r.min);469assert_eq!(u.max, r.max);470}471472#[test]473fn rect_inflate() {474let r = IRect::from_center_size(IVec2::ZERO, IVec2::splat(4)); // [-2,-2] - [2,2]475476let r2 = r.inflate(2);477assert_eq!(r2.min, IVec2::new(-4, -4));478assert_eq!(r2.max, IVec2::new(4, 4));479}480}481482483