Move sprite::Rect into bevy_math (#5686)
# Objective Promote the `Rect` utility of `sprite::Rect`, which defines a rectangle by its minimum and maximum corners, to the `bevy_math` crate to make it available as a general math type to all crates without the need to depend on the `bevy_sprite` crate. Fixes #5575 ## Solution Move `sprite::Rect` into `bevy_math` and fix all uses. Implement `Reflect` for `Rect` directly into the `bevy_reflect` crate by having `bevy_reflect` depend on `bevy_math`. This looks like a new dependency, but the `bevy_reflect` was "cheating" for other math types by directly depending on `glam` to reflect other math types, thereby giving the illusion that there was no dependency on `bevy_math`. In practice conceptually Bevy's math types are reflected into the `bevy_reflect` crate to avoid a dependency of that crate to a "lower level" utility crate like `bevy_math` (which in turn would make `bevy_reflect` be a dependency of most other crates, and increase the risk of circular dependencies). So this change simply formalizes that dependency in `Cargo.toml`. The `Rect` struct is also augmented in this change with a collection of utility methods to improve its usability. A few uses cases are updated to use those new methods, resulting is more clear and concise syntax. --- ## Changelog ### Changed - Moved the `sprite::Rect` type into `bevy_math`. ### Added - Added several utility methods to the `math::Rect` type. ## Migration Guide The `bevy::sprite::Rect` type moved to the math utility crate as `bevy::math::Rect`. You should change your imports from `use bevy::sprite::Rect` to `use bevy::math::Rect`.
This commit is contained in:
parent
5b0381655d
commit
8b7b44d839
@ -10,3 +10,4 @@ keywords = ["bevy"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
glam = { version = "0.21", features = ["serde", "bytemuck"] }
|
glam = { version = "0.21", features = ["serde", "bytemuck"] }
|
||||||
|
serde = "1"
|
||||||
|
|||||||
@ -6,12 +6,16 @@
|
|||||||
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
|
mod rect;
|
||||||
|
|
||||||
|
pub use rect::Rect;
|
||||||
|
|
||||||
/// The `bevy_math` prelude.
|
/// The `bevy_math` prelude.
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
BVec2, BVec3, BVec4, EulerRot, IVec2, IVec3, IVec4, Mat2, Mat3, Mat4, Quat, UVec2, UVec3,
|
BVec2, BVec3, BVec4, EulerRot, IVec2, IVec3, IVec4, Mat2, Mat3, Mat4, Quat, Rect, UVec2,
|
||||||
UVec4, Vec2, Vec3, Vec4,
|
UVec3, UVec4, Vec2, Vec3, Vec4,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
426
crates/bevy_math/src/rect.rs
Normal file
426
crates/bevy_math/src/rect.rs
Normal file
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -10,10 +10,11 @@ keywords = ["bevy"]
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
bevy = ["glam", "smallvec"]
|
bevy = ["glam", "smallvec", "bevy_math"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# bevy
|
# 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_reflect_derive = { path = "bevy_reflect_derive", version = "0.9.0-dev" }
|
||||||
bevy_utils = { path = "../bevy_utils", 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" }
|
bevy_ptr = { path = "../bevy_ptr", version = "0.9.0-dev" }
|
||||||
|
|||||||
14
crates/bevy_reflect/src/impls/rect.rs
Normal file
14
crates/bevy_reflect/src/impls/rect.rs
Normal file
@ -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,
|
||||||
|
}
|
||||||
|
);
|
||||||
@ -15,12 +15,16 @@ mod type_uuid;
|
|||||||
mod impls {
|
mod impls {
|
||||||
#[cfg(feature = "glam")]
|
#[cfg(feature = "glam")]
|
||||||
mod glam;
|
mod glam;
|
||||||
|
#[cfg(feature = "bevy_math")]
|
||||||
|
mod rect;
|
||||||
#[cfg(feature = "smallvec")]
|
#[cfg(feature = "smallvec")]
|
||||||
mod smallvec;
|
mod smallvec;
|
||||||
mod std;
|
mod std;
|
||||||
|
|
||||||
#[cfg(feature = "glam")]
|
#[cfg(feature = "glam")]
|
||||||
pub use self::glam::*;
|
pub use self::glam::*;
|
||||||
|
#[cfg(feature = "bevy_math")]
|
||||||
|
pub use self::rect::*;
|
||||||
#[cfg(feature = "smallvec")]
|
#[cfg(feature = "smallvec")]
|
||||||
pub use self::smallvec::*;
|
pub use self::smallvec::*;
|
||||||
pub use self::std::*;
|
pub use self::std::*;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use crate::{Rect, TextureAtlas};
|
use crate::TextureAtlas;
|
||||||
use bevy_asset::Assets;
|
use bevy_asset::Assets;
|
||||||
use bevy_math::Vec2;
|
use bevy_math::{IVec2, Rect, Vec2};
|
||||||
use bevy_render::texture::{Image, TextureFormatPixelInfo};
|
use bevy_render::texture::{Image, TextureFormatPixelInfo};
|
||||||
use guillotiere::{size2, Allocation, AtlasAllocator};
|
use guillotiere::{size2, Allocation, AtlasAllocator};
|
||||||
|
|
||||||
@ -30,9 +30,8 @@ impl DynamicTextureAtlasBuilder {
|
|||||||
if let Some(allocation) = allocation {
|
if let Some(allocation) = allocation {
|
||||||
let atlas_texture = textures.get_mut(&texture_atlas.texture).unwrap();
|
let atlas_texture = textures.get_mut(&texture_atlas.texture).unwrap();
|
||||||
self.place_texture(atlas_texture, allocation, texture);
|
self.place_texture(atlas_texture, allocation, texture);
|
||||||
let mut rect: Rect = allocation.rectangle.into();
|
let mut rect: Rect = to_rect(allocation.rectangle);
|
||||||
rect.max.x -= self.padding as f32;
|
rect.max -= self.padding as f32;
|
||||||
rect.max.y -= self.padding as f32;
|
|
||||||
Some(texture_atlas.add_texture(rect))
|
Some(texture_atlas.add_texture(rect))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -86,12 +85,10 @@ impl DynamicTextureAtlasBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<guillotiere::Rectangle> for Rect {
|
fn to_rect(rectangle: guillotiere::Rectangle) -> Rect {
|
||||||
fn from(rectangle: guillotiere::Rectangle) -> Self {
|
|
||||||
Rect {
|
Rect {
|
||||||
min: Vec2::new(rectangle.min.x as f32, rectangle.min.y as f32),
|
min: IVec2::new(rectangle.min.x, rectangle.min.y).as_vec2(),
|
||||||
max: Vec2::new(rectangle.max.x as f32, rectangle.max.y as f32),
|
max: IVec2::new(rectangle.max.x, rectangle.max.y).as_vec2(),
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
mod bundle;
|
mod bundle;
|
||||||
mod dynamic_texture_atlas_builder;
|
mod dynamic_texture_atlas_builder;
|
||||||
mod mesh2d;
|
mod mesh2d;
|
||||||
mod rect;
|
|
||||||
mod render;
|
mod render;
|
||||||
mod sprite;
|
mod sprite;
|
||||||
mod texture_atlas;
|
mod texture_atlas;
|
||||||
@ -22,7 +21,6 @@ pub mod prelude {
|
|||||||
pub use bundle::*;
|
pub use bundle::*;
|
||||||
pub use dynamic_texture_atlas_builder::*;
|
pub use dynamic_texture_atlas_builder::*;
|
||||||
pub use mesh2d::*;
|
pub use mesh2d::*;
|
||||||
pub use rect::*;
|
|
||||||
pub use render::*;
|
pub use render::*;
|
||||||
pub use sprite::*;
|
pub use sprite::*;
|
||||||
pub use texture_atlas::*;
|
pub use texture_atlas::*;
|
||||||
|
|||||||
@ -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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -2,7 +2,7 @@ use std::cmp::Ordering;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
texture_atlas::{TextureAtlas, TextureAtlasSprite},
|
texture_atlas::{TextureAtlas, TextureAtlasSprite},
|
||||||
Rect, Sprite, SPRITE_SHADER_HANDLE,
|
Sprite, SPRITE_SHADER_HANDLE,
|
||||||
};
|
};
|
||||||
use bevy_asset::{AssetEvent, Assets, Handle, HandleId};
|
use bevy_asset::{AssetEvent, Assets, Handle, HandleId};
|
||||||
use bevy_core_pipeline::core_2d::Transparent2d;
|
use bevy_core_pipeline::core_2d::Transparent2d;
|
||||||
@ -10,7 +10,7 @@ use bevy_ecs::{
|
|||||||
prelude::*,
|
prelude::*,
|
||||||
system::{lifetimeless::*, SystemParamItem},
|
system::{lifetimeless::*, SystemParamItem},
|
||||||
};
|
};
|
||||||
use bevy_math::Vec2;
|
use bevy_math::{Rect, Vec2};
|
||||||
use bevy_reflect::Uuid;
|
use bevy_reflect::Uuid;
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
color::Color,
|
color::Color,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use crate::{Anchor, Rect};
|
use crate::Anchor;
|
||||||
use bevy_asset::Handle;
|
use bevy_asset::Handle;
|
||||||
use bevy_ecs::component::Component;
|
use bevy_ecs::component::Component;
|
||||||
use bevy_math::Vec2;
|
use bevy_math::{Rect, Vec2};
|
||||||
use bevy_reflect::{Reflect, TypeUuid};
|
use bevy_reflect::{Reflect, TypeUuid};
|
||||||
use bevy_render::{color::Color, texture::Image};
|
use bevy_render::{color::Color, texture::Image};
|
||||||
use bevy_utils::HashMap;
|
use bevy_utils::HashMap;
|
||||||
@ -91,35 +91,32 @@ impl TextureAtlas {
|
|||||||
offset: Vec2,
|
offset: Vec2,
|
||||||
) -> TextureAtlas {
|
) -> TextureAtlas {
|
||||||
let mut sprites = Vec::new();
|
let mut sprites = Vec::new();
|
||||||
let mut x_padding = 0.0;
|
let mut current_padding = Vec2::ZERO;
|
||||||
let mut y_padding = 0.0;
|
|
||||||
|
|
||||||
for y in 0..rows {
|
for y in 0..rows {
|
||||||
if y > 0 {
|
if y > 0 {
|
||||||
y_padding = padding.y;
|
current_padding.y = padding.y;
|
||||||
}
|
}
|
||||||
for x in 0..columns {
|
for x in 0..columns {
|
||||||
if x > 0 {
|
if x > 0 {
|
||||||
x_padding = padding.x;
|
current_padding.x = padding.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
let rect_min = Vec2::new(
|
let cell = Vec2::new(x as f32, y as f32);
|
||||||
(tile_size.x + x_padding) * x as f32 + offset.x,
|
|
||||||
(tile_size.y + y_padding) * y as f32 + offset.y,
|
let rect_min = (tile_size + current_padding) * cell + offset;
|
||||||
);
|
|
||||||
|
|
||||||
sprites.push(Rect {
|
sprites.push(Rect {
|
||||||
min: rect_min,
|
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 {
|
TextureAtlas {
|
||||||
size: Vec2::new(
|
size: ((tile_size + current_padding) * grid_size) - current_padding,
|
||||||
((tile_size.x + x_padding) * columns as f32) - x_padding,
|
|
||||||
((tile_size.y + y_padding) * rows as f32) - y_padding,
|
|
||||||
),
|
|
||||||
textures: sprites,
|
textures: sprites,
|
||||||
texture,
|
texture,
|
||||||
texture_handles: None,
|
texture_handles: None,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
use bevy_asset::{Assets, Handle};
|
use bevy_asset::{Assets, Handle};
|
||||||
use bevy_log::{debug, error, warn};
|
use bevy_log::{debug, error, warn};
|
||||||
use bevy_math::Vec2;
|
use bevy_math::{Rect, Vec2};
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
render_resource::{Extent3d, TextureDimension, TextureFormat},
|
render_resource::{Extent3d, TextureDimension, TextureFormat},
|
||||||
texture::{Image, TextureFormatPixelInfo},
|
texture::{Image, TextureFormatPixelInfo},
|
||||||
@ -12,7 +12,7 @@ use rectangle_pack::{
|
|||||||
};
|
};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::{texture_atlas::TextureAtlas, Rect};
|
use crate::texture_atlas::TextureAtlas;
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum TextureAtlasBuilderError {
|
pub enum TextureAtlasBuilderError {
|
||||||
|
|||||||
@ -9,7 +9,7 @@ use crate::{prelude::UiCameraConfig, CalculatedClip, Node, UiColor, UiImage};
|
|||||||
use bevy_app::prelude::*;
|
use bevy_app::prelude::*;
|
||||||
use bevy_asset::{load_internal_asset, AssetEvent, Assets, Handle, HandleUntyped};
|
use bevy_asset::{load_internal_asset, AssetEvent, Assets, Handle, HandleUntyped};
|
||||||
use bevy_ecs::prelude::*;
|
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_reflect::TypeUuid;
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
camera::{Camera, CameraProjection, OrthographicProjection, WindowOrigin},
|
camera::{Camera, CameraProjection, OrthographicProjection, WindowOrigin},
|
||||||
@ -23,7 +23,7 @@ use bevy_render::{
|
|||||||
view::{ComputedVisibility, ExtractedView, ViewUniforms},
|
view::{ComputedVisibility, ExtractedView, ViewUniforms},
|
||||||
Extract, RenderApp, RenderStage,
|
Extract, RenderApp, RenderStage,
|
||||||
};
|
};
|
||||||
use bevy_sprite::{Rect, SpriteAssetEvents, TextureAtlas};
|
use bevy_sprite::{SpriteAssetEvents, TextureAtlas};
|
||||||
use bevy_text::{DefaultTextPipeline, Text};
|
use bevy_text::{DefaultTextPipeline, Text};
|
||||||
use bevy_transform::components::GlobalTransform;
|
use bevy_transform::components::GlobalTransform;
|
||||||
use bevy_utils::FloatOrd;
|
use bevy_utils::FloatOrd;
|
||||||
@ -204,7 +204,7 @@ pub fn extract_uinodes(
|
|||||||
extracted_uinodes.uinodes.push(ExtractedUiNode {
|
extracted_uinodes.uinodes.push(ExtractedUiNode {
|
||||||
transform: transform.compute_matrix(),
|
transform: transform.compute_matrix(),
|
||||||
color: color.0,
|
color: color.0,
|
||||||
rect: bevy_sprite::Rect {
|
rect: Rect {
|
||||||
min: Vec2::ZERO,
|
min: Vec2::ZERO,
|
||||||
max: uinode.size,
|
max: uinode.size,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -2,7 +2,7 @@ use crate::{Size, UiRect};
|
|||||||
use bevy_asset::Handle;
|
use bevy_asset::Handle;
|
||||||
use bevy_derive::{Deref, DerefMut};
|
use bevy_derive::{Deref, DerefMut};
|
||||||
use bevy_ecs::{prelude::Component, reflect::ReflectComponent};
|
use bevy_ecs::{prelude::Component, reflect::ReflectComponent};
|
||||||
use bevy_math::Vec2;
|
use bevy_math::{Rect, Vec2};
|
||||||
use bevy_reflect::prelude::*;
|
use bevy_reflect::prelude::*;
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
color::Color,
|
color::Color,
|
||||||
@ -408,5 +408,5 @@ impl From<Handle<Image>> for UiImage {
|
|||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct CalculatedClip {
|
pub struct CalculatedClip {
|
||||||
/// The rect of the clip
|
/// The rect of the clip
|
||||||
pub clip: bevy_sprite::Rect,
|
pub clip: Rect,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,8 +9,7 @@ use bevy_ecs::{
|
|||||||
system::{Commands, Query},
|
system::{Commands, Query},
|
||||||
};
|
};
|
||||||
use bevy_hierarchy::{Children, Parent};
|
use bevy_hierarchy::{Children, Parent};
|
||||||
use bevy_math::Vec2;
|
use bevy_math::Rect;
|
||||||
use bevy_sprite::Rect;
|
|
||||||
use bevy_transform::components::{GlobalTransform, Transform};
|
use bevy_transform::components::{GlobalTransform, Transform};
|
||||||
|
|
||||||
/// The resolution of `Z` values for UI
|
/// The resolution of `Z` values for UI
|
||||||
@ -109,18 +108,8 @@ fn update_clipping(
|
|||||||
Overflow::Visible => clip,
|
Overflow::Visible => clip,
|
||||||
Overflow::Hidden => {
|
Overflow::Hidden => {
|
||||||
let node_center = global_transform.translation().truncate();
|
let node_center = global_transform.translation().truncate();
|
||||||
let node_rect = Rect {
|
let node_rect = Rect::from_center_size(node_center, node.size);
|
||||||
min: node_center - node.size / 2.,
|
Some(clip.map_or(node_rect, |c| c.intersect(node_rect)))
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user