|
- use std::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
- use std::f64::consts::PI;
-
- use auto_ops::impl_op_ex;
- #[cfg(feature = "serde")]
- use serde::{Deserialize, Serialize};
-
- pub use super::numeric::{Float, Numeric};
-
- /* Angle */
-
- #[derive(Debug, Clone, Copy)]
- #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
- pub enum Angle<T> {
- Deg(T),
- Rad(T),
- }
-
- impl<T> Angle<T>
- where
- T: Numeric,
- {
- pub fn into_inner(self) -> T {
- match self {
- Self::Deg(v) => v,
- Self::Rad(v) => v,
- }
- }
-
- pub fn into_deg(self) -> Self {
- match self {
- Self::Deg(v) => Self::Deg(v),
- Self::Rad(v) => Self::Rad(T::to_degrees(v)),
- }
- }
-
- pub fn into_rad(self) -> Self {
- match self {
- Self::Deg(v) => Self::Deg(T::to_radians(v)),
- Self::Rad(v) => Self::Rad(v),
- }
- }
-
- pub fn abs(self) -> Self {
- match self {
- Self::Deg(value) => Self::Deg(value.abs()),
- Self::Rad(value) => Self::Rad(value.abs()),
- }
- }
-
- fn _neg(self) -> Self {
- match self {
- Self::Deg(value) => Self::Deg(-value),
- Self::Rad(value) => Self::Rad(-value),
- }
- }
-
- fn _add(self, other: &Self) -> Self {
- match self {
- Self::Deg(value) => Self::Deg(value + other.into_deg().into_inner()),
- Self::Rad(value) => Self::Rad(value + other.into_rad().into_inner()),
- }
- }
-
- fn _mul(self, other: T) -> Self {
- match self {
- Self::Deg(value) => Self::Deg(value * other),
- Self::Rad(value) => Self::Rad(value * other),
- }
- }
- }
-
- impl<T> Angle<T>
- where
- T: Float,
- {
- pub fn sin(self) -> T {
- T::sin(self.into_rad().into_inner())
- }
-
- pub fn cos(self) -> T {
- T::cos(self.into_rad().into_inner())
- }
-
- /// Normalizes the angle to (-pi, pi] / (-180.0, 180.0]
- pub fn normalize(self) -> Self {
- match self {
- Self::Deg(value) => Self::Deg(normalize(value, T::new(-180.0), T::new(180.0))),
- Self::Rad(value) => Self::Rad(normalize(value, T::new(-PI), T::new(PI))),
- }
- }
- }
-
- impl_op_ex!(*|a: &f32, b: &Angle<f32>| -> Angle<f32> { b._mul(*a) });
- impl_op_ex!(- <T: Numeric> |a: &Angle<T>| -> Angle<T> { a._neg() });
- impl_op_ex!(+ <T: Numeric> |a: &Angle<T>, b: &Angle<T>| -> Angle<T> { a._add(b) });
- impl_op_ex!(- <T: Numeric> |a: &Angle<T>, b: &Angle<T>| -> Angle<T> { a._add(&b._neg()) });
- impl_op_ex!(* <T: Numeric> |a: &Angle<T>, b: &T| -> Angle<T> { a._mul(*b) });
- impl_op_ex!(/ <T: Numeric> |a: &Angle<T>, b: &T| -> Angle<T> { a._mul(T::one() / *b) });
- impl_op_ex!(+= <T: Numeric> |a: &mut Angle<T>, b: &Angle<T>| { *a = a._add(b); });
- impl_op_ex!(-= <T: Numeric> |a: &mut Angle<T>, b: &Angle<T>| { *a = a._add(&b._neg()); });
-
- impl<T, S> PartialEq<Angle<S>> for Angle<T>
- where
- T: Numeric + PartialEq<S>,
- S: Numeric,
- {
- fn eq(&self, other: &Angle<S>) -> bool {
- match self {
- Self::Deg(v) => v.eq(&other.into_deg().into_inner()),
- Self::Rad(v) => v.eq(&other.into_rad().into_inner()),
- }
- }
- }
-
- impl<T> Eq for Angle<T> where T: Numeric + Eq {}
-
- impl<T, S> PartialOrd<Angle<S>> for Angle<T>
- where
- T: Numeric + PartialOrd<S>,
- S: Numeric,
- {
- fn partial_cmp(&self, other: &Angle<S>) -> Option<Ordering> {
- match self {
- Self::Deg(v) => v.partial_cmp(&other.into_deg().into_inner()),
- Self::Rad(v) => v.partial_cmp(&other.into_rad().into_inner()),
- }
- }
- }
-
- impl<T> Ord for Angle<T>
- where
- T: Ord + Numeric,
- {
- fn cmp(&self, other: &Self) -> Ordering {
- match self {
- Self::Deg(v) => v.cmp(&other.into_deg().into_inner()),
- Self::Rad(v) => v.cmp(&other.into_rad().into_inner()),
- }
- }
- }
-
- fn normalize<T>(value: T, min: T, max: T) -> T
- where
- T: Float,
- {
- let range = max - min;
- let value = value - min;
- let f = (value / range).floor();
-
- value + min - f * range
- }
-
- #[cfg(test)]
- mod tests {
- use super::*;
-
- #[test]
- fn normalize() {
- assert_eq!(Angle::Deg(-180.0), Angle::Deg(540.0).normalize());
- assert_eq!(Angle::Deg(-180.0), Angle::Deg(180.0).normalize());
- assert_eq!(Angle::Deg(-180.0), Angle::Deg(-180.0).normalize());
- assert_eq!(Angle::Deg(-180.0), Angle::Deg(-540.0).normalize());
-
- assert_eq!(Angle::Deg(0.0), Angle::Deg(720.0).normalize());
- assert_eq!(Angle::Deg(0.0), Angle::Deg(360.0).normalize());
- assert_eq!(Angle::Deg(0.0), Angle::Deg(-360.0).normalize());
- assert_eq!(Angle::Deg(0.0), Angle::Deg(-720.0).normalize());
-
- assert_eq!(Angle::Deg(90.0), Angle::Deg(-630.0).normalize());
- assert_eq!(Angle::Deg(90.0), Angle::Deg(-270.0).normalize());
- assert_eq!(Angle::Deg(90.0), Angle::Deg(90.0).normalize());
- assert_eq!(Angle::Deg(90.0), Angle::Deg(450.0).normalize());
- assert_eq!(Angle::Deg(90.0), Angle::Deg(810.0).normalize());
-
- assert_eq!(Angle::Deg(-90.0), Angle::Deg(-810.0).normalize());
- assert_eq!(Angle::Deg(-90.0), Angle::Deg(-450.0).normalize());
- assert_eq!(Angle::Deg(-90.0), Angle::Deg(-90.0).normalize());
- assert_eq!(Angle::Deg(-90.0), Angle::Deg(270.0).normalize());
- assert_eq!(Angle::Deg(-90.0), Angle::Deg(630.0).normalize());
- }
- }
|