tge 0.0.4

A lightweight cross-platform 2D game framework written in pure Rust and based on OpenGL 3.3+.
Documentation
use super::Float;
use std::ops::{Add, Sub, Mul, Div, AddAssign, SubAssign, MulAssign, DivAssign};
use std::cmp::Ordering;

#[derive(Debug, Copy, Clone)]
pub enum Angle<F: Float = f32> {
    Radians(F),
    Degrees(F),
}

impl<F: Float> Angle<F> {
    pub fn radians(value: F) -> Self {
        Angle::Radians(value)
    }

    pub fn degrees(value: F) -> Self {
        Angle::Degrees(value)
    }

    pub fn n_pi(n: F) -> Self {
        Self::radians(n * F::pi())
    }

    pub fn zero() -> Self {
        Self::radians(F::zero())
    }

    pub fn value(&self) -> F {
        match self {
            Self::Radians(value) => *value,
            Self::Degrees(value) => *value,
        }
    }

    pub fn set_value(&mut self, new_value: F) {
        match self {
            Self::Radians(value) => *value = new_value,
            Self::Degrees(value) => *value = new_value,
        }
    }

    pub fn radians_value(&self) -> F {
        match self {
            Self::Radians(value) => *value,
            Self::Degrees(value) => value.to_radians(),
        }
    }

    pub fn degrees_value(&self) -> F {
        match self {
            Self::Radians(value) => value.to_degrees(),
            Self::Degrees(value) => *value,
        }
    }

    pub fn to_degrees(&self) -> Self {
        Angle::Degrees(self.degrees_value())
    }

    pub fn to_radians(&self) -> Self {
        Angle::Radians(self.radians_value())
    }
}

impl<F: Float> Add for Angle<F> {
    type Output = Self;

    fn add(self, other: Self) -> Self::Output {
        match self {
            Self::Radians(value) => Self::Radians(value + other.radians_value()),
            Self::Degrees(value) => Self::Degrees(value + other.degrees_value()),
        }
    }
}

impl<F: Float> Sub for Angle<F> {
    type Output = Self;

    fn sub(self, other: Self) -> Self::Output {
        match self {
            Self::Radians(value) => Self::Radians(value - other.radians_value()),
            Self::Degrees(value) => Self::Degrees(value - other.degrees_value()),
        }
    }
}

impl<F: Float> AddAssign for Angle<F> {
    fn add_assign(&mut self, other: Self) {
        match self {
            Self::Radians(value) => *value += other.radians_value(),
            Self::Degrees(value) => *value += other.degrees_value(),
        }
    }
}

impl<F: Float> SubAssign for Angle<F> {
    fn sub_assign(&mut self, other: Self) {
        match self {
            Self::Radians(value) => *value -= other.radians_value(),
            Self::Degrees(value) => *value -= other.degrees_value(),
        }
    }
}

impl<F: Float> Mul<F> for Angle<F> {
    type Output = Self;

    fn mul(self, rhs: F) -> Self::Output {
        match self {
            Self::Radians(value) => Self::Radians(value * rhs),
            Self::Degrees(value) => Self::Degrees(value * rhs),
        }
    }
}

impl<F: Float> Div<F> for Angle<F> {
    type Output = Self;

    fn div(self, rhs: F) -> Self::Output {
        match self {
            Self::Radians(value) => Self::Radians(value / rhs),
            Self::Degrees(value) => Self::Degrees(value / rhs),
        }
    }
}

impl<F: Float> MulAssign<F> for Angle<F> {
    fn mul_assign(&mut self, rhs: F) {
        match self {
            Self::Radians(value) => *value *= rhs,
            Self::Degrees(value) => *value *= rhs,
        }
    }
}

impl<F: Float> DivAssign<F> for Angle<F> {
    fn div_assign(&mut self, rhs: F) {
        match self {
            Self::Radians(value) => *value /= rhs,
            Self::Degrees(value) => *value /= rhs,
        }
    }
}

impl<F: Float> PartialEq for Angle<F> {
    fn eq(&self, other: &Self) -> bool {
        self.radians_value() == other.radians_value()
    }
}

impl<F: Float> PartialOrd for Angle<F> {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        self.radians_value().partial_cmp(&other.radians_value())
    }
}

#[cfg(test)]
mod tests {
    use super::Angle;

    #[test]
    fn test_create() {
        assert_eq!(Angle::<f32>::radians(std::f32::consts::PI * 2.0), Angle::<f32>::n_pi(2.0));
        assert_eq!(Angle::<f32>::n_pi(1.0), Angle::<f32>::degrees(180.0));
        assert_eq!(Angle::<f32>::n_pi(2.0).value(), std::f32::consts::PI * 2.0);
        assert_eq!(Angle::<f32>::degrees(90.0).value(), 90.0f32);
        assert_eq!(Angle::<f32>::zero(), Angle::<f32>::radians(0.0));
        assert_eq!(Angle::<f32>::zero(), Angle::<f32>::degrees(0.0));
        assert_eq!(Angle::<f32>::zero().value(), 0.0f32);
    }

    #[test]
    fn test_convert() {
        assert_eq!(Angle::<f32>::n_pi(1.0).to_degrees().value(), 180.0f32);
        assert_eq!(Angle::<f32>::degrees(90.0).to_radians().value(), std::f32::consts::PI * 0.5);
        assert_eq!(Angle::<f32>::n_pi(2.0).to_degrees().value(), Angle::<f32>::n_pi(2.0).degrees_value());
        assert_eq!(Angle::<f32>::degrees(180.0).to_radians().value(), Angle::<f32>::degrees(180.0).radians_value());
    }

    #[test]
    fn test_operator() {
        assert_eq!(Angle::<f32>::n_pi(1.0) + Angle::<f32>::n_pi(1.0), Angle::<f32>::n_pi(2.0));
        assert_eq!(Angle::<f32>::degrees(90.0) + Angle::<f32>::degrees(90.0), Angle::<f32>::degrees(180.0));
        assert_eq!(Angle::<f32>::n_pi(1.0) + Angle::<f32>::degrees(180.0), Angle::<f32>::n_pi(2.0));
        assert_eq!(Angle::<f32>::degrees(90.0) + Angle::<f32>::n_pi(0.5), Angle::<f32>::degrees(180.0));
        assert_eq!(Angle::<f32>::n_pi(2.0) - Angle::<f32>::n_pi(1.0), Angle::<f32>::n_pi(1.0));
        assert_eq!(Angle::<f32>::degrees(180.0) - Angle::<f32>::degrees(90.0), Angle::<f32>::degrees(90.0));
        assert_eq!(Angle::<f32>::n_pi(2.0) - Angle::<f32>::degrees(180.0), Angle::<f32>::n_pi(1.0));
        assert_eq!(Angle::<f32>::degrees(180.0) - Angle::<f32>::n_pi(0.5), Angle::<f32>::degrees(90.0));
        assert_eq!(Angle::<f32>::n_pi(1.0) * 2.0, Angle::<f32>::n_pi(2.0));
        assert_eq!(Angle::<f32>::degrees(90.0) * 2.0, Angle::<f32>::degrees(180.0));
        assert_eq!(Angle::<f32>::n_pi(2.0) / 2.0, Angle::<f32>::n_pi(1.0));
        assert_eq!(Angle::<f32>::degrees(90.0) / 2.0, Angle::<f32>::degrees(45.0));

        let mut angle = Angle::<f32>::n_pi(2.0);
        angle += Angle::<f32>::n_pi(2.0);
        assert_eq!(angle, Angle::<f32>::n_pi(4.0));
        angle += Angle::<f32>::degrees(180.0);
        assert_eq!(angle, Angle::<f32>::n_pi(5.0));
        angle -= Angle::<f32>::n_pi(1.0);
        assert_eq!(angle, Angle::<f32>::n_pi(4.0));
        angle -= Angle::<f32>::degrees(180.0);
        assert_eq!(angle, Angle::<f32>::n_pi(3.0));

        let mut angle = Angle::<f32>::degrees(180.0);
        angle += Angle::<f32>::degrees(180.0);
        assert_eq!(angle, Angle::<f32>::degrees(360.0));
        angle += Angle::<f32>::n_pi(2.0);
        assert_eq!(angle, Angle::<f32>::degrees(720.0));
        angle -= Angle::<f32>::degrees(360.0);
        assert_eq!(angle, Angle::<f32>::degrees(360.0));
        angle -= Angle::<f32>::n_pi(1.0);
        assert_eq!(angle, Angle::<f32>::degrees(180.0));

        let mut angle = Angle::<f32>::n_pi(1.0);
        angle *= 4.0;
        assert_eq!(angle, Angle::<f32>::n_pi(4.0));
        angle /= 2.0;
        assert_eq!(angle, Angle::<f32>::n_pi(2.0));

        let mut angle = Angle::<f32>::degrees(180.0);
        angle *= 4.0;
        assert_eq!(angle, Angle::<f32>::degrees(720.0));
        angle /= 2.0;
        assert_eq!(angle, Angle::<f32>::degrees(360.0));
    }

    #[test]
    fn test_compare() {
        assert!(Angle::<f32>::n_pi(1.5) > Angle::<f32>::n_pi(0.5));
        assert!(Angle::<f32>::n_pi(1.0) < Angle::<f32>::n_pi(2.0));
        assert!(Angle::<f32>::degrees(90.0) > Angle::<f32>::degrees(45.0));
        assert!(Angle::<f32>::degrees(30.0) < Angle::<f32>::degrees(60.0));
        assert!(Angle::<f32>::n_pi(2.0) > Angle::<f32>::degrees(300.0));
        assert!(Angle::<f32>::n_pi(0.5) < Angle::<f32>::degrees(135.0));
    }
}