use crate::Vec2;
#[repr(C)]
#[derive(Default, Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
pub struct Rect {
pub min: Vec2,
pub max: Vec2,
}
impl Rect {
#[inline]
pub fn new(x0: f32, y0: f32, x1: f32, y1: f32) -> Self {
Self::from_corners(Vec2::new(x0, y0), Vec2::new(x1, y1))
}
#[inline]
pub fn from_corners(p0: Vec2, p1: Vec2) -> Self {
Rect {
min: p0.min(p1),
max: p0.max(p1),
}
}
#[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)
}
#[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,
}
}
#[inline]
pub fn is_empty(&self) -> bool {
self.min.cmpge(self.max).any()
}
#[inline]
pub fn width(&self) -> f32 {
self.max.x - self.min.x
}
#[inline]
pub fn height(&self) -> f32 {
self.max.y - self.min.y
}
#[inline]
pub fn size(&self) -> Vec2 {
self.max - self.min
}
#[inline]
pub fn half_size(&self) -> Vec2 {
self.size() * 0.5
}
#[inline]
pub fn center(&self) -> Vec2 {
(self.min + self.max) * 0.5
}
#[inline]
pub fn contains(&self, point: Vec2) -> bool {
(point.cmpge(self.min) & point.cmple(self.max)).all()
}
#[inline]
pub fn union(&self, other: Rect) -> Rect {
Rect {
min: self.min.min(other.min),
max: self.max.max(other.max),
}
}
#[inline]
pub fn union_point(&self, other: Vec2) -> Rect {
Rect {
min: self.min.min(other),
max: self.max.max(other),
}
}
#[inline]
pub fn intersect(&self, other: Rect) -> Rect {
let mut r = Rect {
min: self.min.max(other.min),
max: self.max.min(other.max),
};
r.min = r.min.min(r.max);
r
}
#[inline]
pub fn inset(&self, inset: f32) -> Rect {
let mut r = Rect {
min: self.min - inset,
max: self.max + inset,
};
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);
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));
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));
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));
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);
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));
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);
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));
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);
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));
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);
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));
}
}