1use std::f64::consts::PI;
4
5pub use geometry_macros::{TransformMut, TransformRef, TranslateMut, TranslateRef};
6use impl_trait_for_tuples::impl_for_tuples;
7use serde::{Deserialize, Serialize};
8
9use super::orientation::Orientation;
10use crate::point::Point;
11
12#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
17pub struct Transformation {
18 pub(crate) mat: TransformationMatrix,
20 pub(crate) b: Point,
22}
23
24impl Default for Transformation {
25 fn default() -> Self {
26 Self::identity()
27 }
28}
29
30#[derive(Debug, Clone, Copy, Default, Eq, Ord, PartialOrd, PartialEq, Serialize, Deserialize)]
32pub enum Rotation {
33 #[default]
35 R0,
36 R90,
38 R180,
40 R270,
42}
43
44impl std::ops::Add<Rotation> for Rotation {
45 type Output = Rotation;
46 fn add(self, rhs: Rotation) -> Self::Output {
47 use Rotation::*;
48 match (self, rhs) {
49 (R0, R0) => R0,
50 (R0, R90) => R90,
51 (R0, R180) => R180,
52 (R0, R270) => R270,
53 (R90, R0) => R90,
54 (R90, R90) => R180,
55 (R90, R180) => R270,
56 (R90, R270) => R0,
57 (R180, R0) => R180,
58 (R180, R90) => R270,
59 (R180, R180) => R0,
60 (R180, R270) => R90,
61 (R270, R0) => R270,
62 (R270, R90) => R0,
63 (R270, R180) => R90,
64 (R270, R270) => R180,
65 }
66 }
67}
68
69impl std::ops::AddAssign for Rotation {
70 fn add_assign(&mut self, rhs: Self) {
71 *self = *self + rhs;
72 }
73}
74
75impl std::ops::Sub<Rotation> for Rotation {
76 type Output = Rotation;
77 fn sub(self, rhs: Rotation) -> Self::Output {
78 use Rotation::*;
79 match (self, rhs) {
80 (R0, R0) => R0,
81 (R0, R90) => R270,
82 (R0, R180) => R180,
83 (R0, R270) => R90,
84 (R90, R0) => R90,
85 (R90, R90) => R0,
86 (R90, R180) => R270,
87 (R90, R270) => R180,
88 (R180, R0) => R180,
89 (R180, R90) => R90,
90 (R180, R180) => R0,
91 (R180, R270) => R270,
92 (R270, R0) => R270,
93 (R270, R90) => R180,
94 (R270, R180) => R90,
95 (R270, R270) => R0,
96 }
97 }
98}
99
100impl std::ops::SubAssign for Rotation {
101 fn sub_assign(&mut self, rhs: Self) {
102 *self = *self - rhs;
103 }
104}
105
106impl Rotation {
107 #[inline]
109 pub fn transformation_matrix(&self) -> TransformationMatrix {
110 TransformationMatrix::from(*self)
111 }
112
113 pub fn degrees(&self) -> f64 {
115 match self {
116 Rotation::R0 => 0.,
117 Rotation::R90 => 90.,
118 Rotation::R180 => 180.,
119 Rotation::R270 => 270.,
120 }
121 }
122
123 pub fn radians(&self) -> f64 {
125 match self {
126 Rotation::R0 => 0.,
127 Rotation::R90 => PI / 2.,
128 Rotation::R180 => PI,
129 Rotation::R270 => PI * 1.5,
130 }
131 }
132}
133
134pub struct NonManhattanAngleError;
139
140impl TryFrom<f64> for Rotation {
141 type Error = NonManhattanAngleError;
142 fn try_from(value: f64) -> Result<Self, Self::Error> {
143 let value = (((value % 360.) + 360.) % 360.).round() as i64;
144 match value {
145 0 => Ok(Rotation::R0),
146 90 => Ok(Rotation::R90),
147 180 => Ok(Rotation::R180),
148 270 => Ok(Rotation::R270),
149 _ => Err(NonManhattanAngleError),
150 }
151 }
152}
153
154#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Serialize, Deserialize)]
158pub struct TransformationMatrix([[i8; 2]; 2]);
159
160impl TransformationMatrix {
161 #[inline]
165 pub fn identity() -> Self {
166 Self([[1, 0], [0, 1]])
167 }
168
169 pub fn rotate(&self, angle: Rotation) -> Self {
171 angle.transformation_matrix() * *self
172 }
173
174 pub fn reflect_vert(&self) -> Self {
176 Self([[1, 0], [0, -1]]) * *self
177 }
178
179 pub fn inverse(&self) -> Self {
181 Self(unitary_matinv(&self.0))
182 }
183}
184
185impl From<Rotation> for TransformationMatrix {
186 fn from(value: Rotation) -> Self {
187 Self(match value {
188 Rotation::R0 => [[1, 0], [0, 1]],
189 Rotation::R90 => [[0, -1], [1, 0]],
190 Rotation::R180 => [[-1, 0], [0, -1]],
191 Rotation::R270 => [[0, 1], [-1, 0]],
192 })
193 }
194}
195
196fn matmul_i8(a: &[[i8; 2]; 2], b: &[[i8; 2]; 2]) -> [[i8; 2]; 2] {
198 [
199 [
200 a[0][0] * b[0][0] + a[0][1] * b[1][0],
201 a[0][0] * b[0][1] + a[0][1] * b[1][1],
202 ],
203 [
204 a[1][0] * b[0][0] + a[1][1] * b[1][0],
205 a[1][0] * b[0][1] + a[1][1] * b[1][1],
206 ],
207 ]
208}
209
210fn matvec_i8_i64(a: &[[i8; 2]; 2], b: &[i64; 2]) -> [i64; 2] {
212 [
213 a[0][0] as i64 * b[0] + a[0][1] as i64 * b[1],
214 a[1][0] as i64 * b[0] + a[1][1] as i64 * b[1],
215 ]
216}
217
218impl std::ops::Mul<TransformationMatrix> for TransformationMatrix {
219 type Output = Self;
220 fn mul(self, rhs: TransformationMatrix) -> Self::Output {
221 Self(matmul_i8(&self.0, &rhs.0))
222 }
223}
224
225impl std::ops::Mul<Point> for TransformationMatrix {
226 type Output = Point;
227 fn mul(self, rhs: Point) -> Self::Output {
228 let out = matvec_i8_i64(&self.0, &[rhs.x, rhs.y]);
229 Point::new(out[0], out[1])
230 }
231}
232
233impl std::ops::Deref for TransformationMatrix {
234 type Target = [[i8; 2]; 2];
235 fn deref(&self) -> &Self::Target {
236 &self.0
237 }
238}
239
240impl std::ops::DerefMut for TransformationMatrix {
241 fn deref_mut(&mut self) -> &mut Self::Target {
242 &mut self.0
243 }
244}
245
246impl Default for TransformationMatrix {
247 #[inline]
248 fn default() -> Self {
249 Self::identity()
250 }
251}
252
253impl Transformation {
254 pub fn identity() -> Self {
256 Self {
257 mat: TransformationMatrix::identity(),
258 b: Point::zero(),
259 }
260 }
261 pub fn translate(x: i64, y: i64) -> Self {
263 Self {
264 mat: TransformationMatrix::identity(),
265 b: Point::new(x, y),
266 }
267 }
268 pub fn translate_ref(&self, x: i64, y: i64) -> Self {
270 Self {
271 mat: self.mat,
272 b: Point::new(x, y) + self.b,
273 }
274 }
275
276 pub fn rotate(angle: Rotation) -> Self {
278 let mat = TransformationMatrix::from(angle);
279 Self {
280 mat,
281 b: Point::zero(),
282 }
283 }
284
285 pub fn reflect_vert() -> Self {
287 Self {
288 mat: TransformationMatrix([[1, 0], [0, -1]]),
289 b: Point::zero(),
290 }
291 }
292
293 #[inline]
295 pub fn builder() -> TransformationBuilder {
296 TransformationBuilder::default()
297 }
298
299 pub fn from_offset(offset: Point) -> Self {
304 Self::builder()
305 .point(offset)
306 .orientation(Orientation::default())
307 .build()
308 }
309
310 pub fn from_offset_and_orientation(offset: Point, orientation: impl Into<Orientation>) -> Self {
312 Self::builder()
313 .point(offset)
314 .orientation(orientation.into())
315 .build()
316 }
317
318 pub fn from_opts(offset: Point, reflect_vert: bool, angle: Rotation) -> Self {
321 Self::builder()
322 .point(offset)
323 .reflect_vert(reflect_vert)
324 .angle(angle)
325 .build()
326 }
327
328 pub fn cascade(parent: Transformation, child: Transformation) -> Transformation {
342 let mut b = parent.mat * child.b;
345 b += parent.b;
346 let mat = parent.mat * child.mat;
348 Self { mat, b }
349 }
350
351 pub fn offset_point(&self) -> Point {
353 self.b
354 }
355
356 pub fn orientation(&self) -> Orientation {
358 let reflect_vert = if self.mat[0][0] == 0 {
359 self.mat[0][1].signum() == self.mat[1][0].signum()
360 } else {
361 self.mat[0][0].signum() != self.mat[1][1].signum()
362 };
363 let cos = self.mat[0][0];
364 let sin = self.mat[1][0];
365 let angle = match (cos, sin) {
366 (1, 0) => Rotation::R0,
367 (0, 1) => Rotation::R90,
368 (-1, 0) => Rotation::R180,
369 (0, -1) => Rotation::R270,
370 _ => panic!("transformation did not represent a valid Manhattan transformation"),
371 };
372 Orientation {
373 reflect_vert,
374 angle,
375 }
376 }
377
378 pub fn inv(&self) -> Transformation {
396 let inv = self.mat.inverse();
397 let invb = inv * self.b;
398 Self { mat: inv, b: -invb }
399 }
400}
401
402impl<T> From<T> for Transformation
403where
404 T: Into<Orientation>,
405{
406 fn from(value: T) -> Self {
407 Self::builder().orientation(value).build()
408 }
409}
410
411#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
413pub struct TransformationBuilder {
414 x: i64,
415 y: i64,
416 reflect_vert: bool,
417 angle: Rotation,
418}
419
420impl TransformationBuilder {
421 pub fn point(&mut self, point: impl Into<Point>) -> &mut Self {
423 let point = point.into();
424 self.x = point.x;
425 self.y = point.y;
426 self
427 }
428
429 pub fn orientation(&mut self, o: impl Into<Orientation>) -> &mut Self {
433 let o = o.into();
434 self.reflect_vert = o.reflect_vert;
435 self.angle = o.angle;
436 self
437 }
438
439 pub fn angle(&mut self, angle: Rotation) -> &mut Self {
441 self.angle = angle;
442 self
443 }
444
445 pub fn reflect_vert(&mut self, reflect_vert: bool) -> &mut Self {
447 self.reflect_vert = reflect_vert;
448 self
449 }
450
451 pub fn build(&mut self) -> Transformation {
453 let mut mat = self.angle.transformation_matrix();
454 if self.reflect_vert {
455 mat[0][1] = -mat[0][1];
456 mat[1][1] = -mat[1][1];
457 }
458 Transformation {
459 mat,
460 b: Point::new(self.x, self.y),
461 }
462 }
463}
464
465fn unitary_matinv(a: &[[i8; 2]; 2]) -> [[i8; 2]; 2] {
470 [[a[1][1], -a[0][1]], [-a[1][0], a[0][0]]]
471}
472
473pub trait TransformRef: TranslateRef {
475 fn transform_ref(&self, trans: Transformation) -> Self;
477}
478
479impl TransformRef for () {
480 fn transform_ref(&self, _trans: Transformation) -> Self {}
481}
482
483impl<T: TransformRef> TransformRef for Vec<T> {
484 fn transform_ref(&self, trans: Transformation) -> Self {
485 self.iter().map(|elt| elt.transform_ref(trans)).collect()
486 }
487}
488
489impl<T: TransformRef> TransformRef for Option<T> {
490 fn transform_ref(&self, trans: Transformation) -> Self {
491 self.as_ref().map(move |elt| elt.transform_ref(trans))
492 }
493}
494
495#[impl_for_tuples(32)]
497pub trait TransformMut: TranslateMut {
498 fn transform_mut(&mut self, trans: Transformation);
500}
501
502impl<T: TransformMut> TransformMut for Vec<T> {
503 fn transform_mut(&mut self, trans: Transformation) {
504 for i in self.iter_mut() {
505 i.transform_mut(trans);
506 }
507 }
508}
509
510impl<T: TransformMut> TransformMut for Option<T> {
511 fn transform_mut(&mut self, trans: Transformation) {
512 if let Some(inner) = self.as_mut() {
513 inner.transform_mut(trans);
514 }
515 }
516}
517
518pub trait Transform: Translate + TransformMut + Sized {
522 #[inline]
526 fn transform(mut self, trans: Transformation) -> Self {
527 self.transform_mut(trans);
528 self
529 }
530}
531
532impl<T: TransformMut + Sized> Transform for T {}
533
534pub trait TranslateRef: Sized {
536 fn translate_ref(&self, p: Point) -> Self;
538}
539
540impl TranslateRef for () {
541 fn translate_ref(&self, _p: Point) -> Self {}
542}
543
544impl<T: TranslateRef> TranslateRef for Vec<T> {
545 fn translate_ref(&self, p: Point) -> Self {
546 self.iter().map(|elt| elt.translate_ref(p)).collect()
547 }
548}
549
550impl<T: TranslateRef> TranslateRef for Option<T> {
551 fn translate_ref(&self, p: Point) -> Self {
552 self.as_ref().map(move |elt| elt.translate_ref(p))
553 }
554}
555
556#[impl_for_tuples(32)]
558pub trait TranslateMut {
559 fn translate_mut(&mut self, p: Point);
561}
562
563impl<T: TranslateMut> TranslateMut for Vec<T> {
564 fn translate_mut(&mut self, p: Point) {
565 for i in self.iter_mut() {
566 i.translate_mut(p);
567 }
568 }
569}
570
571impl<T: TranslateMut> TranslateMut for Option<T> {
572 fn translate_mut(&mut self, p: Point) {
573 if let Some(inner) = self.as_mut() {
574 inner.translate_mut(p);
575 }
576 }
577}
578
579pub trait Translate: TranslateMut + Sized {
583 fn translate(mut self, p: Point) -> Self {
587 self.translate_mut(p);
588 self
589 }
590}
591
592impl<T: TranslateMut + Sized> Translate for T {}
593
594#[cfg(test)]
595mod tests {
596 use super::*;
597 use crate::{orientation::NamedOrientation, rect::Rect};
598
599 #[test]
600 fn matvec_works() {
601 let a = [[1, 2], [3, 4]];
602 let b = [5, 6];
603 assert_eq!(matvec_i8_i64(&a, &b), [17, 39]);
604 }
605
606 #[test]
607 fn matmul_works() {
608 let a = [[1, 2], [3, 4]];
609 let b = [[5, 6], [7, 8]];
610 assert_eq!(matmul_i8(&a, &b), [[19, 22], [43, 50]]);
611 }
612
613 #[test]
614 fn unitary_matinv_works() {
615 let a = [[1, 1], [3, 4]];
616 let inv = unitary_matinv(&a);
617 let a_mul_inv = matmul_i8(&a, &inv);
618 assert_eq!(a_mul_inv[0][0], 1);
619 assert_eq!(a_mul_inv[0][1], 0);
620 assert_eq!(a_mul_inv[1][0], 0);
621 assert_eq!(a_mul_inv[1][1], 1);
622 }
623
624 #[test]
625 fn cascade_identity_preserves_transformation() {
626 for orientation in NamedOrientation::all_rectangular() {
627 let tf = Transformation::from_offset_and_orientation(Point::new(520, 130), orientation);
628 let casc = Transformation::cascade(tf, Transformation::identity());
629 assert_eq!(
630 tf, casc,
631 "Cascading with identity produced incorrect transformation for orientation {:?}",
632 orientation
633 );
634 }
635 }
636
637 #[test]
638 fn transformation_offset_and_orientation_preserves_components() {
639 let pt = Point::new(8930, 730);
640 for orientation in NamedOrientation::all_rectangular() {
641 println!("Testing orientation {:?}", orientation);
642 let tf = Transformation::from_offset_and_orientation(pt, orientation);
643 assert_eq!(tf.orientation(), orientation.into());
644 assert_eq!(tf.offset_point(), pt);
645 }
646 }
647
648 #[test]
649 fn transformation_equivalent_to_offset_and_orientation() {
650 for orientation in NamedOrientation::all_rectangular() {
651 println!("Testing orientation {:?}", orientation);
652 let tf1 =
653 Transformation::from_offset_and_orientation(Point::new(380, 340), orientation);
654 assert_eq!(tf1.orientation(), orientation.into());
655 let tf2 =
656 Transformation::from_offset_and_orientation(tf1.offset_point(), tf1.orientation());
657 assert_eq!(tf1, tf2);
658 }
659 }
660
661 #[test]
662 fn point_transformations_work() {
663 let pt = Point::new(2, 1);
664
665 let pt_reflect_vert = pt.transform(Transformation::from_offset_and_orientation(
666 Point::zero(),
667 NamedOrientation::ReflectVert,
668 ));
669 assert_eq!(pt_reflect_vert, Point::new(2, -1));
670
671 let pt_reflect_horiz = pt.transform(Transformation::from_offset_and_orientation(
672 Point::zero(),
673 NamedOrientation::ReflectHoriz,
674 ));
675 assert_eq!(pt_reflect_horiz, Point::new(-2, 1));
676
677 let pt_r90 = pt.transform(Transformation::from_offset_and_orientation(
678 Point::new(23, 11),
679 NamedOrientation::R90,
680 ));
681 assert_eq!(pt_r90, Point::new(22, 13));
682
683 let pt_r180 = pt.transform(Transformation::from_offset_and_orientation(
684 Point::new(-50, 10),
685 NamedOrientation::R180,
686 ));
687 assert_eq!(pt_r180, Point::new(-52, 9));
688
689 let pt_r270 = pt.transform(Transformation::from_offset_and_orientation(
690 Point::new(80, 90),
691 NamedOrientation::R270,
692 ));
693 assert_eq!(pt_r270, Point::new(81, 88));
694
695 let pt_r90cw = pt.transform(Transformation::from_offset_and_orientation(
696 Point::new(5, 13),
697 NamedOrientation::R90Cw,
698 ));
699 assert_eq!(pt_r90cw, Point::new(6, 11));
700
701 let pt_r180cw = pt.transform(Transformation::from_offset_and_orientation(
702 Point::zero(),
703 NamedOrientation::R180Cw,
704 ));
705 assert_eq!(pt_r180cw, Point::new(-2, -1));
706
707 let pt_r270cw = pt.transform(Transformation::from_offset_and_orientation(
708 Point::new(1, 100),
709 NamedOrientation::R270Cw,
710 ));
711 assert_eq!(pt_r270cw, Point::new(0, 102));
712
713 let pt_flip_yx = pt.transform(Transformation::from_offset_and_orientation(
714 Point::new(-65, -101),
715 NamedOrientation::FlipYx,
716 ));
717 assert_eq!(pt_flip_yx, Point::new(-64, -99));
718
719 let pt_flip_minus_yx = pt.transform(Transformation::from_offset_and_orientation(
720 Point::new(1, -5),
721 NamedOrientation::FlipMinusYx,
722 ));
723 assert_eq!(pt_flip_minus_yx, Point::new(0, -7));
724 }
725
726 #[test]
727 fn translate_works_for_tuples() {
728 let mut tuple = (
729 Rect::from_sides(0, 0, 100, 200),
730 Rect::from_sides(50, -50, 150, 0),
731 );
732 tuple.translate_mut(Point::new(5, 10));
733 assert_eq!(
734 tuple,
735 (
736 Rect::from_sides(5, 10, 105, 210),
737 Rect::from_sides(55, -40, 155, 10)
738 )
739 );
740 }
741
742 #[test]
743 fn translate_works_for_vecs() {
744 let mut v = vec![
745 Rect::from_sides(0, 0, 100, 200),
746 Rect::from_sides(50, -50, 150, 0),
747 ];
748 v.translate_mut(Point::new(5, 10));
749 assert_eq!(
750 v,
751 vec![
752 Rect::from_sides(5, 10, 105, 210),
753 Rect::from_sides(55, -40, 155, 10)
754 ]
755 );
756 }
757
758 #[test]
759 fn transform_works_for_tuples() {
760 let mut tuple = (
761 Rect::from_sides(0, 0, 100, 200),
762 Rect::from_sides(50, -50, 150, 0),
763 );
764 tuple.transform_mut(Transformation::from_offset_and_orientation(
765 Point::zero(),
766 NamedOrientation::R90,
767 ));
768 assert_eq!(
769 tuple,
770 (
771 Rect::from_sides(-200, 0, 0, 100),
772 Rect::from_sides(0, 50, 50, 150)
773 )
774 );
775 }
776
777 #[test]
778 fn transform_works_for_vecs() {
779 let mut v = vec![
780 Rect::from_sides(0, 0, 100, 200),
781 Rect::from_sides(50, -50, 150, 0),
782 ];
783 v.transform_mut(Transformation::from_offset_and_orientation(
784 Point::zero(),
785 NamedOrientation::R90,
786 ));
787 assert_eq!(
788 v,
789 vec![
790 Rect::from_sides(-200, 0, 0, 100),
791 Rect::from_sides(0, 50, 50, 150)
792 ]
793 );
794 }
795
796 #[test]
797 fn transform_macros_work() {
798 #[derive(Debug, Copy, Clone, Eq, PartialEq, TranslateMut, TransformMut)]
799 pub struct TwoPointGroup {
800 p1: Point,
801 p2: Point,
802 }
803
804 #[derive(Debug, Copy, Clone, Eq, PartialEq, TranslateMut, TransformMut)]
805 pub enum PointEnum {
806 First(Point),
807 Second { pt: Point },
808 }
809
810 let group = TwoPointGroup {
811 p1: Point::new(100, 200),
812 p2: Point::new(-400, 300),
813 };
814
815 let group = group.translate(Point::new(100, 50));
816 assert_eq!(
817 group,
818 TwoPointGroup {
819 p1: Point::new(200, 250),
820 p2: Point::new(-300, 350),
821 }
822 );
823
824 let mut group = PointEnum::First(Point::new(100, 200));
825 group = group.transform(NamedOrientation::ReflectVert.into());
826 assert_eq!(group, PointEnum::First(Point::new(100, -200)),);
827
828 let mut group = PointEnum::Second {
829 pt: Point::new(100, 200),
830 };
831 group = group.transform(NamedOrientation::ReflectVert.into());
832 assert_eq!(
833 group,
834 PointEnum::Second {
835 pt: Point::new(100, -200)
836 }
837 );
838 }
839}