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