1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
use std::ops::{Add, Sub};

crate::utils::maybe_convertible_enum!(
    #[repr(u16)]
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
    /// Operations that have to be applied to orient the image correctly
    pub enum Orientation {
        Id = 1,
        Rotation90 = 8,
        Rotation180 = 3,
        Rotation270 = 6,
        Mirrored = 2,
        MirroredRotation90 = 5,
        MirroredRotation180 = 4,
        MirroredRotation270 = 7,
    }
);

impl Orientation {
    pub fn new(rotation: Rotation, mirrored: bool) -> Self {
        match (mirrored, rotation) {
            (false, Rotation::_0) => Self::Id,
            (false, Rotation::_90) => Self::Rotation90,
            (false, Rotation::_180) => Self::Rotation180,
            (false, Rotation::_270) => Self::Rotation270,
            (true, Rotation::_0) => Self::Mirrored,
            (true, Rotation::_90) => Self::MirroredRotation90,
            (true, Rotation::_180) => Self::MirroredRotation180,
            (true, Rotation::_270) => Self::MirroredRotation270,
        }
    }
}

#[derive(Debug)]
pub struct UnknownOrientation;

#[allow(dead_code)]
#[derive(Debug)]
/// Rotation was not given in multiples of 90
pub struct InvalidRotation(f64);

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Rotation {
    _0,
    _90,
    _180,
    _270,
}

impl Rotation {
    pub fn degrees(self) -> u16 {
        match self {
            Rotation::_0 => 0,
            Rotation::_90 => 90,
            Rotation::_180 => 180,
            Rotation::_270 => 270,
        }
    }
}

impl Add for Rotation {
    type Output = Self;
    fn add(self, rhs: Self) -> Self::Output {
        Rotation::try_from((self.degrees().checked_add(rhs.degrees()).unwrap()) as f64).unwrap()
    }
}

impl Sub for Rotation {
    type Output = Self;
    fn sub(self, rhs: Self) -> Self::Output {
        Rotation::try_from((self.degrees() as f64) - (rhs.degrees() as f64)).unwrap()
    }
}

/// Get rotation from multiples of 90 deg
///
/// The given value is rounded to an integer number
///
/// ```
/// # use gufo_common::orientation::Rotation;
/// assert_eq!(Rotation::try_from(90.).unwrap(), Rotation::_90);
/// assert_eq!(Rotation::try_from(-90.).unwrap(), Rotation::_270);
/// assert!(Rotation::try_from(1.).is_err());
/// ```
impl TryFrom<f64> for Rotation {
    type Error = InvalidRotation;
    fn try_from(value: f64) -> Result<Self, Self::Error> {
        let rotation = value.round().rem_euclid(360.);

        if rotation == 0. {
            Ok(Self::_0)
        } else if rotation == 90. {
            Ok(Self::_90)
        } else if rotation == 180. {
            Ok(Self::_180)
        } else if rotation == 270. {
            Ok(Self::_270)
        } else {
            Err(InvalidRotation(rotation))
        }
    }
}

impl Orientation {
    pub fn mirror(self) -> bool {
        matches!(
            self,
            Self::Mirrored
                | Self::MirroredRotation90
                | Self::MirroredRotation180
                | Self::MirroredRotation270
        )
    }

    pub fn rotate(self) -> Rotation {
        match self {
            Self::Id | Self::Mirrored => Rotation::_0,
            Self::Rotation90 | Self::MirroredRotation90 => Rotation::_90,
            Self::Rotation180 | Self::MirroredRotation180 => Rotation::_180,
            Self::Rotation270 | Self::MirroredRotation270 => Rotation::_270,
        }
    }
}