use std::cmp::Ordering;
use geom::{Vec2, DirVec2, v2, Card, CardMask};
use core::{Hitbox, HbVel};
use float::n64;
mod normals;
#[cfg(test)] mod tests;
#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
pub enum ShapeKind {
Circle,
Rect
}
#[derive(PartialEq, Copy, Clone, Debug)]
pub struct Shape {
kind: ShapeKind,
dims: Vec2
}
impl Shape {
pub fn new(kind: ShapeKind, dims: Vec2) -> Shape {
assert!(dims.x >= 0.0 && dims.y >= 0.0, "dims must be non-negative");
Shape::with_any_dims(kind, dims)
}
fn with_any_dims(kind: ShapeKind, dims: Vec2) -> Shape {
if kind == ShapeKind::Circle {
assert_eq!(dims.x, dims.y, "circle width must equal height");
}
Shape { kind: kind, dims: dims }
}
#[inline]
pub fn circle(diam: f64) -> Shape {
Shape::new(ShapeKind::Circle, v2(diam, diam))
}
#[inline]
pub fn rect(dims: Vec2) -> Shape {
Shape::new(ShapeKind::Rect, dims)
}
#[inline]
pub fn square(width: f64) -> Shape {
Shape::new(ShapeKind::Rect, v2(width, width))
}
#[inline]
pub fn kind(&self) -> ShapeKind {
self.kind
}
#[inline]
pub fn dims(&self) -> Vec2 {
self.dims
}
#[inline]
pub fn place(self, pos: Vec2) -> PlacedShape {
PlacedShape::new(pos, self)
}
pub(crate) fn advance(&self, resize_vel: Vec2, elapsed: f64) -> Shape {
Shape::with_any_dims(self.kind, self.dims + resize_vel * elapsed)
}
}
#[derive(PartialEq, Copy, Clone, Debug)]
pub struct PlacedShape {
pub pos: Vec2,
pub shape: Shape
}
impl PlacedShape {
#[inline]
pub fn new(pos: Vec2, shape: Shape) -> PlacedShape {
PlacedShape { pos: pos, shape: shape }
}
#[inline]
pub fn kind(&self) -> ShapeKind {
self.shape.kind()
}
#[inline]
pub fn dims(&self) -> Vec2 {
self.shape.dims()
}
pub fn min_x(&self) -> f64 { self.bounds_left() }
pub fn min_y(&self) -> f64 { self.bounds_bottom() }
pub fn max_x(&self) -> f64 { self.bounds_right() }
pub fn max_y(&self) -> f64 { self.bounds_top() }
pub fn overlaps(&self, other: &PlacedShape) -> bool {
self.normal_from(other).len() >= 0.0
}
pub fn normal_from(&self, other: &PlacedShape) -> DirVec2 {
match (self.kind(), other.kind()) {
(ShapeKind::Rect, ShapeKind::Rect) => normals::rect_rect_normal(self, other),
(ShapeKind::Rect, ShapeKind::Circle) => normals::rect_circle_normal(self, other),
(ShapeKind::Circle, ShapeKind::Rect) => normals::rect_circle_normal(other, self).flip(),
(ShapeKind::Circle, ShapeKind::Circle) => normals::circle_circle_normal(self, other),
}
}
pub fn masked_normal_from(&self, other: &PlacedShape, mask: CardMask) -> DirVec2 {
match (self.kind(), other.kind()) {
(ShapeKind::Rect, ShapeKind::Rect) => normals::masked_rect_rect_normal(self, other, mask),
(ShapeKind::Rect, ShapeKind::Circle) => normals::masked_rect_circle_normal(self, other, mask),
(ShapeKind::Circle, ShapeKind::Rect) => normals::masked_rect_circle_normal(other, self, mask.flip()).flip(),
(ShapeKind::Circle, ShapeKind::Circle) => normals::masked_circle_circle_normal(self, other, mask),
}
}
pub fn contact_point(&self, other: &PlacedShape) -> Vec2 {
match (self.kind(), other.kind()) {
(ShapeKind::Rect, ShapeKind::Rect) => normals::rect_rect_contact(self, other),
(ShapeKind::Circle, _) => normals::circle_any_contact(self, other),
(ShapeKind::Rect, ShapeKind::Circle) => normals::circle_any_contact(other, self),
}
}
#[inline]
pub fn moving(self, vel: Vec2) -> Hitbox {
Hitbox::new(self, HbVel::moving(vel))
}
#[inline]
pub fn moving_until(self, vel: Vec2, end_time: f64) -> Hitbox {
Hitbox::new(self, HbVel::moving_until(vel, end_time))
}
#[inline]
pub fn still(self) -> Hitbox {
Hitbox::new(self, HbVel::still())
}
#[inline]
pub fn still_until(self, end_time: f64) -> Hitbox {
Hitbox::new(self, HbVel::still_until(end_time))
}
pub(crate) fn sector(&self, point: Vec2) -> Sector {
let x = interval_sector(self.min_x(), self.max_x(), point.x);
let y = interval_sector(self.min_y(), self.max_y(), point.y);
Sector::new(x, y)
}
pub(crate) fn as_rect(&self) -> PlacedShape {
PlacedShape::new(self.pos, Shape::rect(self.shape.dims()))
}
pub(crate) fn bounding_box(&self, other: &PlacedShape) -> PlacedShape {
let right = self.max_x().max(other.max_x());
let top = self.max_y().max(other.max_y());
let left = self.min_x().min(other.min_x());
let bottom = self.min_y().min(other.min_y());
let shape = Shape::rect(v2(right - left, top - bottom));
let pos = v2(left + shape.dims().x * 0.5, bottom + shape.dims().y * 0.5);
PlacedShape::new(pos, shape)
}
pub(crate) fn advance(&self, vel: Vec2, resize_vel: Vec2, elapsed: f64) -> PlacedShape {
PlacedShape::new(self.pos + vel * elapsed, self.shape.advance(resize_vel, elapsed))
}
}
pub(crate) trait PlacedBounds {
fn bounds_center(&self) -> &Vec2;
fn bounds_dims(&self) -> &Vec2;
fn bounds_bottom(&self) -> f64 { self.bounds_center().y - self.bounds_dims().y * 0.5 }
fn bounds_left(&self) -> f64 { self.bounds_center().x - self.bounds_dims().x * 0.5 }
fn bounds_top(&self) -> f64 { self.bounds_center().y + self.bounds_dims().y * 0.5 }
fn bounds_right(&self) -> f64 { self.bounds_center().x + self.bounds_dims().x * 0.5 }
fn edge(&self, card: Card) -> f64 {
match card {
Card::MinusY => -self.bounds_bottom(),
Card::MinusX => -self.bounds_left(),
Card::PlusY => self.bounds_top(),
Card::PlusX => self.bounds_right(),
}
}
fn max_edge(&self) -> f64 {
Card::values().iter()
.map(|&card| self.edge(card).abs())
.max_by_key(|&edge| n64(edge))
.unwrap()
}
fn card_overlap(&self, src: &Self, card: Card) -> f64 {
src.edge(card) + self.edge(card.flip())
}
fn corner(&self, sector: Sector) -> Vec2 {
let x = match sector.x {
Ordering::Less => self.bounds_left(),
Ordering::Greater => self.bounds_right(),
Ordering::Equal => panic!("expected corner sector")
};
let y = match sector.y {
Ordering::Less => self.bounds_bottom(),
Ordering::Greater => self.bounds_top(),
Ordering::Equal => panic!("expected corner sector")
};
v2(x, y)
}
}
impl PlacedBounds for PlacedShape {
fn bounds_center(&self) -> &Vec2 { &self.pos }
fn bounds_dims(&self) -> &Vec2 { &self.shape.dims }
}
fn interval_sector(left: f64, right: f64, val: f64) -> Ordering {
if val < left {
Ordering::Less
} else if val > right {
Ordering::Greater
} else {
Ordering::Equal
}
}
#[derive(PartialEq, Eq, Copy, Clone)]
pub(crate) struct Sector {
x: Ordering,
y: Ordering
}
impl Sector {
pub fn new(x: Ordering, y: Ordering) -> Sector {
Sector { x: x, y: y }
}
pub fn is_corner(&self) -> bool {
self.x != Ordering::Equal && self.y != Ordering::Equal
}
pub fn corner_cards(&self) -> Option<(Card, Card)> {
if self.is_corner() {
Some((
if self.x == Ordering::Greater { Card::PlusX } else { Card::MinusX },
if self.y == Ordering::Greater { Card::PlusY } else { Card::MinusY },
))
} else {
None
}
}
}