use {
crate::point,
num::PrimInt,
std::{cmp::PartialOrd, default::Default, fmt::Debug},
};
pub(crate) type Type<U> = (point::Type<U>, (U, U));
#[derive(PartialEq, Eq, Clone, Copy, Hash, Builder)]
#[builder(build_fn(validate = "Self::validate"))]
pub struct Area<U>
where
U: PrimInt + Default + PartialOrd,
{
anchor: point::Point<U>,
#[builder(default = "(U::one(), U::one())")]
dimensions: (U, U),
}
impl<U> AreaBuilder<U>
where
U: PrimInt + Default + PartialOrd,
{
fn validate(&self) -> Result<(), String> {
if let Some((w, h)) = self.dimensions {
if w <= U::zero() {
return Err("Areas may not have nonpositive widths.".to_string());
}
if h <= U::zero() {
return Err("Areas may not have nonpositive heights.".to_string());
}
}
Ok(())
}
}
impl<U> Debug for Area<U>
where
U: PrimInt + Default + Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"({:?})->{:?}x{:?}",
self.anchor(),
self.width(),
self.height()
)
}
}
impl<U> Into<Type<U>> for Area<U>
where
U: PrimInt + Default,
{
fn into(self) -> Type<U> {
(self.anchor.into(), self.dimensions())
}
}
impl<U> Area<U>
where
U: PrimInt + Default,
{
pub fn anchor(&self) -> point::Point<U> {
self.anchor
}
pub fn width(&self) -> U {
self.dimensions.0
}
pub fn height(&self) -> U {
self.dimensions.1
}
pub fn top_edge(&self) -> U {
self.anchor().y()
}
pub fn bottom_edge(&self) -> U {
self.anchor().y() + self.height()
}
pub fn left_edge(&self) -> U {
self.anchor().x()
}
pub fn right_edge(&self) -> U {
self.anchor().x() + self.width()
}
pub fn intersects(self, other: Self) -> bool {
self.left_edge() < other.right_edge()
&& self.right_edge() > other.left_edge()
&& self.top_edge() < other.bottom_edge()
&& self.bottom_edge() > other.top_edge()
}
pub fn contains(self, other: Self) -> bool {
other.right_edge() <= self.right_edge()
&& other.left_edge() >= self.left_edge()
&& other.top_edge() >= self.top_edge()
&& other.bottom_edge() <= self.bottom_edge()
}
pub fn contains_pt(self, pt: point::Point<U>) -> bool {
self.contains(
AreaBuilder::default()
.anchor(pt)
.dimensions((U::one(), U::one()))
.build()
.expect("Unexpected error in Area::contains_pt."),
)
}
pub(crate) fn center_pt(&self) -> point::Point<U> {
self.anchor()
+ point::Point {
x: self.width() / Self::two(),
y: self.height() / Self::two(),
}
}
pub(crate) fn dimensions(&self) -> (U, U) {
self.dimensions
}
fn two() -> U {
U::one() + U::one()
}
}