use super::{about_equal, Circle, Line, Vector};
use std::cmp::{Eq, PartialEq};
#[derive(Clone, Copy, Default, Debug)]
pub struct Rectangle {
pub x: f32,
pub y: f32,
pub width: f32,
pub height: f32,
}
impl Rectangle {
pub fn new(x: f32, y: f32, width: f32, height: f32) -> Rectangle {
Rectangle {
x: x,
y: y,
width: width,
height: height,
}
}
pub fn newv(pos: Vector, size: Vector) -> Rectangle {
Rectangle::new(pos.x, pos.y, size.x, size.y)
}
pub fn newi(x: i32, y: i32, width: i32, height: i32) -> Rectangle {
Rectangle::new(x as f32, y as f32, width as f32, height as f32)
}
pub fn new_sized(width: f32, height: f32) -> Rectangle {
Rectangle::new(0f32, 0f32, width, height)
}
pub fn newi_sized(width: i32, height: i32) -> Rectangle {
Rectangle::newi(0, 0, width, height)
}
pub fn newv_sized(size: Vector) -> Rectangle {
Rectangle::newv(Vector::zero(), size)
}
pub fn top_left(self) -> Vector {
Vector::new(self.x, self.y)
}
pub fn size(self) -> Vector {
Vector::new(self.width, self.height)
}
pub fn center(self) -> Vector {
self.top_left() + self.size() / 2
}
pub fn contains(self, v: Vector) -> bool {
v.x >= self.x && v.y >= self.y && v.x < self.x + self.width && v.y < self.y + self.height
}
pub fn overlaps_rect(self, b: Rectangle) -> bool {
self.x < b.x + b.width && self.x + self.width > b.x && self.y < b.y + b.height &&
self.y + self.height > b.y
}
pub fn overlaps_circ(self, c: Circle) -> bool {
(c.center().clamp(self.top_left(), self.top_left() + self.size()) - c.center()).len2() < c.radius.powi(2)
}
pub fn constrain(self, outer: Rectangle) -> Rectangle {
Rectangle::newv(self.top_left().clamp(
outer.top_left(), outer.top_left() + outer.size() - self.size()
), self.size())
}
pub fn translate(self, v: Vector) -> Rectangle {
Rectangle::new(self.x + v.x, self.y + v.y, self.width, self.height)
}
pub fn with_center(self, v: Vector) -> Rectangle {
self.translate(v - self.center())
}
pub fn top(self) -> Line {
Line::new(self.top_left(), self.top_left() + self.size().x_comp())
}
pub fn left(self) -> Line {
Line::new(self.top_left(), self.top_left() + self.size().y_comp())
}
pub fn bottom(self) -> Line {
Line::new(self.top_left() + self.size().y_comp(), self.top_left() + self.size())
}
pub fn right(self) -> Line {
Line::new(self.top_left() + self.size().x_comp(), self.top_left() + self.size())
}
pub fn intersects(self, l: Line) -> bool {
self.contains(l.start) || self.contains(l.end) || self.top().intersects(l) ||
self.left().intersects(l) || self.right().intersects(l) || self.bottom().intersects(l)
}
}
impl PartialEq for Rectangle {
fn eq(&self, other: &Rectangle) -> bool {
about_equal(self.x, other.x) && about_equal(self.y, other.y) && about_equal(self.width, other.width)
&& about_equal(self.height, other.height)
}
}
impl Eq for Rectangle {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn overlap() {
let a = Rectangle::newi_sized(32, 32);
let b = Rectangle::newi(16, 16, 32, 32);
let c = Rectangle::newi(50, 50, 5, 5);
assert!(a.overlaps_rect(b));
assert!(!a.overlaps_rect(c));
}
#[test]
fn contains() {
let rect = Rectangle::newi_sized(32, 32);
let vec1 = Vector::newi(5, 5);
let vec2 = Vector::newi(33, 1);
assert!(rect.contains(vec1));
assert!(!rect.contains(vec2));
}
#[test]
fn constraint() {
let constraint = Rectangle::newi_sized(10, 10);
let a = Rectangle::newi(-1, 3, 5, 5);
let b = Rectangle::newi(4, 4, 8, 3);
let a = a.constrain(constraint);
assert_eq!(a.top_left(), Vector::newi(0, 3));
let b = b.constrain(constraint);
assert_eq!(b.top_left(), Vector::newi(2, 4));
}
#[test]
fn translate() {
let a = Rectangle::newi(10, 10, 5, 5);
let v = Vector::newi(1, -1);
let translated = a.translate(v);
assert_eq!(a.top_left() + v, translated.top_left());
}
#[test]
fn intersect() {
let line1 = Line::new(Vector::newi(0, 0), Vector::newi(32, 32));
let line2 = Line::new(Vector::newi(0, 32), Vector::newi(32, 0));
let line3 = Line::new(Vector::newi(32, 32), Vector::newi(64, 64));
let line4 = Line::new(Vector::newi(100, 100), Vector::newi(1000, 1000));
let rect = Rectangle::newv_sized(Vector::newi(32, 32));
assert!(rect.intersects(line1));
assert!(rect.intersects(line2));
assert!(rect.intersects(line3));
assert!(!rect.intersects(line4));
}
}