1use std::ops::{Add, Div, Mul, Sub};
5
6#[derive(Debug, Clone, Copy, PartialEq, Default)]
8pub struct Point {
9 pub x: f64,
10 pub y: f64,
11}
12
13impl Point {
14 pub const ZERO: Point = Point { x: 0.0, y: 0.0 };
15
16 pub fn new(x: f64, y: f64) -> Self {
17 Point { x, y }
18 }
19
20 pub fn dist(self, other: Point) -> f64 {
22 (self - other).len()
23 }
24
25 pub fn len(self) -> f64 {
27 self.x.hypot(self.y)
28 }
29
30 pub fn lerp(self, other: Point, t: f64) -> Point {
32 self + (other - self) * t
33 }
34
35 pub fn rotate(self, angle: f64) -> Point {
37 let (s, c) = angle.sin_cos();
38 Point::new(self.x * c - self.y * s, self.x * s + self.y * c)
39 }
40}
41
42impl Add for Point {
43 type Output = Point;
44 fn add(self, o: Point) -> Point {
45 Point::new(self.x + o.x, self.y + o.y)
46 }
47}
48impl Sub for Point {
49 type Output = Point;
50 fn sub(self, o: Point) -> Point {
51 Point::new(self.x - o.x, self.y - o.y)
52 }
53}
54impl Mul<f64> for Point {
55 type Output = Point;
56 fn mul(self, k: f64) -> Point {
57 Point::new(self.x * k, self.y * k)
58 }
59}
60impl Div<f64> for Point {
61 type Output = Point;
62 fn div(self, k: f64) -> Point {
63 Point::new(self.x / k, self.y / k)
64 }
65}
66
67#[derive(Debug, Clone, Copy, PartialEq)]
69pub struct Bbox {
70 pub min: Point,
71 pub max: Point,
72 empty: bool,
73}
74
75impl Default for Bbox {
76 fn default() -> Self {
77 Bbox::new()
78 }
79}
80
81impl Bbox {
82 pub(crate) fn add_rect_rotated_about(
88 &mut self,
89 min: Point,
90 max: Point,
91 pivot: Point,
92 deg: f64,
93 ) {
94 let (sin, cos) = deg.to_radians().sin_cos();
95 for (cx, cy) in [
96 (min.x, min.y),
97 (max.x, min.y),
98 (min.x, max.y),
99 (max.x, max.y),
100 ] {
101 let (dx, dy) = (cx - pivot.x, cy - pivot.y);
102 self.add(Point::new(
103 pivot.x + dx * cos - dy * sin,
104 pivot.y + dx * sin + dy * cos,
105 ));
106 }
107 }
108
109 pub fn new() -> Self {
110 Bbox {
111 min: Point::ZERO,
112 max: Point::ZERO,
113 empty: true,
114 }
115 }
116
117 pub fn is_empty(&self) -> bool {
118 self.empty
119 }
120
121 pub fn add(&mut self, p: Point) {
123 if !p.x.is_finite() || !p.y.is_finite() {
124 return;
125 }
126 if self.empty {
127 self.min = p;
128 self.max = p;
129 self.empty = false;
130 } else {
131 self.min.x = self.min.x.min(p.x);
132 self.min.y = self.min.y.min(p.y);
133 self.max.x = self.max.x.max(p.x);
134 self.max.y = self.max.y.max(p.y);
135 }
136 }
137
138 pub fn union(&mut self, other: &Bbox) {
140 if !other.empty {
141 self.add(other.min);
142 self.add(other.max);
143 }
144 }
145
146 pub fn width(&self) -> f64 {
147 self.max.x - self.min.x
148 }
149 pub fn height(&self) -> f64 {
150 self.max.y - self.min.y
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 #[test]
159 fn point_ops() {
160 let a = Point::new(1.0, 2.0);
161 let b = Point::new(3.0, 4.0);
162 assert_eq!(a + b, Point::new(4.0, 6.0));
163 assert_eq!(b - a, Point::new(2.0, 2.0));
164 assert_eq!(a * 2.0, Point::new(2.0, 4.0));
165 assert_eq!(a.lerp(b, 0.5), Point::new(2.0, 3.0));
166 assert!((Point::new(3.0, 4.0).len() - 5.0).abs() < 1e-12);
167 }
168
169 #[test]
170 fn bbox_grows() {
171 let mut bb = Bbox::new();
172 assert!(bb.is_empty());
173 bb.add(Point::new(1.0, 1.0));
174 bb.add(Point::new(-1.0, 3.0));
175 assert_eq!(bb.min, Point::new(-1.0, 1.0));
176 assert_eq!(bb.max, Point::new(1.0, 3.0));
177 assert_eq!(bb.width(), 2.0);
178 assert_eq!(bb.height(), 2.0);
179 }
180}