use crate::{error::Result, prelude::*};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[repr(transparent)]
#[must_use]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Ellipse<T = i32>(pub(crate) [T; 4]);
#[macro_export]
macro_rules! ellipse {
($p:expr, $r:expr$(,)?) => {
ellipse!($p, $r, $r)
};
($p:expr, $width:expr, $height:expr$(,)?) => {
$crate::prelude::Ellipse::with_position($p, $width, $height)
};
($x:expr, $y:expr, $width:expr, $height:expr$(,)?) => {
$crate::prelude::Ellipse::new($x, $y, $width, $height)
};
}
#[macro_export]
macro_rules! circle {
($p:expr, $r:expr$(,)?) => {
$crate::prelude::Ellipse::circle_with_position($p, $r)
};
($x:expr, $y:expr, $r:expr$(,)?) => {
$crate::prelude::Ellipse::circle($x, $y, $r)
};
}
impl<T> Ellipse<T> {
pub const fn new(x: T, y: T, width: T, height: T) -> Self {
Self([x, y, width, height])
}
}
impl<T: Copy> Ellipse<T> {
#[inline]
pub fn coords(&self) -> [T; 4] {
self.0
}
#[inline]
pub fn coords_mut(&mut self) -> &mut [T; 4] {
&mut self.0
}
#[inline]
pub fn x(&self) -> T {
self.0[0]
}
#[inline]
pub fn set_x(&mut self, x: T) {
self.0[0] = x;
}
#[inline]
pub fn y(&self) -> T {
self.0[1]
}
#[inline]
pub fn set_y(&mut self, y: T) {
self.0[1] = y;
}
#[inline]
pub fn width(&self) -> T {
self.0[2]
}
#[inline]
pub fn set_width(&mut self, width: T) {
self.0[2] = width;
}
#[inline]
pub fn height(&self) -> T {
self.0[3]
}
#[inline]
pub fn set_height(&mut self, height: T) {
self.0[3] = height;
}
}
impl<T: Num> Ellipse<T> {
pub fn circle(x: T, y: T, radius: T) -> Self {
let two = T::one() + T::one();
let diameter = radius * two;
Self::new(x, y, diameter, diameter)
}
pub fn with_position<P: Into<Point<T>>>(p: P, width: T, height: T) -> Self {
let p = p.into();
Self::new(p.x(), p.y(), width, height)
}
pub fn circle_with_position<P: Into<Point<T>>>(p: P, radius: T) -> Self {
let p = p.into();
Self::circle(p.x(), p.y(), radius)
}
pub fn from_center<P: Into<Point<T>>>(p: P, width: T, height: T) -> Self {
let p = p.into();
let two = T::one() + T::one();
Self::new(p.x() - width / two, p.y() - height / two, width, height)
}
pub fn circle_from_center<P: Into<Point<T>>>(p: P, radius: T) -> Self {
let p = p.into();
let two = T::one() + T::one();
Self::circle_with_position(p - radius / two, radius)
}
#[inline]
pub fn radius(&self) -> T {
let two = T::one() + T::one();
self.diameter() / two
}
#[inline]
pub fn set_radius(&mut self, radius: T) {
let two = T::one() + T::one();
let diameter = radius * two;
self.0[2] = diameter;
self.0[3] = diameter;
}
#[inline]
pub fn diameter(&self) -> T {
assert!(self.0[2] == self.0[3], "shape is not a circle");
self.0[2]
}
#[inline]
pub fn offset<P>(&mut self, offsets: P)
where
P: Into<Point<T>>,
{
let offsets = offsets.into();
for (v, o) in self.iter_mut().take(2).zip(offsets) {
*v += o;
}
}
#[inline]
pub fn offset_x(&mut self, offset: T) {
self.0[0] += offset;
}
#[inline]
pub fn offset_y(&mut self, offset: T) {
self.0[1] += offset;
}
#[inline]
pub fn offset_width(&mut self, offset: T) {
self.0[2] += offset;
}
#[inline]
pub fn offset_height(&mut self, offset: T) {
self.0[3] += offset;
}
#[inline]
pub fn offset_radius(&mut self, offset: T) {
self.0[2] += offset;
self.0[3] += offset;
}
#[inline]
pub fn size(&self) -> Point<T> {
point!(self.width(), self.height())
}
#[inline]
pub fn bounding_rect(&self) -> Rect<T> {
rect![self.left(), self.top(), self.width(), self.height()]
}
pub fn to_vec(self) -> Vec<T> {
self.0.to_vec()
}
pub fn left(&self) -> T {
let two = T::one() + T::one();
self.x() - self.width() / two
}
pub fn right(&self) -> T {
let two = T::one() + T::one();
self.x() + self.width() / two
}
pub fn top(&self) -> T {
let two = T::one() + T::one();
self.y() - self.height() / two
}
pub fn bottom(&self) -> T {
let two = T::one() + T::one();
self.y() + self.height() / two
}
pub fn set_left(&mut self, left: T) {
let two = T::one() + T::one();
self.set_x(left + self.width() / two);
}
pub fn set_right(&mut self, right: T) {
let two = T::one() + T::one();
self.set_x(right - self.width() / two);
}
pub fn set_top(&mut self, top: T) {
let two = T::one() + T::one();
self.set_y(top + self.height() / two);
}
pub fn set_bottom(&mut self, bottom: T) {
let two = T::one() + T::one();
self.set_y(bottom - self.height() / two);
}
pub fn center(&self) -> Point<T> {
point!(self.x(), self.y())
}
pub fn top_left(&self) -> Point<T> {
point!(self.left(), self.top())
}
pub fn top_right(&self) -> Point<T> {
point!(self.right(), self.top())
}
pub fn bottom_left(&self) -> Point<T> {
point!(self.left(), self.bottom())
}
pub fn bottom_right(&self) -> Point<T> {
point!(self.right(), self.bottom())
}
pub fn center_on<P: Into<Point<T>>>(&mut self, p: P) {
let p = p.into();
self.set_x(p.x());
self.set_y(p.y());
}
}
impl<T: Num> Contains<Point<T>> for Ellipse<T> {
fn contains(&self, p: Point<T>) -> bool {
let px = p.x() - self.x();
let py = p.y() - self.y();
let two = T::one() + T::one();
let rx = self.width() / two;
let ry = self.height() / two;
(px * px) / (rx * rx) + (py * py) / (ry * ry) <= T::one()
}
}
impl<T: Num> Contains<Ellipse<T>> for Ellipse<T> {
fn contains(&self, ellipse: Ellipse<T>) -> bool {
let px = self.x() - ellipse.x();
let py = self.y() - ellipse.y();
let rx = self.width() + ellipse.width();
let ry = self.height() + ellipse.height();
(px * px) / (rx * rx) + (py * py) / (ry * ry) <= T::one()
}
}
impl Draw for Ellipse<i32> {
fn draw(&self, s: &mut PixState) -> Result<()> {
s.ellipse(*self)
}
}
impl<T: Num> From<[T; 3]> for Ellipse<T> {
#[inline]
fn from([x, y, r]: [T; 3]) -> Self {
Self::circle(x, y, r)
}
}
impl<T: Num> From<&[T; 3]> for Ellipse<T> {
#[inline]
fn from(&[x, y, r]: &[T; 3]) -> Self {
Self::circle(x, y, r)
}
}