use crate::{Point, Size};
#[derive(Copy, Clone, Default, Debug, PartialEq)]
pub struct Rectangle {
position: Point,
size: Size,
}
impl Rectangle {
pub fn new(position: impl Into<Point>, size: impl Into<Size>) -> Self {
Rectangle {
position: position.into(),
size: size.into(),
}
}
pub fn x(&self) -> f64 {
self.position.x()
}
pub fn set_x(&mut self, x: impl Into<f64>) {
self.position.set_x(x);
}
pub fn y(&self) -> f64 {
self.position.y()
}
pub fn set_y(&mut self, y: impl Into<f64>) {
self.position.set_y(y);
}
pub fn position(&self) -> Point {
self.position
}
pub fn set_position(&mut self, position: impl Into<Point>) {
self.position = position.into();
}
pub fn width(&self) -> f64 {
self.size.width()
}
pub fn set_width(&mut self, width: impl Into<f64>) {
self.size.set_width(width.into());
}
pub fn height(&self) -> f64 {
self.size.height()
}
pub fn set_height(&mut self, height: impl Into<f64>) {
self.size.set_height(height.into());
}
pub fn size(&self) -> Size {
self.size
}
pub fn set_size(&mut self, width: impl Into<f64>, height: impl Into<f64>) {
self.size.set_width(width.into());
self.size.set_height(height.into());
}
pub fn contains(&self, point: impl Into<Point>) -> bool {
let point: Point = point.into();
point.x() >= self.x()
&& point.x() <= self.x() + self.width()
&& point.y() >= self.y()
&& point.y() <= self.y() + self.height()
}
pub fn contains_rect(&self, rect: &Rectangle) -> bool {
let p1 = rect.position();
let p2 = (p1.x() + rect.width(), p1.y() + rect.height());
self.contains(p1) && self.contains(p2)
}
pub fn intersects(&self, rect: &Rectangle) -> bool {
!(rect.x() > (self.x() + self.width())
|| self.x() > (rect.x() + rect.width())
|| rect.y() > (self.y() + self.height())
|| self.y() > (rect.y() + rect.height()))
}
pub fn join_with_rectangle(&mut self, other: &Rectangle) {
if other.x() < self.x() {
self.set_width(self.width() + self.x() - other.x());
self.set_x(other.x());
}
if other.y() < self.y() {
self.set_height(self.height() + self.y() - other.y());
self.set_y(other.y());
}
if other.x() + other.width() > self.x() + self.width() {
self.set_width(other.x() + other.width() - self.x());
}
if other.y() + other.height() > self.y() + self.height() {
self.set_height(other.y() + other.height() - self.y());
}
}
pub fn join_with_point(&mut self, point: &Point) {
if point.x() < self.x() {
self.set_width(self.width() + self.x() - point.x());
self.set_x(point.x());
}
if point.y() < self.y() {
self.set_height(self.height() + self.y() - point.y());
self.set_y(point.y());
}
if point.x() > self.x() + self.width() {
self.set_width(point.x() - self.x());
}
if point.y() > self.y() + self.height() {
self.set_height(point.y() - self.y());
}
}
pub fn box_into(&mut self, container: Rectangle) {
if self.x() < container.x() {
self.set_width(self.width() - (container.x() - self.x()));
self.set_x(container.x());
}
if self.y() < container.y() {
self.set_height(self.height() - (container.y() - self.y()));
self.set_y(container.y());
}
if self.x() + self.width() > container.x() + container.width() {
self.set_width(container.width() - container.x() + self.x());
}
if self.y() + self.height() > container.y() + container.height() {
self.set_height(container.height() - container.y() + self.y());
}
}
}
impl From<(Point, Size)> for Rectangle {
fn from(t: (Point, Size)) -> Self {
Rectangle::new(t.0, t.1)
}
}
impl From<(i32, i32, i32, i32)> for Rectangle {
fn from(t: (i32, i32, i32, i32)) -> Self {
Rectangle::new((t.0 as f64, t.1 as f64), (t.2 as f64, t.3 as f64))
}
}
impl From<(f64, f64, f64, f64)> for Rectangle {
fn from(t: (f64, f64, f64, f64)) -> Self {
Rectangle::new((t.0, t.1), (t.2, t.3))
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
#[test]
fn test_new() {
let rect = Rectangle::new((5.0, 10.0), (20.0, 30.0));
assert!(crate::f64_cmp(rect.x(), 5.0));
assert!(crate::f64_cmp(rect.y(), 10.0));
assert!(crate::f64_cmp(rect.width(), 20.0));
assert!(crate::f64_cmp(rect.height(), 30.0));
}
#[test]
fn test_contains() {
let rect = Rectangle::new((5.0, 10.0), (20.0, 30.0));
let p = Point::new(5.0, 10.0);
assert!(rect.contains(p), "{:?}", p);
let p = Point::new(25.0, 40.0);
assert!(rect.contains(p), "{:?}", p);
let p = Point::new(15.0, 15.0);
assert!(rect.contains(p), "{:?}", p);
let p = Point::new(30.0, 15.0);
assert!(!rect.contains(p), "{:?}", p);
let p = Point::new(15.0, 50.0);
assert!(!rect.contains(p), "{:?}", p);
let p = Point::new(30.0, 40.0);
assert!(!rect.contains(p), "{:?}", p);
}
#[test]
fn test_contains_rect() {
let rect = Rectangle::new((5.0, 10.0), (20.0, 30.0));
let r = Rectangle::new((5.0, 10.0), (20.0, 30.0));
assert!(rect.contains_rect(&r), "{:?}", r);
let r = Rectangle::new((5.0, 20.0), (10.0, 20.0));
assert!(rect.contains_rect(&r), "{:?}", r);
let r = Rectangle::new((5.0, 10.0), (10.0, 20.0));
assert!(rect.contains_rect(&r), "{:?}", r);
let r = Rectangle::new((10.0, 20.0), (5.0, 10.0));
assert!(rect.contains_rect(&r), "{:?}", r);
let r = Rectangle::new((20.0, 25.0), (20.0, 30.0));
assert!(!rect.contains_rect(&r), "{:?}", r);
let r = Rectangle::new((50.0, 100.0), (20.0, 30.0));
assert!(!rect.contains_rect(&r), "{:?}", r);
}
#[test]
fn test_intersects() {
let rect = Rectangle::new((5.0, 10.0), (20.0, 30.0));
let r = Rectangle::new((5.0, 10.0), (20.0, 30.0));
assert!(rect.intersects(&r), "{:?}", r);
let r = Rectangle::new((25.0, 10.0), (20.0, 30.0));
assert!(rect.intersects(&r), "{:?}", r);
let r = Rectangle::new((-15.0, 10.0), (20.0, 30.0));
assert!(rect.intersects(&r), "{:?}", r);
let r = Rectangle::new((5.0, 40.0), (20.0, 30.0));
assert!(rect.intersects(&r), "{:?}", r);
let r = Rectangle::new((5.0, -20.0), (20.0, 30.0));
assert!(rect.intersects(&r), "{:?}", r);
let r = Rectangle::new((30.0, 10.0), (20.0, 30.0));
assert!(!rect.intersects(&r), "{:?}", r);
let r = Rectangle::new((-20.0, 10.0), (20.0, 30.0));
assert!(!rect.intersects(&r), "{:?}", r);
let r = Rectangle::new((5.0, 50.0), (20.0, 30.0));
assert!(!rect.intersects(&r), "{:?}", r);
let r = Rectangle::new((5.0, -30.0), (20.0, 30.0));
assert!(!rect.intersects(&r), "{:?}", r);
}
}