2019-12-30 17:51:06 +00:00
|
|
|
use std::ops::Add;
|
|
|
|
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
|
|
pub struct Point {
|
|
|
|
x: u32,
|
|
|
|
y: u32,
|
|
|
|
}
|
|
|
|
|
2019-12-31 00:31:18 +00:00
|
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
2019-12-30 17:51:06 +00:00
|
|
|
pub struct Rectangle {
|
2019-12-31 00:23:21 +00:00
|
|
|
top_left: Point,
|
2019-12-30 17:51:06 +00:00
|
|
|
width: u32,
|
|
|
|
height: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Point {
|
|
|
|
pub fn new(x: u32, y: u32) -> Self {
|
|
|
|
Self { x, y }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn origin() -> Self {
|
|
|
|
Self { x: 0, y: 0 }
|
|
|
|
}
|
|
|
|
|
2019-12-31 00:08:58 +00:00
|
|
|
pub fn with_x(&self, x: u32) -> Self {
|
|
|
|
Self { x, y: self.y }
|
2019-12-30 17:51:06 +00:00
|
|
|
}
|
|
|
|
|
2019-12-31 00:08:58 +00:00
|
|
|
pub fn with_y(&self, y: u32) -> Self {
|
|
|
|
Self { x: self.x, y }
|
2019-12-30 17:51:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn x(&self) -> u32 {
|
|
|
|
self.x
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn y(&self) -> u32 {
|
|
|
|
self.y
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn left_of(&self, other: &Self) -> bool {
|
|
|
|
self.x < other.x
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn right_of(&self, other: &Self) -> bool {
|
|
|
|
self.x > other.x
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn above(&self, other: &Self) -> bool {
|
|
|
|
self.y < other.y
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn below(&self, other: &Self) -> bool {
|
|
|
|
self.y > other.y
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Add for Point {
|
|
|
|
type Output = Point;
|
|
|
|
|
|
|
|
fn add(self, other: Point) -> Point {
|
|
|
|
Point {
|
|
|
|
x: self.x + other.x,
|
|
|
|
y: self.y + other.y,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-31 00:11:11 +00:00
|
|
|
// These can be derived too, but implement them for demonstration.
|
2019-12-30 17:51:06 +00:00
|
|
|
impl PartialEq for Point {
|
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
self.x == other.x && self.y == other.y
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-31 00:31:28 +00:00
|
|
|
// Marker trait to indicate reflexivity of equality (not just transitivity & symmetry)
|
2019-12-30 17:51:06 +00:00
|
|
|
impl Eq for Point {}
|
|
|
|
|
2019-12-31 00:21:43 +00:00
|
|
|
impl Rectangle {
|
2019-12-31 01:32:02 +00:00
|
|
|
pub fn from_corner_width_height(top_left: Point, width: u32, height: u32) -> Rectangle {
|
2019-12-31 00:21:43 +00:00
|
|
|
Rectangle {
|
2019-12-31 00:23:21 +00:00
|
|
|
top_left,
|
2019-12-31 00:21:43 +00:00
|
|
|
width,
|
|
|
|
height,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-31 01:32:02 +00:00
|
|
|
pub fn from_corners(corner_a: Point, corner_b: Point) -> Rectangle {
|
2019-12-31 00:21:43 +00:00
|
|
|
let left = corner_a.x().min(corner_b.x());
|
|
|
|
let top = corner_a.y().min(corner_b.y());
|
|
|
|
|
|
|
|
let width = (corner_a.x() as i64 - corner_b.x() as i64).abs() as u32;
|
|
|
|
let height = (corner_a.y() as i64 - corner_b.y() as i64).abs() as u32;
|
|
|
|
|
2019-12-31 00:54:00 +00:00
|
|
|
Rectangle::from_corner_width_height(Point::new(left, top), width, height)
|
2019-12-31 00:21:43 +00:00
|
|
|
}
|
|
|
|
|
2019-12-31 01:32:02 +00:00
|
|
|
pub fn at_origin_with_size(width: u32, height: u32) -> Rectangle {
|
2019-12-31 00:54:00 +00:00
|
|
|
Rectangle::from_corner_width_height(Point::origin(), width, height)
|
2019-12-31 00:21:43 +00:00
|
|
|
}
|
|
|
|
|
2019-12-31 01:32:02 +00:00
|
|
|
pub fn width(&self) -> u32 {
|
2019-12-31 00:21:43 +00:00
|
|
|
self.width
|
|
|
|
}
|
|
|
|
|
2019-12-31 01:32:02 +00:00
|
|
|
pub fn height(&self) -> u32 {
|
2019-12-31 00:21:43 +00:00
|
|
|
self.height
|
|
|
|
}
|
|
|
|
|
2019-12-31 01:32:02 +00:00
|
|
|
pub fn with_width(&self, width: u32) -> Rectangle {
|
2019-12-31 00:21:43 +00:00
|
|
|
Rectangle {
|
2019-12-31 00:23:21 +00:00
|
|
|
top_left: self.top_left,
|
2019-12-31 00:21:43 +00:00
|
|
|
width,
|
|
|
|
height: self.height,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-31 01:32:02 +00:00
|
|
|
pub fn with_height(&self, height: u32) -> Rectangle {
|
2019-12-31 00:21:43 +00:00
|
|
|
Rectangle {
|
2019-12-31 00:23:21 +00:00
|
|
|
top_left: self.top_left,
|
2019-12-31 00:21:43 +00:00
|
|
|
width: self.width,
|
|
|
|
height,
|
|
|
|
}
|
|
|
|
}
|
2019-12-31 00:29:05 +00:00
|
|
|
|
2019-12-31 01:32:02 +00:00
|
|
|
pub fn top_left(&self) -> Point {
|
2019-12-31 00:29:05 +00:00
|
|
|
self.top_left
|
|
|
|
}
|
|
|
|
|
2019-12-31 01:32:02 +00:00
|
|
|
pub fn top_right(&self) -> Point {
|
2019-12-31 00:29:05 +00:00
|
|
|
Point::new(self.top_left.x() + self.width, self.top_left.y())
|
|
|
|
}
|
|
|
|
|
2019-12-31 01:32:02 +00:00
|
|
|
pub fn bottom_right(&self) -> Point {
|
2019-12-31 00:29:05 +00:00
|
|
|
Point::new(
|
|
|
|
self.top_left.x() + self.width,
|
|
|
|
self.top_left.y() + self.height,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-12-31 01:32:02 +00:00
|
|
|
pub fn bottom_left(&self) -> Point {
|
2019-12-31 00:29:05 +00:00
|
|
|
Point::new(self.top_left.x(), self.top_left.y() + self.height)
|
|
|
|
}
|
|
|
|
|
2019-12-31 01:32:02 +00:00
|
|
|
pub fn area(&self) -> u32 {
|
2019-12-31 00:29:05 +00:00
|
|
|
self.width * self.height
|
|
|
|
}
|
|
|
|
|
2019-12-31 01:32:02 +00:00
|
|
|
pub fn intersects(&self, other: &Rectangle) -> bool {
|
2019-12-31 00:29:05 +00:00
|
|
|
!(self.top_left().right_of(&other.top_right())
|
|
|
|
|| self.top_left().below(&other.bottom_left())
|
|
|
|
|| self.top_right().left_of(&other.top_left())
|
|
|
|
|| self.bottom_left().above(&other.top_left()))
|
|
|
|
}
|
2019-12-31 00:21:43 +00:00
|
|
|
}
|
|
|
|
|
2019-12-30 17:51:06 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_point_get_x() {
|
|
|
|
assert_eq!(Point::new(42, 52).x(), 42);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_point_get_y() {
|
|
|
|
assert_eq!(Point::new(42, 52).y(), 52);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_point_set_x() {
|
2019-12-31 00:08:58 +00:00
|
|
|
assert_eq!(Point::new(42, 52).with_x(12), Point::new(12, 52));
|
2019-12-30 17:51:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_point_set_y() {
|
2019-12-31 00:08:58 +00:00
|
|
|
assert_eq!(Point::new(42, 52).with_y(12), Point::new(42, 12));
|
2019-12-30 17:51:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_point_left_of() {
|
|
|
|
assert!(Point::new(10, 20).left_of(&Point::new(20, 20)));
|
|
|
|
assert!(!Point::new(10, 11).left_of(&Point::new(10, 11)));
|
|
|
|
assert!(!Point::new(10, 11).left_of(&Point::new(8, 11)));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_point_right_of() {
|
|
|
|
assert!(Point::new(10, 20).right_of(&Point::new(0, 20)));
|
|
|
|
assert!(!Point::new(10, 11).right_of(&Point::new(10, 11)));
|
|
|
|
assert!(!Point::new(10, 11).right_of(&Point::new(12, 11)));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_point_above() {
|
|
|
|
assert!(Point::new(1, 1).above(&Point::new(1, 2)));
|
|
|
|
assert!(!Point::new(10, 11).above(&Point::origin()));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_point_below() {
|
|
|
|
assert!(Point::new(1, 1).below(&Point::new(1, 0)));
|
|
|
|
assert!(!Point::new(10, 11).below(&Point::new(12, 11)));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_point_add() {
|
|
|
|
assert_eq!(Point::new(1, 1) + Point::new(10, 10), Point::new(11, 11));
|
|
|
|
assert_eq!(
|
|
|
|
Point::new(10, 0) + Point::new(0, 10),
|
|
|
|
Point::new(0, 10) + Point::new(10, 0)
|
|
|
|
);
|
|
|
|
}
|
2019-12-31 00:52:31 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_rectangle_get_width() {
|
|
|
|
assert_eq!(
|
|
|
|
Rectangle::from_corners(Point::new(1, 1), Point::new(11, 11)).width(),
|
|
|
|
10
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
Rectangle::from_corner_width_height(Point::new(1, 1), 20, 30).width(),
|
|
|
|
20
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_rectangle_get_height() {
|
|
|
|
assert_eq!(
|
|
|
|
Rectangle::from_corners(Point::new(1, 0), Point::new(11, 11)).height(),
|
|
|
|
11
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
Rectangle::from_corner_width_height(Point::new(7, 6), 20, 30).height(),
|
|
|
|
30
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_rectangle_set_width() {
|
|
|
|
assert_eq!(
|
|
|
|
Rectangle::from_corner_width_height(Point::new(1, 2), 3, 4).with_width(5),
|
|
|
|
Rectangle::from_corner_width_height(Point::new(1, 2), 5, 4)
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
Rectangle::from_corners(Point::new(1, 2), Point::new(6, 7)).with_width(10),
|
|
|
|
Rectangle::from_corners(Point::new(1, 2), Point::new(11, 7))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_rectangle_set_height() {
|
|
|
|
assert_eq!(
|
|
|
|
Rectangle::from_corner_width_height(Point::new(1, 2), 3, 4).with_height(5),
|
|
|
|
Rectangle::from_corner_width_height(Point::new(1, 2), 3, 5)
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
Rectangle::from_corners(Point::new(1, 2), Point::new(6, 7)).with_height(10),
|
|
|
|
Rectangle::from_corners(Point::new(1, 2), Point::new(6, 12))
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_rectangle_get_top_left() {
|
|
|
|
assert_eq!(
|
|
|
|
Point::new(1, 2),
|
|
|
|
Rectangle::from_corner_width_height(Point::new(1, 2), 3, 4).top_left()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_rectangle_get_top_right() {
|
|
|
|
assert_eq!(
|
|
|
|
Point::new(4, 2),
|
|
|
|
Rectangle::from_corner_width_height(Point::new(1, 2), 3, 4).top_right()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_rectangle_get_bottom_left() {
|
|
|
|
assert_eq!(
|
|
|
|
Point::new(1, 6),
|
|
|
|
Rectangle::from_corner_width_height(Point::new(1, 2), 3, 4).bottom_left()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_rectangle_get_bottom_right() {
|
|
|
|
assert_eq!(
|
|
|
|
Point::new(4, 6),
|
|
|
|
Rectangle::from_corner_width_height(Point::new(1, 2), 3, 4).bottom_right()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_rectangle_get_area() {
|
|
|
|
assert_eq!(100, Rectangle::at_origin_with_size(10, 10).area());
|
|
|
|
assert_eq!(
|
|
|
|
30,
|
|
|
|
Rectangle::from_corners(Point::origin(), Point::new(6, 5)).area()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_rectangle_intersect_1() {
|
|
|
|
let r1 = Rectangle::from_corner_width_height(Point::new(0, 0), 10, 10);
|
|
|
|
let r2 = Rectangle::from_corner_width_height(Point::new(8, 0), 10, 10);
|
|
|
|
let r3 = Rectangle::from_corner_width_height(Point::new(0, 8), 10, 10);
|
|
|
|
let r4 = Rectangle::from_corner_width_height(Point::new(8, 8), 10, 10);
|
|
|
|
|
|
|
|
let r5 = Rectangle::from_corner_width_height(Point::new(0, 100), 5, 5);
|
|
|
|
let r6 = Rectangle::from_corner_width_height(Point::new(100, 100), 5, 5);
|
|
|
|
|
|
|
|
assert!(r1.intersects(&r2));
|
|
|
|
assert!(r1.intersects(&r3));
|
|
|
|
assert!(r1.intersects(&r4));
|
|
|
|
assert!(r2.intersects(&r3));
|
|
|
|
assert!(r2.intersects(&r4));
|
|
|
|
assert!(r3.intersects(&r4));
|
|
|
|
assert!(r1.intersects(&r1));
|
|
|
|
assert!(r2.intersects(&r2));
|
|
|
|
assert!(r3.intersects(&r3));
|
|
|
|
assert!(r4.intersects(&r4));
|
|
|
|
|
|
|
|
assert!(!r1.intersects(&r5));
|
|
|
|
assert!(!r2.intersects(&r5));
|
|
|
|
assert!(!r3.intersects(&r5));
|
|
|
|
assert!(!r4.intersects(&r5));
|
|
|
|
|
|
|
|
assert!(!r1.intersects(&r6));
|
|
|
|
assert!(!r2.intersects(&r6));
|
|
|
|
assert!(!r3.intersects(&r6));
|
|
|
|
assert!(!r4.intersects(&r6));
|
|
|
|
|
|
|
|
assert!(!r5.intersects(&r6));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_rectangle_intersect_2() {
|
|
|
|
assert!(Rectangle::at_origin_with_size(3, 3).intersects(
|
|
|
|
&Rectangle::from_corner_width_height(Point::new(3, 3), 10, 10)
|
|
|
|
));
|
|
|
|
assert!(Rectangle::at_origin_with_size(3, 3).intersects(
|
|
|
|
&Rectangle::from_corner_width_height(Point::new(3, 0), 10, 10)
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_rectangle_intersect_3() {
|
|
|
|
assert!(!Rectangle::at_origin_with_size(3, 3).intersects(
|
|
|
|
&Rectangle::from_corner_width_height(Point::new(4, 4), 10, 10)
|
|
|
|
));
|
|
|
|
}
|
2019-12-30 17:51:06 +00:00
|
|
|
}
|