use crate::vec2::*;
use crate::math;
use core::marker::PhantomData;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct Rect2<Unit: Copy = (), Space: Copy = ()> {
pub pos: Vec2<Unit, Space>,
pub dim: Vec2<Unit, Space>,
#[cfg_attr(feature = "serde", serde(skip))]
_unit: PhantomData<Unit>,
#[cfg_attr(feature = "serde", serde(skip))]
_space: PhantomData<Space>,
}
pub type Rect2f32 = Rect2<(),()>;
pub type Rect2Meters = Rect2<Meters,()>;
pub type Rect2Pixels = Rect2<Pixels,()>;
pub type Rect2World = Rect2<(),World>;
pub type Rect2Local = Rect2<(),Local>;
pub type Rect2Screen = Rect2<(),Screen>;
pub type Rect2MetersWorld = Rect2<Meters,World>;
pub type Rect2PixelsScreen = Rect2<Pixels,Screen>;
impl<Unit: Copy, Space: Copy> Rect2<Unit, Space> {
#[inline]
pub const fn new(pos: Vec2<Unit, Space>, dim: Vec2<Unit, Space>) -> Self {
Self { pos, dim, _unit: PhantomData, _space: PhantomData }
}
#[inline]
pub const fn from_array(v: [f32; 4]) -> Self {
Self {
pos: Vec2::new(v[0], v[1]),
dim: Vec2::new(v[2], v[3]),
_unit: PhantomData,
_space: PhantomData,
}
}
#[inline]
pub fn from_slice(v: &[f32; 4]) -> Self {
Self {
pos: Vec2::new(v[0], v[1]),
dim: Vec2::new(v[2], v[3]),
_unit: PhantomData,
_space: PhantomData,
}
}
#[inline]
pub fn contains_point_coords(&self, x: f32, y: f32) -> bool {
let min_x = self.pos.x.min(self.pos.x + self.dim.x);
let max_x = self.pos.x.max(self.pos.x + self.dim.x);
let min_y = self.pos.y.min(self.pos.y + self.dim.y);
let max_y = self.pos.y.max(self.pos.y + self.dim.y);
x >= min_x && x < max_x && y >= min_y && y < max_y
}
#[inline]
pub fn contains_point(&self, p: Vec2<Unit, Space>) -> bool {
self.contains_point_coords(p.x, p.y)
}
#[inline]
pub fn intersects(&self, other: &Self) -> bool {
self.intersection(other).is_some()
}
#[inline]
pub fn intersection(&self, other: &Self) -> Option<Self> {
let a_min_x = self.pos.x.min(self.pos.x + self.dim.x);
let a_max_x = self.pos.x.max(self.pos.x + self.dim.x);
let a_min_y = self.pos.y.min(self.pos.y + self.dim.y);
let a_max_y = self.pos.y.max(self.pos.y + self.dim.y);
let b_min_x = other.pos.x.min(other.pos.x + other.dim.x);
let b_max_x = other.pos.x.max(other.pos.x + other.dim.x);
let b_min_y = other.pos.y.min(other.pos.y + other.dim.y);
let b_max_y = other.pos.y.max(other.pos.y + other.dim.y);
let x0 = a_min_x.max(b_min_x);
let y0 = a_min_y.max(b_min_y);
let x1 = a_max_x.min(b_max_x);
let y1 = a_max_y.min(b_max_y);
if x0 < x1 && y0 < y1 {
Some(Self::new(Vec2::new(x0, y0), Vec2::new(x1 - x0, y1 - y0)))
} else {
None
}
}
pub const fn min(&self) -> Vec2<Unit, Space> {
Vec2::new(
self.pos.x.min(self.pos.x + self.dim.x),
self.pos.y.min(self.pos.y + self.dim.y),
)
}
pub const fn max(&self) -> Vec2<Unit, Space> {
Vec2::new(
self.pos.x.max(self.pos.x + self.dim.x),
self.pos.y.max(self.pos.y + self.dim.y),
)
}
pub fn size(&self) -> Vec2<Unit, Space> {
Vec2::new(self.dim.x.abs(), self.dim.y.abs())
}
pub fn area(&self) -> f32 {
let s = self.size();
s.x * s.y
}
pub fn is_empty(&self) -> bool {
self.dim.x == 0.0 || self.dim.y == 0.0
}
pub fn from_min_max(min: Vec2<Unit, Space>, max: Vec2<Unit, Space>) -> Self {
Self {
pos: min,
dim: max - min,
_unit: PhantomData,
_space: PhantomData,
}
}
pub fn expand_to_include(&self, point: Vec2<Unit, Space>) -> Self {
let min = self.min();
let max = self.max();
let new_min = Vec2::new(min.x.min(point.x), min.y.min(point.y));
let new_max = Vec2::new(max.x.max(point.x), max.y.max(point.y));
Self::from_min_max(new_min, new_max)
}
pub fn union(&self, other: &Self) -> Self {
let min = self.min().min(other.min());
let max = self.max().max(other.max());
Self::from_min_max(min, max)
}
#[cfg(feature = "mat4")]
pub fn transform(&self, mat: &crate::mat4::Mat4) -> Self {
let min = self.min();
let max = self.max();
let corners: [Vec2; 4] = [
Vec2::new(min.x, min.y),
Vec2::new(min.x, max.y),
Vec2::new(max.x, min.y),
Vec2::new(max.x, max.y),
];
let mut tmin = Vec2::new(f32::INFINITY, f32::INFINITY);
let mut tmax = Vec2::new(f32::NEG_INFINITY, f32::NEG_INFINITY);
for &c in &corners {
let v = mat.transform_point(crate::vec3::Vec3::new(c.x, c.y, 0.0));
tmin = tmin.min(Vec2::new(v.x, v.y));
tmax = tmax.max(Vec2::new(v.x, v.y));
}
Self::from_min_max(tmin, tmax)
}
pub fn closest_point(&self, point: Vec2<Unit, Space>) -> Vec2<Unit, Space> {
let min = self.min();
let max = self.max();
point.clamp(min, max)
}
pub fn distance(&self, point: Vec2<Unit, Space>) -> f32 {
let min = self.min();
let max = self.max();
let dx = if point.x < min.x {
min.x - point.x
} else if point.x > max.x {
point.x - max.x
} else {
0.0
};
let dy = if point.y < min.y {
min.y - point.y
} else if point.y > max.y {
point.y - max.y
} else {
0.0
};
math::sqrt(dx * dx + dy * dy)
}
pub fn intersect_ray(&self, origin: Vec2<Unit, Space>, dir: Vec2<Unit, Space>) -> Option<f32> {
let min = self.min();
let max = self.max();
let mut tmin = (min.x - origin.x) / dir.x;
let mut tmax = (max.x - origin.x) / dir.x;
if tmin > tmax {
core::mem::swap(&mut tmin, &mut tmax);
}
let mut tymin = (min.y - origin.y) / dir.y;
let mut tymax = (max.y - origin.y) / dir.y;
if tymin > tymax {
core::mem::swap(&mut tymin, &mut tymax);
}
if (tmin > tymax) || (tymin > tmax) {
return None;
}
tmin = tmin.max(tymin);
tmax = tmax.min(tymax);
if tmax < 0.0 {
return None;
}
Some(if tmin >= 0.0 { tmin } else { tmax })
}
}
impl<Space: Copy> Rect2<Meters, Space> {
pub fn to_pixels(self, scale: f32) -> Rect2<Pixels, Space> {
Rect2 {
pos: self.pos.to_pixels(scale),
dim: self.dim.to_pixels(scale),
_unit: PhantomData,
_space: PhantomData,
}
}
}