use crate::range::Range;
use crate::vec2::vec2;
use crate::*;
use core::convert::TryInto;
#[inline(always)]
pub fn rect<T>(xstart: T, xend: T, ystart: T, yend: T) -> Rect<T> {
Rect::new(xstart, xend, ystart, yend)
}
#[derive(Default,Hash,Copy, Clone, Debug, Eq, PartialEq)]
#[must_use]
pub struct Rect<T> {
pub x: Range<T>,
pub y: Range<T>,
}
impl<S> Rect<S> {
#[inline(always)]
pub fn inner_into<A>(self) -> Rect<A>
where
S: Into<A>,
{
let x = self.x.inner_into();
let y = self.y.inner_into();
Rect { x, y }
}
#[inline(always)]
pub fn inner_try_into<A>(self) -> Result<Rect<A>, S::Error>
where
S: TryInto<A>,
{
let x = self.x.inner_try_into();
let y = self.y.inner_try_into();
match (x, y) {
(Ok(x), Ok(y)) => Ok(Rect { x, y }),
(Ok(_), Err(e)) => Err(e),
(Err(e), Ok(_)) => Err(e),
(Err(e1), Err(_)) => Err(e1),
}
}
}
impl<T: Copy + core::ops::Sub<Output = T> + core::ops::Add<Output = T>> Rect<T> {
#[inline(always)]
pub fn from_point(point: Vec2<T>, radius: Vec2<T>) -> Rect<T> {
let x = Range::from_point(point.x, radius.x);
let y = Range::from_point(point.y, radius.y);
Rect { x, y }
}
}
impl<B> From<[B; 4]> for Rect<B> {
#[inline(always)]
fn from(a: [B; 4]) -> Self {
let [a, b, c, d] = a;
Rect::new(a, b, c, d)
}
}
impl<B> From<Rect<B>> for [B; 4] {
#[inline(always)]
fn from(a: Rect<B>) -> Self {
[a.x.start, a.x.end, a.y.start, a.y.end]
}
}
impl<B: Copy> From<&Rect<B>> for [B; 4] {
#[inline(always)]
fn from(a: &Rect<B>) -> Self {
[a.x.start, a.x.end, a.y.start, a.y.end]
}
}
impl<T> Rect<T> {
#[inline(always)]
pub fn get_range(&self, axis: impl Axis) -> &Range<T> {
if axis.is_xaxis() {
&self.x
} else {
&self.y
}
}
#[inline(always)]
pub fn get_range_mut(&mut self, axis: impl Axis) -> &mut Range<T> {
if axis.is_xaxis() {
&mut self.x
} else {
&mut self.y
}
}
}
impl<T> Rect<T> {
#[inline(always)]
pub fn new(xstart: T, xend: T, ystart: T, yend: T) -> Rect<T> {
Rect {
x: Range {
start: xstart,
end: xend,
},
y: Range {
start: ystart,
end: yend,
},
}
}
}
impl<T: Copy> Rect<T> {
#[inline(always)]
pub fn top_left(&self) -> Vec2<T> {
vec2(self.x.start, self.y.start)
}
pub fn get_corners(&self) -> [Vec2<T>; 4] {
[
vec2(self.x.start, self.y.start),
vec2(self.x.end, self.y.start),
vec2(self.x.end, self.y.end),
vec2(self.x.start, self.y.end),
]
}
#[inline(always)]
pub fn inner_as<B: 'static + Copy>(&self) -> Rect<B>
where
T: num_traits::AsPrimitive<B>,
{
Rect {
x: self.x.inner_as(),
y: self.y.inner_as(),
}
}
#[inline(always)]
pub fn get(&self) -> ((T, T), (T, T)) {
let f = self;
((f.x.start, f.x.end), (f.y.start, f.y.end))
}
}
impl<T: PartialOrd + Copy> Rect<T> {
#[inline(always)]
pub fn contains_point(&self, a: Vec2<T>) -> bool {
self.x.contains(a.x) && self.y.contains(a.y)
}
}
impl<T: Copy + core::ops::Sub<Output = T> + core::ops::Add<Output = T>> Rect<T> {
#[inline(always)]
pub fn grow(self, radius: T) -> Self {
Rect {
x: self.x.grow(radius),
y: self.y.grow(radius),
}
}
}
impl<
T: Copy
+ PartialOrd
+ core::ops::Sub<Output = T>
+ core::ops::Mul<Output = T>
+ core::ops::Add<Output = T>,
> Rect<T>
{
#[inline(always)]
pub fn distance_squared_to_point(&self, point: Vec2<T>) -> Option<T> {
let (px, py) = (point.x, point.y);
let ((a, b), (c, d)) = self.get();
let xx = num_traits::clamp(px, a, b);
let yy = num_traits::clamp(py, c, d);
let dis = (xx - px) * (xx - px) + (yy - py) * (yy - py);
if xx > a && xx < b && yy > c && yy < d {
None
} else {
Some(dis)
}
}
#[inline(always)]
pub fn furthest_distance_squared_to_point(&self, point: Vec2<T>) -> T {
let (px, py) = (point.x, point.y);
let ((a, b), (c, d)) = self.get();
fn reverse_clamp<N: PartialOrd + core::ops::Sub<Output = N> + Copy>(
px: N,
a: N,
b: N,
) -> N {
let aa = px - a;
let bb = b - px;
if bb > aa {
b
} else {
a
}
}
let xx = reverse_clamp(px, a, b);
let yy = reverse_clamp(py, c, d);
(xx - px) * (xx - px) + (yy - py) * (yy - py)
}
}
impl<T: num_traits::Num + Copy> Rect<T> {
#[inline(always)]
pub fn derive_center(&self) -> Vec2<T> {
let two = T::one() + T::one();
let ((a, b), (c, d)) = self.get();
vec2(a + (b - a) / two, c + (d - c) / two)
}
}
impl<T: PartialOrd + Copy> Rect<T> {
#[inline(always)]
pub fn subdivide<A: Axis>(&self, axis: A, divider: T) -> (Rect<T>, Rect<T>) {
let ca = axis;
let na = axis.next();
let rel = self.get_range(ca);
let carry_thru = *self.get_range(na);
let (l, r) = rel.subdivide(divider);
if axis.is_xaxis() {
(
Rect {
x: l,
y: carry_thru,
},
Rect {
x: r,
y: carry_thru,
},
)
} else {
(
Rect {
x: carry_thru,
y: l,
},
Rect {
x: carry_thru,
y: r,
},
)
}
}
#[inline(always)]
pub fn is_valid(&self) -> bool {
self.x.is_valid() && self.y.is_valid()
}
#[inline(always)]
pub fn contains_rect(&self, rect: &Rect<T>) -> bool {
self.x.contains_range(&rect.x) && self.y.contains_range(&rect.y)
}
#[inline(always)]
pub fn grow_to_fit_point(&mut self, point: Vec2<T>) -> &mut Self {
if point.x < self.x.start {
self.x.start = point.x
} else if self.x.end < point.x {
self.x.end = point.x
}
if point.y < self.y.start {
self.y.start = point.y
} else if self.y.end < point.y {
self.y.end = point.y
}
self
}
#[inline(always)]
pub fn grow_to_fit(&mut self, rect: &Rect<T>) -> &mut Self {
{
macro_rules! macro_axis {
($axis:ident) => {{
let sx = self.get_range_mut($axis);
let rx = rect.get_range($axis);
sx.grow_to_fit(rx);
}};
}
macro_axis!(XAXIS);
macro_axis!(YAXIS);
}
self
}
#[inline(always)]
pub fn intersects_rect(&self, other: &Rect<T>) -> bool {
other.x.intersects(&self.x) && other.y.intersects(&self.y)
}
}
impl<T: PartialOrd + Copy> Rect<T> {
#[inline(always)]
pub fn get_intersect_rect(&self, other: &Rect<T>) -> Option<Rect<T>> {
macro_rules! macro_axis {
($axis:ident) => {{
let xr = other.get_range($axis);
let xf = self.get_range($axis);
let range = Range {
start: partial_min_max::max(xr.start, xf.start),
end: partial_min_max::min(xr.end, xf.end),
};
if range.end <= range.start {
return None;
}
range
}};
}
let x = macro_axis!(XAXIS);
let y = macro_axis!(YAXIS);
Some(Rect { x, y })
}
}