diff --git a/crates/bevy_math/Cargo.toml b/crates/bevy_math/Cargo.toml index be5c06db0ac938..689392fbe53ffe 100644 --- a/crates/bevy_math/Cargo.toml +++ b/crates/bevy_math/Cargo.toml @@ -10,3 +10,4 @@ keywords = ["bevy"] [dependencies] glam = { version = "0.21", features = ["serde", "bytemuck"] } +serde = "1" diff --git a/crates/bevy_math/src/lib.rs b/crates/bevy_math/src/lib.rs index e31af6fbc03730..aa22942c43d11f 100644 --- a/crates/bevy_math/src/lib.rs +++ b/crates/bevy_math/src/lib.rs @@ -6,12 +6,16 @@ #![warn(missing_docs)] +mod rect; + +pub use rect::Rect; + /// The `bevy_math` prelude. pub mod prelude { #[doc(hidden)] pub use crate::{ - BVec2, BVec3, BVec4, EulerRot, IVec2, IVec3, IVec4, Mat2, Mat3, Mat4, Quat, UVec2, UVec3, - UVec4, Vec2, Vec3, Vec4, + BVec2, BVec3, BVec4, EulerRot, IVec2, IVec3, IVec4, Mat2, Mat3, Mat4, Quat, Rect, UVec2, + UVec3, UVec4, Vec2, Vec3, Vec4, }; } diff --git a/crates/bevy_math/src/rect.rs b/crates/bevy_math/src/rect.rs new file mode 100644 index 00000000000000..2674dce65e637b --- /dev/null +++ b/crates/bevy_math/src/rect.rs @@ -0,0 +1,426 @@ +use crate::Vec2; +use serde::{Deserialize, Serialize}; + +/// A rectangle defined by two opposite corners. +/// +/// The rectangle is axis aligned, and defined by its minimum and maximum coordinates, +/// stored in `Rect::min` and `Rect::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, Serialize, Deserialize, PartialEq)] +pub struct Rect { + /// The minimum corner point of the rect. + pub min: Vec2, + /// The maximum corner point of the rect. + pub max: Vec2, +} + +impl Rect { + /// 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::Rect; + /// let r = Rect::new(0., 4., 10., 6.); // w=10 h=2 + /// let r = Rect::new(2., 3., 5., -1.); // w=3 h=4 + /// ``` + #[inline] + pub fn new(x0: f32, y0: f32, x1: f32, y1: f32) -> Self { + Self::from_corners(Vec2::new(x0, y0), Vec2::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::{Rect, Vec2}; + /// // Unit rect from [0,0] to [1,1] + /// let r = Rect::from_corners(Vec2::ZERO, Vec2::ONE); // w=1 h=1 + /// // Same; the points do not need to be ordered + /// let r = Rect::from_corners(Vec2::ONE, Vec2::ZERO); // w=1 h=1 + /// ``` + #[inline] + pub fn from_corners(p0: Vec2, p1: Vec2) -> Self { + Rect { + min: p0.min(p1), + max: p0.max(p1), + } + } + + /// Create a new rectangle from its center and size. + /// + /// # Panics + /// + /// This method panics if any of the components of the size is negative. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_math::{Rect, Vec2}; + /// let r = Rect::from_center_size(Vec2::ZERO, Vec2::ONE); // w=1 h=1 + /// assert!(r.min.abs_diff_eq(Vec2::splat(-0.5), 1e-5)); + /// assert!(r.max.abs_diff_eq(Vec2::splat(0.5), 1e-5)); + /// ``` + #[inline] + pub fn from_center_size(origin: Vec2, size: Vec2) -> Self { + assert!(size.cmpge(Vec2::ZERO).all()); + 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::{Rect, Vec2}; + /// let r = Rect::from_center_half_size(Vec2::ZERO, Vec2::ONE); // w=2 h=2 + /// assert!(r.min.abs_diff_eq(Vec2::splat(-1.), 1e-5)); + /// assert!(r.max.abs_diff_eq(Vec2::splat(1.), 1e-5)); + /// ``` + #[inline] + pub fn from_center_half_size(origin: Vec2, half_size: Vec2) -> Self { + assert!(half_size.cmpge(Vec2::ZERO).all()); + Self { + min: origin - half_size, + max: origin + half_size, + } + } + + /// Check if the rectangle is empty. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_math::{Rect, Vec2}; + /// let r = Rect::from_corners(Vec2::ZERO, Vec2::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::Rect; + /// let r = Rect::new(0., 0., 5., 1.); // w=5 h=1 + /// assert!((r.width() - 5.).abs() <= 1e-5); + /// ``` + #[inline] + pub fn width(&self) -> f32 { + self.max.x - self.min.x + } + + /// Rectangle height (max.y - min.y). + /// + /// # Examples + /// + /// ```rust + /// # use bevy_math::Rect; + /// let r = Rect::new(0., 0., 5., 1.); // w=5 h=1 + /// assert!((r.height() - 1.).abs() <= 1e-5); + /// ``` + #[inline] + pub fn height(&self) -> f32 { + self.max.y - self.min.y + } + + /// Rectangle size. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_math::{Rect, Vec2}; + /// let r = Rect::new(0., 0., 5., 1.); // w=5 h=1 + /// assert!(r.size().abs_diff_eq(Vec2::new(5., 1.), 1e-5)); + /// ``` + #[inline] + pub fn size(&self) -> Vec2 { + self.max - self.min + } + + /// Rectangle half-size. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_math::{Rect, Vec2}; + /// let r = Rect::new(0., 0., 5., 1.); // w=5 h=1 + /// assert!(r.half_size().abs_diff_eq(Vec2::new(2.5, 0.5), 1e-5)); + /// ``` + #[inline] + pub fn half_size(&self) -> Vec2 { + self.size() * 0.5 + } + + /// The center point of the rectangle. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_math::{Rect, Vec2}; + /// let r = Rect::new(0., 0., 5., 1.); // w=5 h=1 + /// assert!(r.center().abs_diff_eq(Vec2::new(2.5, 0.5), 1e-5)); + /// ``` + #[inline] + pub fn center(&self) -> Vec2 { + (self.min + self.max) * 0.5 + } + + /// Check if a point lies within this rectangle, inclusive of its edges. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_math::Rect; + /// let r = Rect::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: Vec2) -> 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::{Rect, Vec2}; + /// let r1 = Rect::new(0., 0., 5., 1.); // w=5 h=1 + /// let r2 = Rect::new(1., -1., 3., 3.); // w=2 h=4 + /// let r = r1.union(r2); + /// assert!(r.min.abs_diff_eq(Vec2::new(0., -1.), 1e-5)); + /// assert!(r.max.abs_diff_eq(Vec2::new(5., 3.), 1e-5)); + /// ``` + #[inline] + pub fn union(&self, other: Rect) -> Rect { + Rect { + 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::{Rect, Vec2}; + /// let r = Rect::new(0., 0., 5., 1.); // w=5 h=1 + /// let u = r.union_point(Vec2::new(3., 6.)); + /// assert!(u.min.abs_diff_eq(Vec2::ZERO, 1e-5)); + /// assert!(u.max.abs_diff_eq(Vec2::new(5., 6.), 1e-5)); + /// ``` + #[inline] + pub fn union_point(&self, other: Vec2) -> Rect { + Rect { + 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 ([`Rect::is_empty()`] returns `true`), but + /// the actual values of [`Rect::min`] and [`Rect::max`] are implementation-dependent. + /// + /// # Examples + /// + /// ```rust + /// # use bevy_math::{Rect, Vec2}; + /// let r1 = Rect::new(0., 0., 5., 1.); // w=5 h=1 + /// let r2 = Rect::new(1., -1., 3., 3.); // w=2 h=4 + /// let r = r1.intersect(r2); + /// assert!(r.min.abs_diff_eq(Vec2::new(1., 0.), 1e-5)); + /// assert!(r.max.abs_diff_eq(Vec2::new(3., 1.), 1e-5)); + /// ``` + #[inline] + pub fn intersect(&self, other: Rect) -> Rect { + let mut r = Rect { + 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::{Rect, Vec2}; + /// let r = Rect::new(0., 0., 5., 1.); // w=5 h=1 + /// let r2 = r.inset(3.); // w=11 h=7 + /// assert!(r2.min.abs_diff_eq(Vec2::splat(-3.), 1e-5)); + /// assert!(r2.max.abs_diff_eq(Vec2::new(8., 4.), 1e-5)); + /// ``` + #[inline] + pub fn inset(&self, inset: f32) -> Rect { + let mut r = Rect { + 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 + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn well_formed() { + let r = Rect::from_center_size(Vec2::new(3., -5.), Vec2::new(8., 11.)); + + assert!(r.min.abs_diff_eq(Vec2::new(-1., -10.5), 1e-5)); + assert!(r.max.abs_diff_eq(Vec2::new(7., 0.5), 1e-5)); + + assert!(r.center().abs_diff_eq(Vec2::new(3., -5.), 1e-5)); + + assert!((r.width() - 8.).abs() <= 1e-5); + assert!((r.height() - 11.).abs() <= 1e-5); + assert!(r.size().abs_diff_eq(Vec2::new(8., 11.), 1e-5)); + assert!(r.half_size().abs_diff_eq(Vec2::new(4., 5.5), 1e-5)); + + assert!(r.contains(Vec2::new(3., -5.))); + assert!(r.contains(Vec2::new(-1., -10.5))); + assert!(r.contains(Vec2::new(-1., 0.5))); + assert!(r.contains(Vec2::new(7., -10.5))); + assert!(r.contains(Vec2::new(7., 0.5))); + assert!(!r.contains(Vec2::new(50., -5.))); + } + + #[test] + fn rect_union() { + let r = Rect::from_center_size(Vec2::ZERO, Vec2::ONE); // [-0.5,-0.5] - [0.5,0.5] + + // overlapping + let r2 = Rect { + min: Vec2::new(-0.8, 0.3), + max: Vec2::new(0.1, 0.7), + }; + let u = r.union(r2); + assert!(u.min.abs_diff_eq(Vec2::new(-0.8, -0.5), 1e-5)); + assert!(u.max.abs_diff_eq(Vec2::new(0.5, 0.7), 1e-5)); + + // disjoint + let r2 = Rect { + min: Vec2::new(-1.8, -0.5), + max: Vec2::new(-1.5, 0.3), + }; + let u = r.union(r2); + assert!(u.min.abs_diff_eq(Vec2::new(-1.8, -0.5), 1e-5)); + assert!(u.max.abs_diff_eq(Vec2::new(0.5, 0.5), 1e-5)); + + // included + let r2 = Rect::from_center_size(Vec2::ZERO, Vec2::splat(0.5)); + let u = r.union(r2); + assert!(u.min.abs_diff_eq(r.min, 1e-5)); + assert!(u.max.abs_diff_eq(r.max, 1e-5)); + + // including + let r2 = Rect::from_center_size(Vec2::ZERO, Vec2::splat(1.5)); + let u = r.union(r2); + assert!(u.min.abs_diff_eq(r2.min, 1e-5)); + assert!(u.max.abs_diff_eq(r2.max, 1e-5)); + } + + #[test] + fn rect_union_pt() { + let r = Rect::from_center_size(Vec2::ZERO, Vec2::ONE); // [-0.5,-0.5] - [0.5,0.5] + + // inside + let v = Vec2::new(0.3, -0.2); + let u = r.union_point(v); + assert!(u.min.abs_diff_eq(r.min, 1e-5)); + assert!(u.max.abs_diff_eq(r.max, 1e-5)); + + // outside + let v = Vec2::new(10., -3.); + let u = r.union_point(v); + assert!(u.min.abs_diff_eq(Vec2::new(-0.5, -3.), 1e-5)); + assert!(u.max.abs_diff_eq(Vec2::new(10., 0.5), 1e-5)); + } + + #[test] + fn rect_intersect() { + let r = Rect::from_center_size(Vec2::ZERO, Vec2::ONE); // [-0.5,-0.5] - [0.5,0.5] + + // overlapping + let r2 = Rect { + min: Vec2::new(-0.8, 0.3), + max: Vec2::new(0.1, 0.7), + }; + let u = r.intersect(r2); + assert!(u.min.abs_diff_eq(Vec2::new(-0.5, 0.3), 1e-5)); + assert!(u.max.abs_diff_eq(Vec2::new(0.1, 0.5), 1e-5)); + + // disjoint + let r2 = Rect { + min: Vec2::new(-1.8, -0.5), + max: Vec2::new(-1.5, 0.3), + }; + let u = r.intersect(r2); + assert!(u.is_empty()); + assert!(u.width() <= 1e-5); + + // included + let r2 = Rect::from_center_size(Vec2::ZERO, Vec2::splat(0.5)); + let u = r.intersect(r2); + assert!(u.min.abs_diff_eq(r2.min, 1e-5)); + assert!(u.max.abs_diff_eq(r2.max, 1e-5)); + + // including + let r2 = Rect::from_center_size(Vec2::ZERO, Vec2::splat(1.5)); + let u = r.intersect(r2); + assert!(u.min.abs_diff_eq(r.min, 1e-5)); + assert!(u.max.abs_diff_eq(r.max, 1e-5)); + } + + #[test] + fn rect_inset() { + let r = Rect::from_center_size(Vec2::ZERO, Vec2::ONE); // [-0.5,-0.5] - [0.5,0.5] + + let r2 = r.inset(0.3); + assert!(r2.min.abs_diff_eq(Vec2::new(-0.8, -0.8), 1e-5)); + assert!(r2.max.abs_diff_eq(Vec2::new(0.8, 0.8), 1e-5)); + } +} diff --git a/crates/bevy_reflect/Cargo.toml b/crates/bevy_reflect/Cargo.toml index 9cd65e2ca91ac2..b2c0e4d8a070cb 100644 --- a/crates/bevy_reflect/Cargo.toml +++ b/crates/bevy_reflect/Cargo.toml @@ -10,10 +10,11 @@ keywords = ["bevy"] readme = "README.md" [features] -bevy = ["glam", "smallvec"] +bevy = ["glam", "smallvec", "bevy_math"] [dependencies] # bevy +bevy_math = { path = "../bevy_math", version = "0.9.0-dev", optional = true } bevy_reflect_derive = { path = "bevy_reflect_derive", version = "0.9.0-dev" } bevy_utils = { path = "../bevy_utils", version = "0.9.0-dev" } bevy_ptr = { path = "../bevy_ptr", version = "0.9.0-dev" } diff --git a/crates/bevy_reflect/src/impls/rect.rs b/crates/bevy_reflect/src/impls/rect.rs new file mode 100644 index 00000000000000..f134e9cfe0627d --- /dev/null +++ b/crates/bevy_reflect/src/impls/rect.rs @@ -0,0 +1,14 @@ +use crate as bevy_reflect; +use crate::prelude::ReflectDefault; +use crate::reflect::Reflect; +use crate::{ReflectDeserialize, ReflectSerialize}; +use bevy_math::{Rect, Vec2}; +use bevy_reflect_derive::impl_reflect_struct; + +impl_reflect_struct!( + #[reflect(Debug, PartialEq, Serialize, Deserialize, Default)] + struct Rect { + min: Vec2, + max: Vec2, + } +); diff --git a/crates/bevy_reflect/src/lib.rs b/crates/bevy_reflect/src/lib.rs index 5f35d27e4e5a35..8c1941653f9156 100644 --- a/crates/bevy_reflect/src/lib.rs +++ b/crates/bevy_reflect/src/lib.rs @@ -15,12 +15,16 @@ mod type_uuid; mod impls { #[cfg(feature = "glam")] mod glam; + #[cfg(feature = "bevy_math")] + mod rect; #[cfg(feature = "smallvec")] mod smallvec; mod std; #[cfg(feature = "glam")] pub use self::glam::*; + #[cfg(feature = "bevy_math")] + pub use self::rect::*; #[cfg(feature = "smallvec")] pub use self::smallvec::*; pub use self::std::*; diff --git a/crates/bevy_sprite/src/dynamic_texture_atlas_builder.rs b/crates/bevy_sprite/src/dynamic_texture_atlas_builder.rs index 2cdf99a1710f27..1155e0c5e23ce2 100644 --- a/crates/bevy_sprite/src/dynamic_texture_atlas_builder.rs +++ b/crates/bevy_sprite/src/dynamic_texture_atlas_builder.rs @@ -1,6 +1,6 @@ -use crate::{Rect, TextureAtlas}; +use crate::TextureAtlas; use bevy_asset::Assets; -use bevy_math::Vec2; +use bevy_math::{IVec2, Rect, Vec2}; use bevy_render::texture::{Image, TextureFormatPixelInfo}; use guillotiere::{size2, Allocation, AtlasAllocator}; @@ -30,9 +30,8 @@ impl DynamicTextureAtlasBuilder { if let Some(allocation) = allocation { let atlas_texture = textures.get_mut(&texture_atlas.texture).unwrap(); self.place_texture(atlas_texture, allocation, texture); - let mut rect: Rect = allocation.rectangle.into(); - rect.max.x -= self.padding as f32; - rect.max.y -= self.padding as f32; + let mut rect: Rect = to_rect(allocation.rectangle); + rect.max -= self.padding as f32; Some(texture_atlas.add_texture(rect)) } else { None @@ -86,12 +85,10 @@ impl DynamicTextureAtlasBuilder { } } -impl From for Rect { - fn from(rectangle: guillotiere::Rectangle) -> Self { - Rect { - min: Vec2::new(rectangle.min.x as f32, rectangle.min.y as f32), - max: Vec2::new(rectangle.max.x as f32, rectangle.max.y as f32), - } +fn to_rect(rectangle: guillotiere::Rectangle) -> Rect { + Rect { + min: IVec2::new(rectangle.min.x, rectangle.min.y).as_vec2(), + max: IVec2::new(rectangle.max.x, rectangle.max.y).as_vec2(), } } diff --git a/crates/bevy_sprite/src/lib.rs b/crates/bevy_sprite/src/lib.rs index bf0da021b8941a..2ef33d6ccc4d1b 100644 --- a/crates/bevy_sprite/src/lib.rs +++ b/crates/bevy_sprite/src/lib.rs @@ -1,7 +1,6 @@ mod bundle; mod dynamic_texture_atlas_builder; mod mesh2d; -mod rect; mod render; mod sprite; mod texture_atlas; @@ -22,7 +21,6 @@ pub mod prelude { pub use bundle::*; pub use dynamic_texture_atlas_builder::*; pub use mesh2d::*; -pub use rect::*; pub use render::*; pub use sprite::*; pub use texture_atlas::*; diff --git a/crates/bevy_sprite/src/rect.rs b/crates/bevy_sprite/src/rect.rs deleted file mode 100644 index 3a7fb2a04dd26c..00000000000000 --- a/crates/bevy_sprite/src/rect.rs +++ /dev/null @@ -1,27 +0,0 @@ -use bevy_math::Vec2; -use bevy_reflect::Reflect; - -/// A rectangle defined by two points. There is no defined origin, so 0,0 could be anywhere -/// (top-left, bottom-left, etc) -#[repr(C)] -#[derive(Default, Clone, Copy, Debug, Reflect)] -pub struct Rect { - /// The beginning point of the rect - pub min: Vec2, - /// The ending point of the rect - pub max: Vec2, -} - -impl Rect { - pub fn width(&self) -> f32 { - self.max.x - self.min.x - } - - pub fn height(&self) -> f32 { - self.max.y - self.min.y - } - - pub fn size(&self) -> Vec2 { - Vec2::new(self.width(), self.height()) - } -} diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index db1dc1a86d7539..70ccd029e2d9a6 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -2,7 +2,7 @@ use std::cmp::Ordering; use crate::{ texture_atlas::{TextureAtlas, TextureAtlasSprite}, - Rect, Sprite, SPRITE_SHADER_HANDLE, + Sprite, SPRITE_SHADER_HANDLE, }; use bevy_asset::{AssetEvent, Assets, Handle, HandleId}; use bevy_core_pipeline::core_2d::Transparent2d; @@ -10,7 +10,7 @@ use bevy_ecs::{ prelude::*, system::{lifetimeless::*, SystemParamItem}, }; -use bevy_math::Vec2; +use bevy_math::{Rect, Vec2}; use bevy_reflect::Uuid; use bevy_render::{ color::Color, diff --git a/crates/bevy_sprite/src/texture_atlas.rs b/crates/bevy_sprite/src/texture_atlas.rs index bc3cea9400f874..ca8e38a0de2b85 100644 --- a/crates/bevy_sprite/src/texture_atlas.rs +++ b/crates/bevy_sprite/src/texture_atlas.rs @@ -1,7 +1,7 @@ -use crate::{Anchor, Rect}; +use crate::Anchor; use bevy_asset::Handle; use bevy_ecs::component::Component; -use bevy_math::Vec2; +use bevy_math::{Rect, Vec2}; use bevy_reflect::{Reflect, TypeUuid}; use bevy_render::{color::Color, texture::Image}; use bevy_utils::HashMap; @@ -91,35 +91,32 @@ impl TextureAtlas { offset: Vec2, ) -> TextureAtlas { let mut sprites = Vec::new(); - let mut x_padding = 0.0; - let mut y_padding = 0.0; + let mut current_padding = Vec2::ZERO; for y in 0..rows { if y > 0 { - y_padding = padding.y; + current_padding.y = padding.y; } for x in 0..columns { if x > 0 { - x_padding = padding.x; + current_padding.x = padding.x; } - let rect_min = Vec2::new( - (tile_size.x + x_padding) * x as f32 + offset.x, - (tile_size.y + y_padding) * y as f32 + offset.y, - ); + let cell = Vec2::new(x as f32, y as f32); + + let rect_min = (tile_size + current_padding) * cell + offset; sprites.push(Rect { min: rect_min, - max: Vec2::new(rect_min.x + tile_size.x, rect_min.y + tile_size.y), + max: rect_min + tile_size, }); } } + let grid_size = Vec2::new(columns as f32, rows as f32); + TextureAtlas { - size: Vec2::new( - ((tile_size.x + x_padding) * columns as f32) - x_padding, - ((tile_size.y + y_padding) * rows as f32) - y_padding, - ), + size: ((tile_size + current_padding) * grid_size) - current_padding, textures: sprites, texture, texture_handles: None, diff --git a/crates/bevy_sprite/src/texture_atlas_builder.rs b/crates/bevy_sprite/src/texture_atlas_builder.rs index 7aa87141be93a2..f77e0cce90c313 100644 --- a/crates/bevy_sprite/src/texture_atlas_builder.rs +++ b/crates/bevy_sprite/src/texture_atlas_builder.rs @@ -1,6 +1,6 @@ use bevy_asset::{Assets, Handle}; use bevy_log::{debug, error, warn}; -use bevy_math::Vec2; +use bevy_math::{Rect, Vec2}; use bevy_render::{ render_resource::{Extent3d, TextureDimension, TextureFormat}, texture::{Image, TextureFormatPixelInfo}, @@ -12,7 +12,7 @@ use rectangle_pack::{ }; use thiserror::Error; -use crate::{texture_atlas::TextureAtlas, Rect}; +use crate::texture_atlas::TextureAtlas; #[derive(Debug, Error)] pub enum TextureAtlasBuilderError { diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 6bc0abee94ec14..4c3f602f54b2dd 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -9,7 +9,7 @@ use crate::{prelude::UiCameraConfig, CalculatedClip, Node, UiColor, UiImage}; use bevy_app::prelude::*; use bevy_asset::{load_internal_asset, AssetEvent, Assets, Handle, HandleUntyped}; use bevy_ecs::prelude::*; -use bevy_math::{Mat4, Vec2, Vec3, Vec4Swizzles}; +use bevy_math::{Mat4, Rect, Vec2, Vec3, Vec4Swizzles}; use bevy_reflect::TypeUuid; use bevy_render::{ camera::{Camera, CameraProjection, OrthographicProjection, WindowOrigin}, @@ -23,7 +23,7 @@ use bevy_render::{ view::{ComputedVisibility, ExtractedView, ViewUniforms}, Extract, RenderApp, RenderStage, }; -use bevy_sprite::{Rect, SpriteAssetEvents, TextureAtlas}; +use bevy_sprite::{SpriteAssetEvents, TextureAtlas}; use bevy_text::{DefaultTextPipeline, Text}; use bevy_transform::components::GlobalTransform; use bevy_utils::FloatOrd; @@ -204,7 +204,7 @@ pub fn extract_uinodes( extracted_uinodes.uinodes.push(ExtractedUiNode { transform: transform.compute_matrix(), color: color.0, - rect: bevy_sprite::Rect { + rect: Rect { min: Vec2::ZERO, max: uinode.size, }, diff --git a/crates/bevy_ui/src/ui_node.rs b/crates/bevy_ui/src/ui_node.rs index e741550db06e24..728c1c597d5bbd 100644 --- a/crates/bevy_ui/src/ui_node.rs +++ b/crates/bevy_ui/src/ui_node.rs @@ -2,7 +2,7 @@ use crate::{Size, UiRect}; use bevy_asset::Handle; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{prelude::Component, reflect::ReflectComponent}; -use bevy_math::Vec2; +use bevy_math::{Rect, Vec2}; use bevy_reflect::prelude::*; use bevy_render::{ color::Color, @@ -408,5 +408,5 @@ impl From> for UiImage { #[reflect(Component)] pub struct CalculatedClip { /// The rect of the clip - pub clip: bevy_sprite::Rect, + pub clip: Rect, } diff --git a/crates/bevy_ui/src/update.rs b/crates/bevy_ui/src/update.rs index b0cf91aa6abb33..62154aea0946ae 100644 --- a/crates/bevy_ui/src/update.rs +++ b/crates/bevy_ui/src/update.rs @@ -9,8 +9,7 @@ use bevy_ecs::{ system::{Commands, Query}, }; use bevy_hierarchy::{Children, Parent}; -use bevy_math::Vec2; -use bevy_sprite::Rect; +use bevy_math::Rect; use bevy_transform::components::{GlobalTransform, Transform}; /// The resolution of `Z` values for UI @@ -109,18 +108,8 @@ fn update_clipping( Overflow::Visible => clip, Overflow::Hidden => { let node_center = global_transform.translation().truncate(); - let node_rect = Rect { - min: node_center - node.size / 2., - max: node_center + node.size / 2., - }; - if let Some(clip) = clip { - Some(Rect { - min: Vec2::max(clip.min, node_rect.min), - max: Vec2::min(clip.max, node_rect.max), - }) - } else { - Some(node_rect) - } + let node_rect = Rect::from_center_size(node_center, node.size); + Some(clip.map_or(node_rect, |c| c.intersect(node_rect))) } };