use crate::{IRect, URect, Vec2};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 `Rect::min` and `Rect::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)]17#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]18#[cfg_attr(19feature = "bevy_reflect",20derive(Reflect),21reflect(Debug, PartialEq, Default, Clone)22)]23#[cfg_attr(24all(feature = "serialize", feature = "bevy_reflect"),25reflect(Serialize, Deserialize)26)]27pub struct Rect {28/// The minimum corner point of the rect.29pub min: Vec2,30/// The maximum corner point of the rect.31pub max: Vec2,32}3334impl Rect {35/// An empty `Rect`, represented by maximum and minimum corner points36/// at `Vec2::NEG_INFINITY` and `Vec2::INFINITY`, respectively.37/// This is so the `Rect` has a infinitely negative size.38/// This is useful, because when taking a union B of a non-empty `Rect` A and39/// this empty `Rect`, B will simply equal A.40pub const EMPTY: Self = Self {41max: Vec2::NEG_INFINITY,42min: Vec2::INFINITY,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::Rect;53/// let r = Rect::new(0., 4., 10., 6.); // w=10 h=254/// let r = Rect::new(2., 3., 5., -1.); // w=3 h=455/// ```56#[inline]57pub fn new(x0: f32, y0: f32, x1: f32, y1: f32) -> Self {58Self::from_corners(Vec2::new(x0, y0), Vec2::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::{Rect, Vec2};70/// // Unit rect from [0,0] to [1,1]71/// let r = Rect::from_corners(Vec2::ZERO, Vec2::ONE); // w=1 h=172/// // Same; the points do not need to be ordered73/// let r = Rect::from_corners(Vec2::ONE, Vec2::ZERO); // w=1 h=174/// ```75#[inline]76pub fn from_corners(p0: Vec2, p1: Vec2) -> Self {77Self {78min: p0.min(p1),79max: p0.max(p1),80}81}8283/// Create a new rectangle from its center and size.84///85/// # Panics86///87/// This method panics if any of the components of the size is negative.88///89/// # Examples90///91/// ```92/// # use bevy_math::{Rect, Vec2};93/// let r = Rect::from_center_size(Vec2::ZERO, Vec2::ONE); // w=1 h=194/// assert!(r.min.abs_diff_eq(Vec2::splat(-0.5), 1e-5));95/// assert!(r.max.abs_diff_eq(Vec2::splat(0.5), 1e-5));96/// ```97#[inline]98pub fn from_center_size(origin: Vec2, size: Vec2) -> Self {99assert!(size.cmpge(Vec2::ZERO).all(), "Rect size must be positive");100let half_size = size / 2.;101Self::from_center_half_size(origin, half_size)102}103104/// Create a new rectangle from its center and half-size.105///106/// # Panics107///108/// This method panics if any of the components of the half-size is negative.109///110/// # Examples111///112/// ```113/// # use bevy_math::{Rect, Vec2};114/// let r = Rect::from_center_half_size(Vec2::ZERO, Vec2::ONE); // w=2 h=2115/// assert!(r.min.abs_diff_eq(Vec2::splat(-1.), 1e-5));116/// assert!(r.max.abs_diff_eq(Vec2::splat(1.), 1e-5));117/// ```118#[inline]119pub fn from_center_half_size(origin: Vec2, half_size: Vec2) -> Self {120assert!(121half_size.cmpge(Vec2::ZERO).all(),122"Rect half_size must be positive"123);124Self {125min: origin - half_size,126max: origin + half_size,127}128}129130/// Check if the rectangle is empty.131///132/// # Examples133///134/// ```135/// # use bevy_math::{Rect, Vec2};136/// let r = Rect::from_corners(Vec2::ZERO, Vec2::new(0., 1.)); // w=0 h=1137/// assert!(r.is_empty());138/// ```139#[inline]140pub fn is_empty(&self) -> bool {141self.min.cmpge(self.max).any()142}143144/// Rectangle width (max.x - min.x).145///146/// # Examples147///148/// ```149/// # use bevy_math::Rect;150/// let r = Rect::new(0., 0., 5., 1.); // w=5 h=1151/// assert!((r.width() - 5.).abs() <= 1e-5);152/// ```153#[inline]154pub fn width(&self) -> f32 {155self.max.x - self.min.x156}157158/// Rectangle height (max.y - min.y).159///160/// # Examples161///162/// ```163/// # use bevy_math::Rect;164/// let r = Rect::new(0., 0., 5., 1.); // w=5 h=1165/// assert!((r.height() - 1.).abs() <= 1e-5);166/// ```167#[inline]168pub fn height(&self) -> f32 {169self.max.y - self.min.y170}171172/// Rectangle size.173///174/// # Examples175///176/// ```177/// # use bevy_math::{Rect, Vec2};178/// let r = Rect::new(0., 0., 5., 1.); // w=5 h=1179/// assert!(r.size().abs_diff_eq(Vec2::new(5., 1.), 1e-5));180/// ```181#[inline]182pub fn size(&self) -> Vec2 {183self.max - self.min184}185186/// Rectangle half-size.187///188/// # Examples189///190/// ```191/// # use bevy_math::{Rect, Vec2};192/// let r = Rect::new(0., 0., 5., 1.); // w=5 h=1193/// assert!(r.half_size().abs_diff_eq(Vec2::new(2.5, 0.5), 1e-5));194/// ```195#[inline]196pub fn half_size(&self) -> Vec2 {197self.size() * 0.5198}199200/// The center point of the rectangle.201///202/// # Examples203///204/// ```205/// # use bevy_math::{Rect, Vec2};206/// let r = Rect::new(0., 0., 5., 1.); // w=5 h=1207/// assert!(r.center().abs_diff_eq(Vec2::new(2.5, 0.5), 1e-5));208/// ```209#[inline]210pub fn center(&self) -> Vec2 {211(self.min + self.max) * 0.5212}213214/// Check if a point lies within this rectangle, inclusive of its edges.215///216/// # Examples217///218/// ```219/// # use bevy_math::Rect;220/// let r = Rect::new(0., 0., 5., 1.); // w=5 h=1221/// assert!(r.contains(r.center()));222/// assert!(r.contains(r.min));223/// assert!(r.contains(r.max));224/// ```225#[inline]226pub fn contains(&self, point: Vec2) -> bool {227(point.cmpge(self.min) & point.cmple(self.max)).all()228}229230/// Build a new rectangle formed of the union of this rectangle and another rectangle.231///232/// The union is the smallest rectangle enclosing both rectangles.233///234/// # Examples235///236/// ```237/// # use bevy_math::{Rect, Vec2};238/// let r1 = Rect::new(0., 0., 5., 1.); // w=5 h=1239/// let r2 = Rect::new(1., -1., 3., 3.); // w=2 h=4240/// let r = r1.union(r2);241/// assert!(r.min.abs_diff_eq(Vec2::new(0., -1.), 1e-5));242/// assert!(r.max.abs_diff_eq(Vec2::new(5., 3.), 1e-5));243/// ```244#[inline]245pub fn union(&self, other: Self) -> Self {246Self {247min: self.min.min(other.min),248max: self.max.max(other.max),249}250}251252/// Build a new rectangle formed of the union of this rectangle and a point.253///254/// The union is the smallest rectangle enclosing both the rectangle and the point. If the255/// point is already inside the rectangle, this method returns a copy of the rectangle.256///257/// # Examples258///259/// ```260/// # use bevy_math::{Rect, Vec2};261/// let r = Rect::new(0., 0., 5., 1.); // w=5 h=1262/// let u = r.union_point(Vec2::new(3., 6.));263/// assert!(u.min.abs_diff_eq(Vec2::ZERO, 1e-5));264/// assert!(u.max.abs_diff_eq(Vec2::new(5., 6.), 1e-5));265/// ```266#[inline]267pub fn union_point(&self, other: Vec2) -> Self {268Self {269min: self.min.min(other),270max: self.max.max(other),271}272}273274/// Build a new rectangle formed of the intersection of this rectangle and another rectangle.275///276/// The intersection is the largest rectangle enclosed in both rectangles. If the intersection277/// is empty, this method returns an empty rectangle ([`Rect::is_empty()`] returns `true`), but278/// the actual values of [`Rect::min`] and [`Rect::max`] are implementation-dependent.279///280/// # Examples281///282/// ```283/// # use bevy_math::{Rect, Vec2};284/// let r1 = Rect::new(0., 0., 5., 1.); // w=5 h=1285/// let r2 = Rect::new(1., -1., 3., 3.); // w=2 h=4286/// let r = r1.intersect(r2);287/// assert!(r.min.abs_diff_eq(Vec2::new(1., 0.), 1e-5));288/// assert!(r.max.abs_diff_eq(Vec2::new(3., 1.), 1e-5));289/// ```290#[inline]291pub fn intersect(&self, other: Self) -> Self {292let mut r = Self {293min: self.min.max(other.min),294max: self.max.min(other.max),295};296// Collapse min over max to enforce invariants and ensure e.g. width() or297// height() never return a negative value.298r.min = r.min.min(r.max);299r300}301302/// Create a new rectangle by expanding it evenly on all sides.303///304/// A positive expansion value produces a larger rectangle,305/// while a negative expansion value produces a smaller rectangle.306/// If this would result in zero or negative width or height, [`Rect::EMPTY`] is returned instead.307///308/// # Examples309///310/// ```311/// # use bevy_math::{Rect, Vec2};312/// let r = Rect::new(0., 0., 5., 1.); // w=5 h=1313/// let r2 = r.inflate(3.); // w=11 h=7314/// assert!(r2.min.abs_diff_eq(Vec2::splat(-3.), 1e-5));315/// assert!(r2.max.abs_diff_eq(Vec2::new(8., 4.), 1e-5));316///317/// let r = Rect::new(0., -1., 6., 7.); // w=6 h=8318/// let r2 = r.inflate(-2.); // w=11 h=7319/// assert!(r2.min.abs_diff_eq(Vec2::new(2., 1.), 1e-5));320/// assert!(r2.max.abs_diff_eq(Vec2::new(4., 5.), 1e-5));321/// ```322#[inline]323pub fn inflate(&self, expansion: f32) -> Self {324let mut r = Self {325min: self.min - expansion,326max: self.max + expansion,327};328// Collapse min over max to enforce invariants and ensure e.g. width() or329// height() never return a negative value.330r.min = r.min.min(r.max);331r332}333334/// Build a new rectangle from this one with its coordinates expressed335/// relative to `other` in a normalized ([0..1] x [0..1]) coordinate system.336///337/// # Examples338///339/// ```340/// # use bevy_math::{Rect, Vec2};341/// let r = Rect::new(2., 3., 4., 6.);342/// let s = Rect::new(0., 0., 10., 10.);343/// let n = r.normalize(s);344///345/// assert_eq!(n.min.x, 0.2);346/// assert_eq!(n.min.y, 0.3);347/// assert_eq!(n.max.x, 0.4);348/// assert_eq!(n.max.y, 0.6);349/// ```350pub fn normalize(&self, other: Self) -> Self {351let outer_size = other.size();352Self {353min: (self.min - other.min) / outer_size,354max: (self.max - other.min) / outer_size,355}356}357358/// Returns self as [`IRect`] (i32)359#[inline]360pub fn as_irect(&self) -> IRect {361IRect::from_corners(self.min.as_ivec2(), self.max.as_ivec2())362}363364/// Returns self as [`URect`] (u32)365#[inline]366pub fn as_urect(&self) -> URect {367URect::from_corners(self.min.as_uvec2(), self.max.as_uvec2())368}369}370371#[cfg(test)]372mod tests {373use crate::ops;374375use super::*;376377#[test]378fn well_formed() {379let r = Rect::from_center_size(Vec2::new(3., -5.), Vec2::new(8., 11.));380381assert!(r.min.abs_diff_eq(Vec2::new(-1., -10.5), 1e-5));382assert!(r.max.abs_diff_eq(Vec2::new(7., 0.5), 1e-5));383384assert!(r.center().abs_diff_eq(Vec2::new(3., -5.), 1e-5));385386assert!(ops::abs(r.width() - 8.) <= 1e-5);387assert!(ops::abs(r.height() - 11.) <= 1e-5);388assert!(r.size().abs_diff_eq(Vec2::new(8., 11.), 1e-5));389assert!(r.half_size().abs_diff_eq(Vec2::new(4., 5.5), 1e-5));390391assert!(r.contains(Vec2::new(3., -5.)));392assert!(r.contains(Vec2::new(-1., -10.5)));393assert!(r.contains(Vec2::new(-1., 0.5)));394assert!(r.contains(Vec2::new(7., -10.5)));395assert!(r.contains(Vec2::new(7., 0.5)));396assert!(!r.contains(Vec2::new(50., -5.)));397}398399#[test]400fn rect_union() {401let r = Rect::from_center_size(Vec2::ZERO, Vec2::ONE); // [-0.5,-0.5] - [0.5,0.5]402403// overlapping404let r2 = Rect {405min: Vec2::new(-0.8, 0.3),406max: Vec2::new(0.1, 0.7),407};408let u = r.union(r2);409assert!(u.min.abs_diff_eq(Vec2::new(-0.8, -0.5), 1e-5));410assert!(u.max.abs_diff_eq(Vec2::new(0.5, 0.7), 1e-5));411412// disjoint413let r2 = Rect {414min: Vec2::new(-1.8, -0.5),415max: Vec2::new(-1.5, 0.3),416};417let u = r.union(r2);418assert!(u.min.abs_diff_eq(Vec2::new(-1.8, -0.5), 1e-5));419assert!(u.max.abs_diff_eq(Vec2::new(0.5, 0.5), 1e-5));420421// included422let r2 = Rect::from_center_size(Vec2::ZERO, Vec2::splat(0.5));423let u = r.union(r2);424assert!(u.min.abs_diff_eq(r.min, 1e-5));425assert!(u.max.abs_diff_eq(r.max, 1e-5));426427// including428let r2 = Rect::from_center_size(Vec2::ZERO, Vec2::splat(1.5));429let u = r.union(r2);430assert!(u.min.abs_diff_eq(r2.min, 1e-5));431assert!(u.max.abs_diff_eq(r2.max, 1e-5));432}433434#[test]435fn rect_union_pt() {436let r = Rect::from_center_size(Vec2::ZERO, Vec2::ONE); // [-0.5,-0.5] - [0.5,0.5]437438// inside439let v = Vec2::new(0.3, -0.2);440let u = r.union_point(v);441assert!(u.min.abs_diff_eq(r.min, 1e-5));442assert!(u.max.abs_diff_eq(r.max, 1e-5));443444// outside445let v = Vec2::new(10., -3.);446let u = r.union_point(v);447assert!(u.min.abs_diff_eq(Vec2::new(-0.5, -3.), 1e-5));448assert!(u.max.abs_diff_eq(Vec2::new(10., 0.5), 1e-5));449}450451#[test]452fn rect_intersect() {453let r = Rect::from_center_size(Vec2::ZERO, Vec2::ONE); // [-0.5,-0.5] - [0.5,0.5]454455// overlapping456let r2 = Rect {457min: Vec2::new(-0.8, 0.3),458max: Vec2::new(0.1, 0.7),459};460let u = r.intersect(r2);461assert!(u.min.abs_diff_eq(Vec2::new(-0.5, 0.3), 1e-5));462assert!(u.max.abs_diff_eq(Vec2::new(0.1, 0.5), 1e-5));463464// disjoint465let r2 = Rect {466min: Vec2::new(-1.8, -0.5),467max: Vec2::new(-1.5, 0.3),468};469let u = r.intersect(r2);470assert!(u.is_empty());471assert!(u.width() <= 1e-5);472473// included474let r2 = Rect::from_center_size(Vec2::ZERO, Vec2::splat(0.5));475let u = r.intersect(r2);476assert!(u.min.abs_diff_eq(r2.min, 1e-5));477assert!(u.max.abs_diff_eq(r2.max, 1e-5));478479// including480let r2 = Rect::from_center_size(Vec2::ZERO, Vec2::splat(1.5));481let u = r.intersect(r2);482assert!(u.min.abs_diff_eq(r.min, 1e-5));483assert!(u.max.abs_diff_eq(r.max, 1e-5));484}485486#[test]487fn rect_inflate() {488let r = Rect::from_center_size(Vec2::ZERO, Vec2::ONE); // [-0.5,-0.5] - [0.5,0.5]489490let r2 = r.inflate(0.3);491assert!(r2.min.abs_diff_eq(Vec2::new(-0.8, -0.8), 1e-5));492assert!(r2.max.abs_diff_eq(Vec2::new(0.8, 0.8), 1e-5));493}494}495496497