use super::{
Area, Containment, ContainsLocalPosition, ContainsPosition, HasArea, HasHeight, HasPosition,
HasSize, HasWidth, IntersectsLocalPosition, IntersectsPosition, IsPosition, Length, Placed,
PlacedObject, PlacedShape, Position, ProvidesArea, ProvidesPlacedShape, ProvidesSize, Shape,
ShapeIterator, Size,
};
#[derive(Copy, Clone, Debug, Display)]
pub struct Oval {
area: Area,
}
impl Oval {
#[must_use]
pub fn new(position: Position, size: Size) -> Self {
Self {
area: Area::new(position, size),
}
}
}
impl ContainsLocalPosition for Oval {
fn contains_local_position(&self, position: Position) -> Containment {
if !self.intersects_local_position(position) {
return Containment::Disjoint;
}
let fwidth = ((f64::from(self.area.width()) / 2.0) - 0.5).max(0.0);
let fheight = ((f64::from(self.area.height()) / 2.0) - 0.5).max(0.0);
let fmin_bounds = (fwidth.min(fheight) - 1.0).max(0.0);
let fmin_bounds = (fmin_bounds * fmin_bounds) / 2.0;
let fmin_bounds = if fmin_bounds > 0.0 {
fmin_bounds.sqrt()
} else {
fmin_bounds
};
let flocal_center_x = fwidth;
let flocal_center_y = fheight;
let adjusted_position_x = f64::from(position.x()) - flocal_center_x;
let adjusted_position_y = f64::from(position.y()) - flocal_center_y;
#[allow(clippy::if_same_then_else)]
if adjusted_position_x.abs() <= fmin_bounds
&& adjusted_position_y.abs() <= fmin_bounds
&& ((fmin_bounds - adjusted_position_x.abs() >= 1.0)
|| (fmin_bounds - adjusted_position_y.abs() >= 1.0))
{
Containment::Contains
} else if self.intersects_local_position(position + Position::NORTH)
&& self.intersects_local_position(position + Position::NORTH + Position::EAST)
&& self.intersects_local_position(position + Position::EAST)
&& self.intersects_local_position(position + Position::SOUTH + Position::EAST)
&& self.intersects_local_position(position + Position::SOUTH)
&& self.intersects_local_position(position + Position::SOUTH + Position::WEST)
&& self.intersects_local_position(position + Position::WEST)
&& self.intersects_local_position(position + Position::NORTH + Position::WEST)
{
Containment::Contains
} else {
Containment::Intersects
}
}
}
impl ContainsPosition for Oval {}
impl HasArea for Oval {
fn area(&self) -> &Area {
&self.area
}
fn area_mut(&mut self) -> &mut Area {
&mut self.area
}
}
impl HasHeight for Oval {
fn height(&self) -> Length {
self.size().height()
}
fn height_mut(&mut self) -> &mut Length {
self.size_mut().height_mut()
}
}
impl HasPosition for Oval {
fn position(&self) -> &Position {
self.area.position()
}
fn position_mut(&mut self) -> &mut Position {
self.area.position_mut()
}
}
impl HasSize for Oval {
fn size(&self) -> &Size {
self.area.size()
}
fn size_mut(&mut self) -> &mut Size {
self.area.size_mut()
}
}
impl HasWidth for Oval {
fn width(&self) -> Length {
self.size().width()
}
fn width_mut(&mut self) -> &mut Length {
self.size_mut().width_mut()
}
}
impl IntersectsLocalPosition for Oval {
fn intersects_local_position(&self, position: Position) -> bool {
let fwidth = ((f64::from(self.area.width()) / 2.0) - 0.5).max(0.0);
let fheight = ((f64::from(self.area.height()) / 2.0) - 0.5).max(0.0);
let ratio: f64 = fwidth / fheight;
let flocal_center_x = fwidth;
let flocal_center_y = fheight;
let adjusted_position_x = f64::from(position.x()) - flocal_center_x;
let adjusted_position_y = f64::from(position.y()) - flocal_center_y;
let circular_position_x = adjusted_position_x / ratio;
let circular_position_y = adjusted_position_y;
let radius_sqr = fheight * fheight;
let dist_sqr = (circular_position_x * circular_position_x)
+ (circular_position_y * circular_position_y);
radius_sqr >= dist_sqr
}
}
impl IntersectsPosition for Oval {}
impl Placed for Oval {}
impl PlacedObject for Oval {}
impl ProvidesArea for Oval {
fn provide_area(&self) -> Area {
self.area.provide_area()
}
}
impl ProvidesPlacedShape for Oval {
fn provide_placed_shape(&self) -> Box<dyn PlacedShape> {
Box::new(*self)
}
}
impl ProvidesSize for Oval {
fn provide_size(&self) -> Size {
self.area.provide_size()
}
}
impl Shape for Oval {
fn box_shape_clone(&self) -> Box<dyn Shape> {
Box::new(*self)
}
fn iter(&self) -> ShapeIterator {
ShapeIterator::new(self)
}
}