#![allow(deprecated)]
use crate::geom::{about_equal, Circle, Line, Rectangle, Transform, Triangle, Vector};
pub trait Shape {
#[must_use]
fn contains(&self, point: Vector) -> bool;
#[must_use]
fn intersects(&self, line: &Line) -> bool {
self.overlaps(line)
}
#[must_use]
fn overlaps_circle(&self, circle: &Circle) -> bool {
self.overlaps(circle)
}
#[must_use]
fn overlaps_rectangle(&self, rectangle: &Rectangle) -> bool {
self.overlaps(rectangle)
}
#[must_use]
#[deprecated(
since = "0.4.0-alpha0.5",
note = "Use another collision library like `vek` instead; please comment on issue #552 for use-cases other libraries don't solve"
)]
fn overlaps(&self, other: &impl Shape) -> bool;
#[must_use]
fn center(&self) -> Vector;
#[must_use]
fn bounding_box(&self) -> Rectangle;
#[deprecated(
since = "0.4.0-alpha0.5",
note = "Use another collision library like `vek` instead; please comment on issue #552 for use-cases other libraries don't solve"
)]
#[must_use]
fn transformed_bounding_box(&self, transform: Transform) -> Rectangle {
let bb = self.bounding_box();
let transform = Transform::translate(bb.center() + bb.pos)
* transform
* Transform::translate(-bb.pos - bb.center());
let tl = transform * bb.pos;
let tr = transform * (bb.pos + Vector::new(bb.size.x, 0.0));
let bl = transform * (bb.pos + Vector::new(0.0, bb.size.y));
let br = transform * (bb.pos + bb.size);
let min = tr.min(tl).min(bl).min(br);
let max = tr.max(tl).max(bl).max(br);
Rectangle::new(min, max - min)
}
#[must_use]
fn translate(&self, amount: Vector) -> Self
where
Self: Sized;
#[must_use]
fn constrain(&self, outer: &Rectangle) -> Self
where
Self: Sized,
{
let area = self.bounding_box();
let clamped = area.top_left().clamp(
outer.top_left(),
outer.top_left() + outer.size() - area.size(),
);
self.translate(clamped - area.top_left())
}
#[must_use]
fn with_center(&self, center: Vector) -> Self
where
Self: Sized,
{
self.translate(center - self.center())
}
}
impl Shape for Circle {
fn contains(&self, v: Vector) -> bool {
(v - self.center()).len2() < self.radius.powi(2)
}
fn overlaps_circle(&self, c: &Circle) -> bool {
(self.center() - c.center()).len2() < (self.radius + c.radius).powi(2)
}
fn overlaps(&self, shape: &impl Shape) -> bool {
shape.overlaps_circle(self)
}
fn center(&self) -> Vector {
self.pos
}
fn bounding_box(&self) -> Rectangle {
Rectangle::new(
self.pos - Vector::ONE * self.radius,
Vector::ONE * 2.0 * self.radius,
)
}
fn translate(&self, v: Vector) -> Self {
Circle {
pos: self.pos + v,
radius: self.radius,
}
}
}
impl Shape for Rectangle {
fn contains(&self, p: Vector) -> bool {
p.x >= self.x()
&& p.y >= self.y()
&& p.x < self.x() + self.width()
&& p.y < self.y() + self.height()
}
fn overlaps_circle(&self, c: &Circle) -> bool {
(c.center()
.clamp(self.top_left(), self.top_left() + self.size())
- c.center())
.len2()
< c.radius.powi(2)
}
fn overlaps_rectangle(&self, b: &Rectangle) -> bool {
self.x() < b.pos.x + b.size.x
&& self.x() + self.width() > b.pos.x
&& self.y() < b.pos.y + b.size.y
&& self.y() + self.height() > b.pos.y
}
fn intersects(&self, l: &Line) -> bool {
l.overlaps_rectangle(self)
}
fn overlaps(&self, shape: &impl Shape) -> bool {
shape.overlaps_rectangle(self)
}
fn center(&self) -> Vector {
self.pos + self.size / 2.0
}
fn bounding_box(&self) -> Rectangle {
*self
}
fn translate(&self, v: Vector) -> Self {
Rectangle {
pos: self.pos + v,
size: self.size,
}
}
}
#[deprecated(
since = "0.4.0-alpha0.5",
note = "Use another collision library like `vek` instead; please comment on issue #552 for use-cases other libraries don't solve"
)]
impl Shape for Triangle {
fn contains(&self, v: Vector) -> bool {
let t_1 = Triangle::new(v, self.a, self.b);
let t_2 = Triangle::new(v, self.b, self.c);
let t_3 = Triangle::new(v, self.c, self.a);
about_equal(t_1.area() + t_2.area() + t_3.area(), self.area())
}
fn intersects(&self, line: &Line) -> bool {
self.contains(line.a)
|| self.contains(line.b)
|| Line::new(self.a, self.b).intersects(line)
|| Line::new(self.b, self.c).intersects(line)
|| Line::new(self.c, self.a).intersects(line)
}
fn overlaps_circle(&self, circ: &Circle) -> bool {
Line::new(self.a, self.b).overlaps_circle(circ)
|| Line::new(self.b, self.c).overlaps_circle(circ)
|| Line::new(self.c, self.a).overlaps_circle(circ)
}
fn overlaps_rectangle(&self, rect: &Rectangle) -> bool {
Line::new(self.a, self.b).overlaps_rectangle(rect)
|| Line::new(self.b, self.c).overlaps_rectangle(rect)
|| Line::new(self.c, self.a).overlaps_rectangle(rect)
}
fn overlaps(&self, other: &impl Shape) -> bool {
self.contains(other.center())
|| other.intersects(&Line::new(self.a, self.b))
|| other.intersects(&Line::new(self.b, self.c))
|| other.intersects(&Line::new(self.a, self.c))
}
fn center(&self) -> Vector {
(self.a + self.b + self.c) / 3.0
}
fn bounding_box(&self) -> Rectangle {
let min = self.a.min(self.b.min(self.c));
let max = self.a.max(self.b.max(self.c));
Rectangle::new(min, max - min)
}
fn translate(&self, v: Vector) -> Self {
Triangle {
a: self.a + v,
b: self.b + v,
c: self.c + v,
}
}
}
#[deprecated(
since = "0.4.0-alpha0.5",
note = "Use another collision library like `vek` instead; please comment on issue #552 for use-cases other libraries don't solve"
)]
impl Shape for Line {
fn contains(&self, v: Vector) -> bool {
about_equal(
v.distance(self.a) + v.distance(self.b),
self.a.distance(self.b),
)
}
fn intersects(&self, l: &Line) -> bool {
let d1 = ((l.b.x - l.a.x) * (self.a.y - l.a.y) - (l.b.y - l.a.y) * (self.a.x - l.a.x))
/ ((l.b.y - l.a.y) * (self.b.x - self.a.x) - (l.b.x - l.a.x) * (self.b.y - self.a.y));
let d2 = ((self.b.x - self.a.x) * (self.a.y - l.a.y)
- (self.b.y - self.a.y) * (self.a.x - l.a.x))
/ ((l.b.y - l.a.y) * (self.b.x - self.a.x) - (l.b.x - l.a.x) * (self.b.y - self.a.y));
if d1 >= 0.0 && d1 <= 1.0 && d2 >= 0.0 && d2 <= 1.0 {
return true;
}
false
}
fn overlaps_circle(&self, c: &Circle) -> bool {
if c.contains(self.a) || c.contains(self.b) {
true
} else {
let length = self.b - self.a;
let dot = length.dot(c.center() - self.a) / length.len2();
let closest = self.a + length * dot;
self.contains(closest) && (closest - c.center()).len2() <= c.radius.powi(2)
}
}
fn overlaps_rectangle(&self, b: &Rectangle) -> bool {
let top_left = b.top_left();
let top_right = top_left + Vector::new(b.width(), 0.0);
let bottom_left = top_left + Vector::new(0.0, b.height());
let bottom_right = top_left + Vector::new(b.width(), b.height());
b.contains(self.a)
|| b.contains(self.b)
|| self.intersects(&Line::new(top_left, top_right))
|| self.intersects(&Line::new(top_left, bottom_left))
|| self.intersects(&Line::new(top_right, bottom_right))
|| self.intersects(&Line::new(bottom_left, bottom_right))
}
fn overlaps(&self, shape: &impl Shape) -> bool {
shape.intersects(self)
}
fn center(&self) -> Vector {
(self.a + self.b) / 2.0
}
fn bounding_box(&self) -> Rectangle {
let min = self.a.min(self.b);
let max = self.a.max(self.b);
Rectangle::new(min, max - min)
}
fn translate(&self, v: Vector) -> Self {
Line {
a: self.a + v,
b: self.b + v,
t: self.t,
}
}
}
impl Shape for Vector {
fn contains(&self, v: Vector) -> bool {
*self == v
}
fn overlaps(&self, shape: &impl Shape) -> bool {
shape.contains(*self)
}
fn center(&self) -> Vector {
*self
}
fn bounding_box(&self) -> Rectangle {
Rectangle::new(*self, Vector::ONE)
}
fn translate(&self, v: Vector) -> Vector {
*self + v
}
}