rust-java-functional-progra.../src/geometry.rs

224 lines
5.0 KiB
Rust

use std::ops::Add;
#[derive(Debug, Copy, Clone)]
pub struct Point {
x: u32,
y: u32,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct Rectangle {
top_left: Point,
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 }
}
pub fn with_x(&self, x: u32) -> Self {
Self { x, y: self.y }
}
pub fn with_y(&self, y: u32) -> Self {
Self { x: self.x, y }
}
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,
}
}
}
// These can be derived too, but implement them for demonstration.
impl PartialEq for Point {
fn eq(&self, other: &Self) -> bool {
self.x == other.x && self.y == other.y
}
}
// Marker trait to indicate reflexivity of equality (not just transitivity & symmetry)
impl Eq for Point {}
impl Rectangle {
fn from_corner_width_height(top_left: Point, width: u32, height: u32) -> Rectangle {
Rectangle {
top_left,
width,
height,
}
}
fn from_corners(corner_a: Point, corner_b: Point) -> Rectangle {
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;
Rectangle {
top_left: Point::new(left, top),
width,
height,
}
}
fn at_origin_with_size(width: u32, height: u32) -> Rectangle {
Rectangle {
top_left: Point::origin(),
width,
height,
}
}
fn width(&self) -> u32 {
self.width
}
fn height(&self) -> u32 {
self.height
}
fn with_width(&self, width: u32) -> Rectangle {
Rectangle {
top_left: self.top_left,
width,
height: self.height,
}
}
fn with_height(&self, height: u32) -> Rectangle {
Rectangle {
top_left: self.top_left,
width: self.width,
height,
}
}
fn top_left(&self) -> Point {
self.top_left
}
fn top_right(&self) -> Point {
Point::new(self.top_left.x() + self.width, self.top_left.y())
}
fn bottom_right(&self) -> Point {
Point::new(
self.top_left.x() + self.width,
self.top_left.y() + self.height,
)
}
fn bottom_left(&self) -> Point {
Point::new(self.top_left.x(), self.top_left.y() + self.height)
}
fn area(&self) -> u32 {
self.width * self.height
}
fn intersects(&self, other: &Rectangle) -> bool {
!(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()))
}
}
#[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() {
assert_eq!(Point::new(42, 52).with_x(12), Point::new(12, 52));
}
#[test]
fn test_point_set_y() {
assert_eq!(Point::new(42, 52).with_y(12), Point::new(42, 12));
}
#[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)
);
}
}