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))]
#[cfg_attr(feature = "zvariant", derive(zvariant::Type))]
#[cfg_attr(feature = "zvariant", zvariant(signature = "u"))]
pub enum Orientation {
Id = 1,
Rotation90 = 8,
Rotation180 = 3,
Rotation270 = 6,
Mirrored = 2,
MirroredRotation90 = 5,
MirroredRotation180 = 4,
MirroredRotation270 = 7,
}
);
impl Orientation {
pub fn new(mirrored: bool, rotation: Rotation) -> 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,
}
}
#[must_use]
pub fn combine(self, orientation: Orientation) -> Orientation {
let mut new_orientation = self;
if orientation.mirror() {
new_orientation = new_orientation.add_mirror_horizontally()
}
new_orientation = new_orientation.add_rotation(orientation.rotate());
new_orientation
}
#[must_use]
pub fn add_mirror_horizontally(self) -> Orientation {
Self::new(!self.mirror(), Rotation::_0 - self.rotate())
}
#[must_use]
pub fn add_mirror_vertically(self) -> Orientation {
Self::new(!self.mirror(), self.rotate() + Rotation::_180)
}
#[must_use]
pub fn add_rotation(self, rotation: Rotation) -> Orientation {
Self::new(self.mirror(), self.rotate() + rotation)
}
}
#[derive(Debug)]
pub struct UnknownOrientation;
#[allow(dead_code)]
#[derive(Debug)]
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()
}
}
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,
}
}
}