1use std::ops::{Add, Neg, Sub};
8
9#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
10pub struct Point {
11 pub x: i64,
12 pub y: i64,
13}
14
15impl Point {
16 pub const ZERO: Self = Self { x: 0, y: 0 };
17 #[inline]
18 pub const fn new(x: i64, y: i64) -> Self {
19 Self { x, y }
20 }
21}
22
23impl Add<Vec2> for Point {
24 type Output = Point;
25 #[inline]
26 fn add(self, v: Vec2) -> Point {
27 Point::new(self.x + v.x, self.y + v.y)
28 }
29}
30
31impl Sub for Point {
32 type Output = Vec2;
33 #[inline]
34 fn sub(self, o: Point) -> Vec2 {
35 Vec2::new(self.x - o.x, self.y - o.y)
36 }
37}
38
39#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
40pub struct Vec2 {
41 pub x: i64,
42 pub y: i64,
43}
44
45impl Vec2 {
46 pub const ZERO: Self = Self { x: 0, y: 0 };
47 #[inline]
48 pub const fn new(x: i64, y: i64) -> Self {
49 Self { x, y }
50 }
51}
52
53impl Add for Vec2 {
54 type Output = Vec2;
55 #[inline]
56 fn add(self, o: Vec2) -> Vec2 {
57 Vec2::new(self.x + o.x, self.y + o.y)
58 }
59}
60
61impl Sub for Vec2 {
62 type Output = Vec2;
63 #[inline]
64 fn sub(self, o: Vec2) -> Vec2 {
65 Vec2::new(self.x - o.x, self.y - o.y)
66 }
67}
68
69impl Neg for Vec2 {
70 type Output = Vec2;
71 #[inline]
72 fn neg(self) -> Vec2 {
73 Vec2::new(-self.x, -self.y)
74 }
75}
76
77#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
78pub struct Bbox {
79 pub min: Point,
80 pub max: Point,
81}
82
83impl Bbox {
84 pub const EMPTY: Self = Self {
85 min: Point::new(i64::MAX, i64::MAX),
86 max: Point::new(i64::MIN, i64::MIN),
87 };
88
89 #[inline]
90 pub const fn new(min: Point, max: Point) -> Self {
91 Self { min, max }
92 }
93
94 #[inline]
95 pub fn from_point(p: Point) -> Self {
96 Self { min: p, max: p }
97 }
98
99 #[inline]
100 pub fn is_empty(&self) -> bool {
101 self.min.x > self.max.x || self.min.y > self.max.y
102 }
103
104 #[inline]
105 pub fn width(&self) -> i64 {
106 if self.is_empty() {
107 0
108 } else {
109 self.max.x - self.min.x
110 }
111 }
112
113 #[inline]
114 pub fn height(&self) -> i64 {
115 if self.is_empty() {
116 0
117 } else {
118 self.max.y - self.min.y
119 }
120 }
121
122 pub fn contains(&self, p: Point) -> bool {
123 !self.is_empty()
124 && p.x >= self.min.x
125 && p.x <= self.max.x
126 && p.y >= self.min.y
127 && p.y <= self.max.y
128 }
129
130 pub fn intersects(&self, other: &Bbox) -> bool {
131 !self.is_empty()
132 && !other.is_empty()
133 && self.min.x <= other.max.x
134 && self.max.x >= other.min.x
135 && self.min.y <= other.max.y
136 && self.max.y >= other.min.y
137 }
138
139 pub fn intersection(&self, other: &Bbox) -> Bbox {
140 if self.is_empty() || other.is_empty() {
141 return Bbox::EMPTY;
142 }
143 let min = Point::new(self.min.x.max(other.min.x), self.min.y.max(other.min.y));
144 let max = Point::new(self.max.x.min(other.max.x), self.max.y.min(other.max.y));
145 if min.x > max.x || min.y > max.y {
146 Bbox::EMPTY
147 } else {
148 Bbox::new(min, max)
149 }
150 }
151
152 pub fn union(&self, other: &Bbox) -> Bbox {
153 if self.is_empty() {
154 return *other;
155 }
156 if other.is_empty() {
157 return *self;
158 }
159 Bbox::new(
160 Point::new(self.min.x.min(other.min.x), self.min.y.min(other.min.y)),
161 Point::new(self.max.x.max(other.max.x), self.max.y.max(other.max.y)),
162 )
163 }
164
165 pub fn expand_to(&mut self, p: Point) {
166 if self.is_empty() {
167 self.min = p;
168 self.max = p;
169 } else {
170 self.min.x = self.min.x.min(p.x);
171 self.min.y = self.min.y.min(p.y);
172 self.max.x = self.max.x.max(p.x);
173 self.max.y = self.max.y.max(p.y);
174 }
175 }
176
177 pub fn corners(&self) -> [Point; 4] {
178 [
179 self.min,
180 Point::new(self.max.x, self.min.y),
181 self.max,
182 Point::new(self.min.x, self.max.y),
183 ]
184 }
185}
186
187impl Default for Bbox {
188 fn default() -> Self {
189 Self::EMPTY
190 }
191}
192
193#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
194#[repr(u8)]
195pub enum Rot4 {
196 R0 = 0,
197 R90 = 1,
198 R180 = 2,
199 R270 = 3,
200}
201
202impl Rot4 {
203 #[inline]
204 pub fn apply(self, p: Point) -> Point {
205 match self {
206 Rot4::R0 => p,
207 Rot4::R90 => Point::new(-p.y, p.x),
208 Rot4::R180 => Point::new(-p.x, -p.y),
209 Rot4::R270 => Point::new(p.y, -p.x),
210 }
211 }
212
213 #[inline]
214 pub fn apply_vec(self, v: Vec2) -> Vec2 {
215 match self {
216 Rot4::R0 => v,
217 Rot4::R90 => Vec2::new(-v.y, v.x),
218 Rot4::R180 => Vec2::new(-v.x, -v.y),
219 Rot4::R270 => Vec2::new(v.y, -v.x),
220 }
221 }
222
223 #[inline]
224 pub fn compose(self, other: Rot4) -> Rot4 {
225 let s = self as u8;
226 let o = other as u8;
227 match (s + o) % 4 {
228 0 => Rot4::R0,
229 1 => Rot4::R90,
230 2 => Rot4::R180,
231 _ => Rot4::R270,
232 }
233 }
234
235 #[inline]
236 pub fn inverse(self) -> Rot4 {
237 match self {
238 Rot4::R0 => Rot4::R0,
239 Rot4::R90 => Rot4::R270,
240 Rot4::R180 => Rot4::R180,
241 Rot4::R270 => Rot4::R90,
242 }
243 }
244}
245
246#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
249pub struct Trans {
250 pub rot: Rot4,
251 pub mirror: bool,
252 pub disp: Vec2,
253}
254
255impl Trans {
256 pub const IDENTITY: Self = Self {
257 rot: Rot4::R0,
258 mirror: false,
259 disp: Vec2::ZERO,
260 };
261
262 #[inline]
263 pub const fn new(rot: Rot4, mirror: bool, disp: Vec2) -> Self {
264 Self { rot, mirror, disp }
265 }
266
267 #[inline]
268 pub fn translate(disp: Vec2) -> Self {
269 Self::new(Rot4::R0, false, disp)
270 }
271
272 #[inline]
273 pub fn rotate(rot: Rot4) -> Self {
274 Self::new(rot, false, Vec2::ZERO)
275 }
276
277 #[inline]
278 pub fn mirror_x() -> Self {
279 Self::new(Rot4::R0, true, Vec2::ZERO)
280 }
281
282 #[inline]
283 pub fn apply(self, p: Point) -> Point {
284 let p = if self.mirror { Point::new(p.x, -p.y) } else { p };
285 let p = self.rot.apply(p);
286 Point::new(p.x + self.disp.x, p.y + self.disp.y)
287 }
288
289 pub fn apply_bbox(self, b: Bbox) -> Bbox {
290 if b.is_empty() {
291 return b;
292 }
293 let mut out = Bbox::EMPTY;
294 for c in b.corners() {
295 out.expand_to(self.apply(c));
296 }
297 out
298 }
299
300 pub fn compose(self, other: Trans) -> Trans {
302 let mirror = self.mirror ^ other.mirror;
303 let inner_rot = if self.mirror {
305 other.rot.inverse()
306 } else {
307 other.rot
308 };
309 let rot = self.rot.compose(inner_rot);
310 let other_disp = if self.mirror {
312 Vec2::new(other.disp.x, -other.disp.y)
313 } else {
314 other.disp
315 };
316 let rotated = self.rot.apply_vec(other_disp);
317 let disp = Vec2::new(rotated.x + self.disp.x, rotated.y + self.disp.y);
318 Trans { rot, mirror, disp }
319 }
320
321 pub fn inverse(self) -> Trans {
322 let neg_d = -self.disp;
324 let rot_inv = self.rot.inverse();
325 let r_neg_d = rot_inv.apply_vec(neg_d);
326 let disp = if self.mirror {
327 Vec2::new(r_neg_d.x, -r_neg_d.y)
328 } else {
329 r_neg_d
330 };
331 Trans {
332 rot: if self.mirror { self.rot } else { rot_inv },
333 mirror: self.mirror,
334 disp,
335 }
336 }
337}
338
339impl Default for Trans {
340 fn default() -> Self {
341 Self::IDENTITY
342 }
343}
344
345#[derive(Copy, Clone, Debug, PartialEq)]
348pub struct DTrans {
349 pub angle_deg: f64,
350 pub mag: f64,
351 pub mirror: bool,
352 pub disp: Vec2,
353}
354
355impl DTrans {
356 pub const IDENTITY: Self = Self {
357 angle_deg: 0.0,
358 mag: 1.0,
359 mirror: false,
360 disp: Vec2::ZERO,
361 };
362}
363
364#[cfg(test)]
365mod tests {
366 use super::*;
367
368 #[test]
369 fn rot90_apply() {
370 let p = Point::new(10, 0);
371 assert_eq!(Rot4::R90.apply(p), Point::new(0, 10));
372 assert_eq!(Rot4::R180.apply(p), Point::new(-10, 0));
373 assert_eq!(Rot4::R270.apply(p), Point::new(0, -10));
374 }
375
376 #[test]
377 fn trans_inverse_roundtrip() {
378 let t = Trans::new(Rot4::R90, true, Vec2::new(7, -3));
379 let inv = t.inverse();
380 for p in [
381 Point::new(0, 0),
382 Point::new(100, 200),
383 Point::new(-50, 75),
384 ] {
385 assert_eq!(inv.apply(t.apply(p)), p, "inverse failed for {p:?}");
386 }
387 }
388
389 #[test]
390 fn trans_compose_matches_apply() {
391 let a = Trans::new(Rot4::R90, false, Vec2::new(10, 20));
392 let b = Trans::new(Rot4::R180, true, Vec2::new(-5, 3));
393 let ab = a.compose(b);
394 for p in [Point::new(1, 2), Point::new(-7, 8), Point::new(0, 0)] {
395 assert_eq!(ab.apply(p), a.apply(b.apply(p)));
396 }
397 }
398
399 #[test]
400 fn bbox_ops() {
401 let a = Bbox::new(Point::new(0, 0), Point::new(10, 10));
402 let b = Bbox::new(Point::new(5, 5), Point::new(15, 15));
403 assert!(a.intersects(&b));
404 assert_eq!(
405 a.union(&b),
406 Bbox::new(Point::new(0, 0), Point::new(15, 15))
407 );
408 assert!(Bbox::EMPTY.is_empty());
409 }
410}