#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, Default)]
pub struct Point {
pub x: f32,
pub y: f32,
}
impl Point {
pub fn new(x: f32, y: f32) -> Self {
Self { x, y }
}
}
#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, Default)]
pub struct Rect {
pub x: f32,
pub y: f32,
pub width: f32,
pub height: f32,
}
impl Rect {
pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
let (nx, nw) = if width < 0.0 {
(x + width, -width)
} else {
(x, width)
};
let (ny, nh) = if height < 0.0 {
(y + height, -height)
} else {
(y, height)
};
Self {
x: nx,
y: ny,
width: nw,
height: nh,
}
}
pub fn normalize(self) -> Self {
Self::new(self.x, self.y, self.width, self.height)
}
pub fn from_points(x0: f32, y0: f32, x1: f32, y1: f32) -> Self {
Self {
x: x0,
y: y0,
width: x1 - x0,
height: y1 - y0,
}
}
pub fn left(&self) -> f32 {
self.x
}
pub fn right(&self) -> f32 {
self.x + self.width
}
pub fn top(&self) -> f32 {
self.y
}
pub fn bottom(&self) -> f32 {
self.y + self.height
}
pub fn center(&self) -> Point {
Point {
x: self.x + self.width / 2.0,
y: self.y + self.height / 2.0,
}
}
pub fn intersects(&self, other: &Rect) -> bool {
self.left() < other.right()
&& self.right() > other.left()
&& self.top() < other.bottom()
&& self.bottom() > other.top()
}
pub fn intersection(&self, other: &Rect) -> Option<Rect> {
if !self.intersects(other) {
return None;
}
let x = self.left().max(other.left());
let y = self.top().max(other.top());
let right = self.right().min(other.right());
let bottom = self.bottom().min(other.bottom());
Some(Rect::new(x, y, right - x, bottom - y))
}
pub fn contains_point(&self, p: &Point) -> bool {
p.x >= self.left() && p.x <= self.right() && p.y >= self.top() && p.y <= self.bottom()
}
pub fn contains_rect(&self, other: &Rect) -> bool {
other.left() >= self.left()
&& other.right() <= self.right()
&& other.top() >= self.top()
&& other.bottom() <= self.bottom()
}
pub fn union(&self, other: &Rect) -> Rect {
let x0 = self.left().min(other.left());
let y0 = self.top().min(other.top());
let x1 = self.right().max(other.right());
let y1 = self.bottom().max(other.bottom());
Rect::from_points(x0, y0, x1, y1)
}
pub fn area(&self) -> f32 {
self.width * self.height
}
}
pub fn euclidean_distance(p1: &Point, p2: &Point) -> f32 {
((p2.x - p1.x).powi(2) + (p2.y - p1.y).powi(2)).sqrt()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_point_creation() {
let p = Point::new(10.0, 20.0);
assert_eq!(p.x, 10.0);
assert_eq!(p.y, 20.0);
}
#[test]
fn test_rect_creation() {
let r = Rect::new(5.0, 10.0, 100.0, 50.0);
assert_eq!(r.x, 5.0);
assert_eq!(r.y, 10.0);
assert_eq!(r.width, 100.0);
assert_eq!(r.height, 50.0);
}
#[test]
fn test_rect_from_points() {
let r = Rect::from_points(10.0, 20.0, 110.0, 70.0);
assert_eq!(r.x, 10.0);
assert_eq!(r.y, 20.0);
assert_eq!(r.width, 100.0);
assert_eq!(r.height, 50.0);
}
#[test]
fn test_rect_edges() {
let r = Rect::new(10.0, 20.0, 100.0, 50.0);
assert_eq!(r.left(), 10.0);
assert_eq!(r.right(), 110.0);
assert_eq!(r.top(), 20.0);
assert_eq!(r.bottom(), 70.0);
}
#[test]
fn test_rect_center() {
let r = Rect::new(0.0, 0.0, 100.0, 50.0);
let center = r.center();
assert_eq!(center.x, 50.0);
assert_eq!(center.y, 25.0);
}
#[test]
fn test_rect_intersects() {
let r1 = Rect::new(0.0, 0.0, 100.0, 100.0);
let r2 = Rect::new(50.0, 50.0, 100.0, 100.0);
let r3 = Rect::new(200.0, 200.0, 100.0, 100.0);
assert!(r1.intersects(&r2));
assert!(r2.intersects(&r1));
assert!(!r1.intersects(&r3));
assert!(!r3.intersects(&r1));
}
#[test]
fn test_rect_contains_point() {
let r = Rect::new(0.0, 0.0, 100.0, 100.0);
let p1 = Point::new(50.0, 50.0);
let p2 = Point::new(150.0, 150.0);
let p3 = Point::new(0.0, 0.0); let p4 = Point::new(100.0, 100.0);
assert!(r.contains_point(&p1));
assert!(!r.contains_point(&p2));
assert!(r.contains_point(&p3));
assert!(r.contains_point(&p4));
}
#[test]
fn test_rect_union() {
let r1 = Rect::new(0.0, 0.0, 50.0, 50.0);
let r2 = Rect::new(25.0, 25.0, 50.0, 50.0);
let union = r1.union(&r2);
assert_eq!(union.x, 0.0);
assert_eq!(union.y, 0.0);
assert_eq!(union.right(), 75.0);
assert_eq!(union.bottom(), 75.0);
}
#[test]
fn test_rect_area() {
let r = Rect::new(0.0, 0.0, 100.0, 50.0);
assert_eq!(r.area(), 5000.0);
}
#[test]
fn test_euclidean_distance() {
let p1 = Point::new(0.0, 0.0);
let p2 = Point::new(3.0, 4.0);
assert_eq!(euclidean_distance(&p1, &p2), 5.0);
let p3 = Point::new(1.0, 1.0);
let p4 = Point::new(1.0, 1.0);
assert_eq!(euclidean_distance(&p3, &p4), 0.0);
}
}