1#[derive(Debug, Clone, Copy, PartialEq, Default)]
3pub struct Point {
4 pub x: f64,
6 pub y: f64,
8}
9
10impl Point {
11 #[must_use]
13 pub const fn new(x: f64, y: f64) -> Self {
14 Self { x, y }
15 }
16
17 #[must_use]
19 pub fn lerp(self, other: Self, t: f64) -> Self {
20 Self::new((other.x - self.x).mul_add(t, self.x), (other.y - self.y).mul_add(t, self.y))
21 }
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Default)]
26pub struct Size {
27 pub width: f64,
29 pub height: f64,
31}
32
33impl Size {
34 #[must_use]
36 pub const fn new(width: f64, height: f64) -> Self {
37 Self { width, height }
38 }
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Default)]
43pub struct Rect {
44 pub origin: Point,
46 pub size: Size,
48}
49
50impl Rect {
51 #[must_use]
53 pub const fn new(x: f64, y: f64, width: f64, height: f64) -> Self {
54 Self { origin: Point::new(x, y), size: Size::new(width, height) }
55 }
56
57 #[must_use]
59 pub const fn min_x(&self) -> f64 {
60 self.origin.x
61 }
62
63 #[must_use]
65 pub const fn min_y(&self) -> f64 {
66 self.origin.y
67 }
68
69 #[must_use]
71 pub fn max_x(&self) -> f64 {
72 self.origin.x + self.size.width
73 }
74
75 #[must_use]
77 pub fn max_y(&self) -> f64 {
78 self.origin.y + self.size.height
79 }
80
81 #[must_use]
83 pub fn center(&self) -> Point {
84 Point::new(self.size.width.mul_add(0.5, self.origin.x), self.size.height.mul_add(0.5, self.origin.y))
85 }
86
87 #[must_use]
89 pub fn area(&self) -> f64 {
90 self.size.width.max(0.0) * self.size.height.max(0.0)
91 }
92
93 #[must_use]
95 pub fn is_empty(&self) -> bool {
96 self.size.width <= 0.0 || self.size.height <= 0.0
97 }
98
99 #[must_use]
101 pub fn contains(&self, point: Point) -> bool {
102 point.x >= self.min_x() && point.x < self.max_x() && point.y >= self.min_y() && point.y < self.max_y()
103 }
104
105 #[must_use]
107 pub fn intersection(&self, other: &Self) -> Option<Self> {
108 let x0 = self.min_x().max(other.min_x());
109 let y0 = self.min_y().max(other.min_y());
110 let x1 = self.max_x().min(other.max_x());
111 let y1 = self.max_y().min(other.max_y());
112 (x1 > x0 && y1 > y0).then(|| Self::new(x0, y0, x1 - x0, y1 - y0))
113 }
114
115 #[must_use]
117 pub fn offset(&self, dx: f64, dy: f64) -> Self {
118 Self::new(self.origin.x + dx, self.origin.y + dy, self.size.width, self.size.height)
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn contains_and_center() {
128 let r = Rect::new(10.0, 20.0, 100.0, 50.0);
129 assert!(r.contains(Point::new(10.0, 20.0)));
130 assert!(!r.contains(Point::new(110.0, 20.0)));
131 assert_eq!(r.center(), Point::new(60.0, 45.0));
132 }
133
134 #[test]
135 fn intersection() {
136 let a = Rect::new(0.0, 0.0, 10.0, 10.0);
137 let b = Rect::new(5.0, 5.0, 10.0, 10.0);
138 assert_eq!(a.intersection(&b), Some(Rect::new(5.0, 5.0, 5.0, 5.0)));
139 assert_eq!(a.intersection(&Rect::new(20.0, 20.0, 1.0, 1.0)), None);
140 }
141}