use crate::math::geometry::{positioning::*, GetShapeDirectionType};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[allow(missing_docs)]
#[cfg_attr(feature = "bitcode", derive(bitcode::Encode, bitcode::Decode))]
#[cfg_attr(
feature = "wincode",
derive(wincode::SchemaWrite, wincode::SchemaRead)
)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "c_compatible", repr(C))]
pub struct Rectangle<T, const CS: bool> {
pub width: T,
pub height: T,
}
impl<T, const CS: bool> Shape<T> for Rectangle<T, CS> {}
impl<T: Default, const CS: bool> Default for Rectangle<T, CS> {
fn default() -> Self {
Self {
width: T::default(),
height: T::default(),
}
}
}
impl<T, const BOTTOM_HIGHER: bool> Rectangle<T, BOTTOM_HIGHER>
where
T: Copy,
{
#[must_use]
pub const fn new(size: (T, T)) -> Self {
Self {
width: size.0,
height: size.1,
}
}
pub const fn set_size(&mut self, size: (T, T)) {
self.width = size.0;
self.height = size.1;
}
}
impl<T, const BOTTOM_HIGHER: bool> Rectangle<T, BOTTOM_HIGHER>
where
T: Copy,
{
#[must_use]
#[inline(always)]
pub const fn get_size(&self) -> (T, T) {
(self.width, self.height)
}
#[must_use]
#[inline(always)]
pub const fn get_width(&self) -> T {
self.width
}
#[must_use]
#[inline(always)]
pub const fn get_height(&self) -> T {
self.height
}
}
impl<T, const BOTTOM_HIGHER: bool> Pos2D<T, Rectangle<T, BOTTOM_HIGHER>>
where
T: Copy,
{
#[must_use]
#[inline(always)]
pub const fn get_size(&self) -> (T, T) {
self.shape.get_size()
}
#[must_use]
#[inline(always)]
pub const fn get_width(&self) -> T {
self.shape.get_width()
}
#[must_use]
#[inline(always)]
pub const fn get_height(&self) -> T {
self.shape.get_height()
}
}
impl<T, const BOTTOM_HIGHER: bool> Pos2D<T, Rectangle<T, BOTTOM_HIGHER>>
where
T: core::ops::Add<Output = T> + PartialOrd + Copy,
{
#[must_use]
#[inline(always)]
pub fn offset_pos(&self, offset_pos: (T, T)) -> Self {
let x = self.get_x();
let y = self.get_y();
Self {
pos: (x + offset_pos.0, offset_pos.1 + y),
shape: Rectangle {
width: self.get_width(),
height: self.get_height(),
},
}
}
#[must_use]
#[inline(always)]
pub const fn left(&self) -> T {
self.get_x()
}
#[must_use]
#[inline(always)]
pub const fn right(&self) -> T
where
T: [const] core::ops::Add<Output = T> + Copy,
{
self.get_x() + self.get_width()
}
#[must_use]
#[inline(always)]
pub const fn top(&self) -> T
where
T: [const] core::ops::Add<Output = T> + Copy,
{
if BOTTOM_HIGHER {
self.get_y() + self.get_height()
} else {
self.get_y()
}
}
#[must_use]
#[inline(always)]
pub const fn bottom(&self) -> T
where
T: [const] core::ops::Add<Output = T> + Copy,
{
if BOTTOM_HIGHER {
self.get_y()
} else {
self.get_y() + self.get_height()
}
}
}
impl<V, const BOTTOM_HIGHER: bool> Pos2D<V, Rectangle<V, BOTTOM_HIGHER>>
where
V: core::ops::Div<Output = V> + PartialOrd + Copy,
{
#[must_use]
#[inline(always)]
pub fn get_ratio(&self) -> V {
self.shape.get_ratio()
}
}
impl<T, const BOTTOM_HIGHER: bool> Rectangle<T, BOTTOM_HIGHER>
where
T: core::ops::Div<Output = T> + PartialOrd + Copy,
{
#[must_use]
#[inline(always)]
pub fn get_ratio(&self) -> T {
self.height / self.width
}
}
use crate::math::{
geometry::{Pos2D, Shape},
ConstNumbers128,
};
impl<
T: ConstNumbers128
+ core::ops::Add<Output = T>
+ core::ops::Div<Output = T>
+ Copy,
const CS: bool,
> Pos2D<T, Rectangle<T, CS>>
{
#[must_use]
#[inline(always)]
pub fn center(&self) -> (T, T) {
(
self.get_x() + self.shape.get_width() / T::CONST_2,
self.get_y() + self.shape.get_height() / T::CONST_2,
)
}
}
#[macro_export]
macro_rules! impl_const_rectangle_for_type {
($t:ty) => {
impl<const CS: bool> Pos2D<$t, Rectangle<$t, CS>> {
#[must_use]
#[inline(always)]
pub const fn do_areas_intersect(
&self,
other: &Pos2D<$t, Rectangle<$t, CS>>,
) -> bool {
self.left() <= other.right()
&& self.right() >= other.left()
&& if CS {
self.bottom() <= other.top()
&& self.top() >= other.bottom()
} else {
self.top() <= other.bottom()
&& self.bottom() >= other.top()
}
}
#[must_use]
#[inline(always)]
pub const fn do_areas_intersect_strict(
&self,
other: &Pos2D<$t, Rectangle<$t, CS>>,
) -> bool {
self.left() < other.right()
&& self.right() > other.left()
&& if CS {
self.bottom() < other.top()
&& self.top() > other.bottom()
} else {
self.top() <= other.bottom()
&& self.bottom() > other.top()
}
}
#[must_use]
#[inline]
pub const fn does_area_fully_include_other_area(
&self,
other: &Pos2D<$t, Rectangle<$t, CS>>,
) -> bool {
if CS {
self.left() <= other.left()
&& self.right() >= other.right()
&& self.bottom() <= other.bottom()
&& self.top() >= other.top()
} else {
self.left() <= other.left()
&& self.right() >= other.right()
&& self.bottom() >= other.bottom()
&& self.top() <= other.top()
}
}
#[must_use]
#[inline(always)]
pub const fn does_area_contain_point(
&self,
point: ($t, $t),
) -> bool {
let (x, y) = point;
x >= self.left()
&& x <= self.right()
&& if !CS {
y >= self.top() && y <= self.bottom()
} else {
y <= self.top() && y >= self.bottom()
}
}
#[must_use]
#[inline(always)]
pub const fn is_point_at_edge(
&self,
point: ($t, $t),
margin: $t,
) -> bool {
let (x, y) = point;
let near_left =
x >= self.left() - margin && x <= self.left() + margin;
let near_right =
x >= self.right() - margin && x <= self.right() + margin;
let near_top =
y >= self.top() - margin && y <= self.top() + margin;
let near_bottom =
y >= self.bottom() - margin && y <= self.bottom() + margin;
near_left || near_right || near_top || near_bottom
}
#[must_use]
#[allow(clippy::too_many_lines)] pub const fn get_edge_position<
const EDGE: bool,
const CORNER: bool,
R: [const] $crate::math::geometry::ShapeDirectionType,
>(
&self,
point: ($t, $t),
margin: $t,
) -> R {
let (x, y) = point;
let (near_left, near_right, near_top, near_bottom) = if CS {
let near_left = x >= self.left() - margin
&& x <= self.left() + margin
&& y >= self.bottom() - margin
&& y <= self.top() + margin;
let near_right = x >= self.right() - margin
&& x <= self.right() + margin
&& y >= self.bottom() - margin
&& y <= self.top() + margin;
let near_top = y >= self.bottom() - margin
&& y <= self.bottom() + margin
&& x >= self.left() - margin
&& x <= self.right() + margin;
let near_bottom = y >= self.top() - margin
&& y <= self.top() + margin
&& x >= self.left() - margin
&& x <= self.right() + margin;
(near_left, near_right, near_top, near_bottom)
} else {
let near_left = x >= self.left() - margin
&& x <= self.left() + margin
&& y >= self.top() - margin
&& y <= self.bottom() + margin;
let near_right = x >= self.right() - margin
&& x <= self.right() + margin
&& y >= self.top() - margin
&& y <= self.bottom() + margin;
let near_top = y >= self.top() - margin
&& y <= self.top() + margin
&& x >= self.left() - margin
&& x <= self.right() + margin;
let near_bottom = y >= self.bottom() - margin
&& y <= self.bottom() + margin
&& x >= self.left() - margin
&& x <= self.right() + margin;
(near_left, near_right, near_top, near_bottom)
};
let (
in_top_left_direction_diagonal,
in_top_right_direction_diagonal,
in_bottom_right_direction_diagonal,
in_bottom_left_direction_diagonal,
) = if CS {
let in_top_left_direction_diagonal = (x < self.left()
&& y > self.bottom())
&& (self.left() - x + y - self.bottom()) <= margin;
let in_top_right_direction_diagonal = (x > self.right()
&& y > self.bottom())
&& (x - self.right() + y - self.bottom()) <= margin;
let in_bottom_right_direction_diagonal = (x > self.right()
&& y < self.top())
&& (x - self.right() + self.top() - y) <= margin;
let in_bottom_left_direction_diagonal = (x < self.left()
&& y < self.top())
&& (self.left() - x + self.top() - y) <= margin;
(
in_top_left_direction_diagonal,
in_top_right_direction_diagonal,
in_bottom_right_direction_diagonal,
in_bottom_left_direction_diagonal,
)
} else {
let in_top_left_direction_diagonal = (x < self.left()
&& y < self.top())
&& (self.left() - x + self.top() - y) <= margin;
let in_top_right_direction_diagonal = (x > self.right()
&& y < self.top())
&& (x - self.right() + self.top() - y) <= margin;
let in_bottom_right_direction_diagonal = (x > self.right()
&& y > self.bottom())
&& (x - self.right() + y - self.bottom()) <= margin;
let in_bottom_left_direction_diagonal = (x < self.left()
&& y > self.bottom())
&& (self.left() - x + y - self.bottom()) <= margin;
(
in_top_left_direction_diagonal,
in_top_right_direction_diagonal,
in_bottom_right_direction_diagonal,
in_bottom_left_direction_diagonal,
)
};
if CORNER
&& ((near_top && near_left)
|| in_top_left_direction_diagonal)
{
return R::top_left_direction(); }
if CORNER
&& ((near_top && near_right)
|| in_top_right_direction_diagonal)
{
return R::top_right_direction(); }
if CORNER
&& ((near_bottom && near_right)
|| in_bottom_right_direction_diagonal)
{
return R::bottom_right_direction(); }
if CORNER
&& ((near_bottom && near_left)
|| in_bottom_left_direction_diagonal)
{
return R::bottom_left_direction(); }
if EDGE && near_top && !near_left && !near_right {
return R::top_direction(); }
if EDGE && near_right && !near_top && !near_bottom {
return R::right_direction(); }
if EDGE && near_bottom && !near_left && !near_right {
return R::bottom_direction(); }
if EDGE && near_left && !near_top && !near_bottom {
return R::left_direction(); }
R::none_directional()
}
}
impl<R: $crate::math::geometry::ShapeDirectionType, const CS: bool>
GetShapeDirectionType<$t, R> for Pos2D<$t, Rectangle<$t, CS>>
{
fn get_edge_or_corner_type(
&self,
point: ($t, $t),
margin: $t,
) -> R {
Self::get_edge_position::<true, true, R>(self, point, margin)
}
fn get_edge_type(&self, point: ($t, $t), margin: $t) -> R {
Self::get_edge_position::<true, false, R>(self, point, margin)
}
fn get_corner_type(&self, point: ($t, $t), margin: $t) -> R {
Self::get_edge_position::<false, true, R>(self, point, margin)
}
}
};
}
impl_const_rectangle_for_type!(i8);
impl_const_rectangle_for_type!(i16);
impl_const_rectangle_for_type!(i32);
impl_const_rectangle_for_type!(i64);
impl_const_rectangle_for_type!(i128);
impl_const_rectangle_for_type!(isize);
impl_const_rectangle_for_type!(u8);
impl_const_rectangle_for_type!(u16);
impl_const_rectangle_for_type!(u32);
impl_const_rectangle_for_type!(u64);
impl_const_rectangle_for_type!(u128);
impl_const_rectangle_for_type!(usize);
impl_const_rectangle_for_type!(f32);
impl_const_rectangle_for_type!(f64);
impl_const_rectangle_for_type!(f16);
impl_const_rectangle_for_type!(f128);