gufo_common/
orientation.rs1use std::ops::{Add, Sub};
2
3crate::utils::maybe_convertible_enum!(
4 #[repr(u16)]
5 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
6 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
7 pub enum Orientation {
11 Id = 1,
12 Rotation90 = 8,
13 Rotation180 = 3,
14 Rotation270 = 6,
15 Mirrored = 2,
16 MirroredRotation90 = 5,
17 MirroredRotation180 = 4,
18 MirroredRotation270 = 7,
19 }
20);
21
22impl Orientation {
23 pub fn new(mirrored: bool, rotation: Rotation) -> Self {
24 match (mirrored, rotation) {
25 (false, Rotation::_0) => Self::Id,
26 (false, Rotation::_90) => Self::Rotation90,
27 (false, Rotation::_180) => Self::Rotation180,
28 (false, Rotation::_270) => Self::Rotation270,
29 (true, Rotation::_0) => Self::Mirrored,
30 (true, Rotation::_90) => Self::MirroredRotation90,
31 (true, Rotation::_180) => Self::MirroredRotation180,
32 (true, Rotation::_270) => Self::MirroredRotation270,
33 }
34 }
35
36 #[must_use]
40 pub fn combine(self, orientation: Orientation) -> Orientation {
41 let mut new_orientation = self;
42 if orientation.mirror() {
43 new_orientation = new_orientation.add_mirror_horizontally()
44 }
45 new_orientation = new_orientation.add_rotation(orientation.rotate());
46
47 new_orientation
48 }
49
50 #[must_use]
51 pub fn add_mirror_horizontally(self) -> Orientation {
52 Self::new(!self.mirror(), Rotation::_0 - self.rotate())
53 }
54
55 #[must_use]
56 pub fn add_mirror_vertically(self) -> Orientation {
57 Self::new(!self.mirror(), self.rotate() + Rotation::_180)
58 }
59
60 #[must_use]
61 pub fn add_rotation(self, rotation: Rotation) -> Orientation {
62 Self::new(self.mirror(), self.rotate() + rotation)
63 }
64}
65
66#[derive(Debug)]
67pub struct UnknownOrientation;
68
69#[allow(dead_code)]
70#[derive(Debug)]
71pub struct InvalidRotation(f64);
73
74#[derive(Clone, Copy, Debug, PartialEq, Eq)]
76#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
77pub enum Rotation {
78 _0,
79 _90,
80 _180,
81 _270,
82}
83
84impl Rotation {
85 pub fn degrees(self) -> u16 {
86 match self {
87 Rotation::_0 => 0,
88 Rotation::_90 => 90,
89 Rotation::_180 => 180,
90 Rotation::_270 => 270,
91 }
92 }
93}
94
95impl Add for Rotation {
96 type Output = Self;
97 fn add(self, rhs: Self) -> Self::Output {
98 Rotation::try_from((self.degrees().checked_add(rhs.degrees()).unwrap()) as f64).unwrap()
99 }
100}
101
102impl Sub for Rotation {
103 type Output = Self;
104 fn sub(self, rhs: Self) -> Self::Output {
105 Rotation::try_from((self.degrees() as f64) - (rhs.degrees() as f64)).unwrap()
106 }
107}
108
109impl TryFrom<f64> for Rotation {
120 type Error = InvalidRotation;
121 fn try_from(value: f64) -> Result<Self, Self::Error> {
122 let rotation = value.round().rem_euclid(360.);
123
124 if rotation == 0. {
125 Ok(Self::_0)
126 } else if rotation == 90. {
127 Ok(Self::_90)
128 } else if rotation == 180. {
129 Ok(Self::_180)
130 } else if rotation == 270. {
131 Ok(Self::_270)
132 } else {
133 Err(InvalidRotation(rotation))
134 }
135 }
136}
137
138impl Orientation {
139 pub fn mirror(self) -> bool {
140 matches!(
141 self,
142 Self::Mirrored
143 | Self::MirroredRotation90
144 | Self::MirroredRotation180
145 | Self::MirroredRotation270
146 )
147 }
148
149 pub fn rotate(self) -> Rotation {
150 match self {
151 Self::Id | Self::Mirrored => Rotation::_0,
152 Self::Rotation90 | Self::MirroredRotation90 => Rotation::_90,
153 Self::Rotation180 | Self::MirroredRotation180 => Rotation::_180,
154 Self::Rotation270 | Self::MirroredRotation270 => Rotation::_270,
155 }
156 }
157}