extern crate num_traits as num;
#[macro_use]
#[cfg(feature = "approx")]
extern crate approx;
#[cfg(feature = "serde")]
#[macro_use]
extern crate serde;
use std::ops::*;
use std::f64::consts;
use std::fmt;
use std::convert::From;
use num::{Float, NumCast};
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Hash)]
#[repr(transparent)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Deg<T>(pub T);
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Hash)]
#[repr(transparent)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Gon<T>(pub T);
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Hash)]
#[repr(transparent)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Rad<T>(pub T);
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Hash)]
#[repr(transparent)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Turns<T>(pub T);
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Hash)]
#[repr(transparent)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ArcMinutes<T>(pub T);
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Hash)]
#[repr(transparent)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct ArcSeconds<T>(pub T);
pub trait FromAngle<T>
where T: Angle
{
fn from_angle(from: T) -> Self;
}
pub trait IntoAngle<To>
where To: Angle<Scalar = Self::OutputScalar>
{
type OutputScalar: Float;
fn into_angle(self) -> To;
}
pub trait Angle: Clone + FromAngle<Self> + PartialEq + PartialOrd + num::Zero {
type Scalar: Float;
fn new(value: Self::Scalar) -> Self;
fn period() -> Self::Scalar;
fn scalar(&self) -> Self::Scalar;
fn set_scalar(&mut self, value: Self::Scalar);
fn normalize(self) -> Self;
fn is_normalized(&self) -> bool;
fn sin(self) -> Self::Scalar;
fn cos(self) -> Self::Scalar;
fn tan(self) -> Self::Scalar;
fn sin_cos(self) -> (Self::Scalar, Self::Scalar);
fn asin(value: Self::Scalar) -> Self;
fn acos(value: Self::Scalar) -> Self;
fn atan(value: Self::Scalar) -> Self;
fn atan2(x: Self::Scalar, y: Self::Scalar) -> Self;
fn full_turn() -> Self;
fn half_turn() -> Self;
fn quarter_turn() -> Self;
fn invert(self) -> Self;
fn reflect_x(self) -> Self;
}
pub trait Interpolate: Angle {
fn interpolate<U>(&self, right: &U, pos: Self::Scalar) -> Self
where U: Clone + IntoAngle<Self, OutputScalar = Self::Scalar>;
fn interpolate_forward<U>(&self, right: &U, pos: Self::Scalar) -> Self
where U: Clone + IntoAngle<Self, OutputScalar = Self::Scalar>;
}
macro_rules! impl_angle {
($Struct: ident, $period: expr) => {
impl<T: Float> Angle for $Struct<T>
{
type Scalar = T;
fn new(value: T) -> $Struct<T> {
$Struct(value)
}
fn period() -> T {
cast($period).unwrap()
}
fn scalar(&self) -> T {
self.0
}
fn set_scalar(&mut self, value: T) {
self.0 = value;
}
fn is_normalized(&self) -> bool {
self.0 >= T::zero() && self.0 < Self::period()
}
fn normalize(self) -> $Struct<T> {
if !self.is_normalized() {
let shifted = self.0 % Self::period();
if shifted < T::zero() {
$Struct(shifted + Self::period())
} else {
$Struct(shifted)
}
} else {
self
}
}
fn sin(self) -> T {
Rad::from_angle(self).0.sin()
}
fn cos(self) -> T {
Rad::from_angle(self).0.cos()
}
fn tan(self) -> T {
Rad::from_angle(self).0.tan()
}
fn sin_cos(self) -> (T, T) {
Rad::from_angle(self).0.sin_cos()
}
fn asin(value: T) -> $Struct<T> {
$Struct::from_angle(Rad(value.asin()))
}
fn acos(value: T) -> $Struct<T> {
$Struct::from_angle(Rad(value.acos()))
}
fn atan(value: T) -> $Struct<T> {
$Struct::from_angle(Rad(value.atan()))
}
fn atan2(y: T, x: T) -> $Struct<T> {
$Struct::from_angle(Rad(y.atan2(x)))
}
fn full_turn() -> Self {
$Struct(Self::period())
}
fn half_turn() -> Self {
$Struct(cast::<_, Self::Scalar>(0.5).unwrap() * Self::period())
}
fn quarter_turn() -> Self {
$Struct(cast::<_, Self::Scalar>(0.25).unwrap() * Self::period())
}
fn invert(self) -> Self {
self + Self::half_turn()
}
fn reflect_x(self) -> Self {
Self::full_turn() - self
}
}
impl<T: Float> Interpolate for $Struct<T> {
fn interpolate<U>(&self, right: &U, pos: Self::Scalar) -> Self
where U: Clone + IntoAngle<Self, OutputScalar=Self::Scalar>
{
let end = right.clone().into_angle();
let forward_distance = (end.0 - self.0).abs();
let inv_pos = cast::<_, Self::Scalar>(1.0).unwrap() - pos;
if forward_distance > Self::half_turn().0 {
if *self > end {
$Struct(self.0 * inv_pos + (end.0 + Self::period()) * pos)
} else {
$Struct((self.0 + Self::period()) * inv_pos + end.0 * pos)
}
} else {
$Struct(self.0 * inv_pos + end.0 * pos)
}
}
fn interpolate_forward<U>(&self, right: &U, pos: Self::Scalar) -> Self
where U: Clone + IntoAngle<Self, OutputScalar = Self::Scalar>
{
let inv_pos = cast::<_, Self::Scalar>(1.0).unwrap() - pos;
$Struct(self.0 * inv_pos + right.clone().into_angle().0 * pos)
}
}
#[cfg(feature = "approx")]
impl<T: Float + approx::AbsDiffEq> approx::AbsDiffEq for $Struct<T>
where T::Epsilon: Clone,
{
type Epsilon = T::Epsilon;
fn default_epsilon() -> Self::Epsilon {
T::default_epsilon()
}
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool
{
let inv_self = self.clone().reflect_x();
self.0.abs_diff_eq(&other.0, epsilon.clone())
|| self.0.abs_diff_eq(&other.clone().reflect_x().0,
epsilon.clone())
|| inv_self.0.abs_diff_eq(&other.0, epsilon)
}
}
#[cfg(feature = "approx")]
impl<T: Float + approx::RelativeEq> approx::RelativeEq for $Struct<T>
where T::Epsilon: Clone,
{
fn default_max_relative() -> Self::Epsilon {
T::default_max_relative()
}
fn relative_eq(&self, other: &Self, epsilon: Self::Epsilon,
max_relative: Self::Epsilon) -> bool {
let inv_self = self.clone().reflect_x();
self.0.relative_eq(&other.0, epsilon.clone(), max_relative.clone())
|| self.0.relative_eq(&other.clone().reflect_x().0,
epsilon.clone(), max_relative.clone())
|| inv_self.0.relative_eq(&other.0, epsilon, max_relative)
}
}
#[cfg(feature = "approx")]
impl<T: Float + approx::UlpsEq> approx::UlpsEq for $Struct<T>
where T::Epsilon: Clone,
{
fn default_max_ulps() -> u32 {
T::default_max_ulps()
}
fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
let inv_self = self.clone().reflect_x();
self.0.ulps_eq(&other.0, epsilon.clone(), max_ulps)
|| self.0.ulps_eq(&other.clone().reflect_x().0, epsilon.clone(), max_ulps)
|| inv_self.0.ulps_eq(&other.0, epsilon, max_ulps)
}
}
impl<T: Rem<T, Output=T>> Rem for $Struct<T> {
type Output=$Struct<T>;
fn rem(self, rhs: $Struct<T>) -> $Struct<T> {
$Struct(self.0 % rhs.0)
}
}
impl<T: RemAssign> RemAssign for $Struct<T> {
fn rem_assign(&mut self, rhs: $Struct<T>) {
self.0 %= rhs.0;
}
}
impl<U, T> Add<U> for $Struct<T>
where T: Float + Add<T, Output=T>,
U: IntoAngle<$Struct<T>, OutputScalar=T>
{
type Output=$Struct<T>;
fn add(self, rhs: U) -> $Struct<T> {
$Struct(self.0 + rhs.into_angle().0)
}
}
impl<U, T> AddAssign<U> for $Struct<T>
where T: Float + AddAssign<T>,
U: IntoAngle<$Struct<T>, OutputScalar=T>
{
fn add_assign(&mut self, rhs: U) {
self.0 += rhs.into_angle().0;
}
}
impl<U, T> Sub<U> for $Struct<T>
where T: Float + Sub<T, Output=T>,
U: IntoAngle<$Struct<T>, OutputScalar=T>
{
type Output=$Struct<T>;
fn sub(self, rhs: U) -> $Struct<T> {
$Struct(self.0 - rhs.into_angle().0)
}
}
impl<U, T> SubAssign<U> for $Struct<T>
where T: Float + SubAssign<T>,
U: IntoAngle<$Struct<T>, OutputScalar=T>
{
fn sub_assign(&mut self, rhs: U) {
self.0 -= rhs.into_angle().0;
}
}
impl<T: Mul<T, Output=T>> Mul<T> for $Struct<T> {
type Output=$Struct<T>;
fn mul(self, rhs: T) -> $Struct<T> {
$Struct(self.0 * rhs)
}
}
impl<T: MulAssign<T>> MulAssign<T> for $Struct<T> {
fn mul_assign(&mut self, rhs: T) {
self.0 *= rhs;
}
}
impl<T: Div<T, Output=T>> Div<T> for $Struct<T> {
type Output=$Struct<T>;
fn div(self, rhs: T) -> $Struct<T> {
$Struct(self.0 / rhs)
}
}
impl<T: DivAssign<T>> DivAssign<T> for $Struct<T> {
fn div_assign(&mut self, rhs: T) {
self.0 /= rhs;
}
}
impl<T: Neg<Output=T>> Neg for $Struct<T> {
type Output=$Struct<T>;
fn neg(self) -> $Struct<T> {
$Struct(-self.0)
}
}
impl<T: Float> num::Zero for $Struct<T> {
fn zero() -> $Struct<T> {
$Struct(T::zero())
}
fn is_zero(&self) -> bool {
self.0 == T::zero()
}
}
impl<T: num::Zero> Default for $Struct<T> {
fn default() -> $Struct<T> {
$Struct(T::zero())
}
}
impl<T, U> FromAngle<U> for $Struct<T>
where U: Angle<Scalar=T>,
T: Float,
{
fn from_angle(from: U) -> $Struct<T> {
$Struct(from.scalar() * $Struct::period() / U::period())
}
}
}
}
macro_rules! impl_from_for_angle {
($from: ty, $to: ty) => {
impl<T: Float> From<$from> for $to {
fn from(from: $from) -> $to {Self::from_angle(from)}
}
}
}
impl_angle!(Deg, 360.0);
impl_angle!(Gon, 400.0);
impl_angle!(Rad, consts::PI * 2.0);
impl_angle!(Turns, 1.0);
impl_angle!(ArcMinutes, 360.0 * 60.0);
impl_angle!(ArcSeconds, 360.0 * 3600.0);
impl_from_for_angle!(Deg<T>, Rad<T>);
impl_from_for_angle!(Deg<T>, Turns<T>);
impl_from_for_angle!(Deg<T>, Gon<T>);
impl_from_for_angle!(Gon<T>, Deg<T>);
impl_from_for_angle!(Gon<T>, Rad<T>);
impl_from_for_angle!(Gon<T>, Turns<T>);
impl_from_for_angle!(Rad<T>, Deg<T>);
impl_from_for_angle!(Rad<T>, Gon<T>);
impl_from_for_angle!(Rad<T>, Turns<T>);
impl_from_for_angle!(Turns<T>, Deg<T>);
impl_from_for_angle!(Turns<T>, Gon<T>);
impl_from_for_angle!(Turns<T>, Rad<T>);
impl_from_for_angle!(ArcMinutes<T>, Deg<T>);
impl_from_for_angle!(ArcSeconds<T>, Deg<T>);
impl_from_for_angle!(ArcSeconds<T>, ArcMinutes<T>);
impl<T: Float> Deg<T> {
pub fn from_components(degs: Deg<T>, mins: ArcMinutes<T>, secs: ArcSeconds<T>) -> Self {
degs + mins + secs
}
pub fn decompose(self) -> (Deg<T>, ArcMinutes<T>, ArcSeconds<T>) {
let sixty: T = cast(60.0).unwrap();
let degs = self.0.floor();
let rem = self.0 - degs;
let mins = (rem * sixty).floor();
let rem_s = rem * sixty - mins;
let seconds = rem_s * sixty;
(Deg(degs), ArcMinutes(mins), ArcSeconds(seconds))
}
}
impl<T: Float> Rad<T> {
pub fn pi() -> Rad<T> {
Rad(cast(consts::PI).unwrap())
}
pub fn pi_over_2() -> Rad<T> {
Rad(cast(consts::PI / 2.0).unwrap())
}
pub fn pi_over_3() -> Rad<T> {
Rad(cast(consts::PI / 3.0).unwrap())
}
pub fn pi_over_4() -> Rad<T> {
Rad(cast(consts::PI / 4.0).unwrap())
}
}
impl<T, U> IntoAngle<U> for T
where U: Angle<Scalar = T::Scalar> + FromAngle<T>,
T: Angle
{
type OutputScalar = T::Scalar;
fn into_angle(self) -> U {
U::from_angle(self)
}
}
impl<T: fmt::Display> fmt::Display for Deg<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}°", self.0)
}
}
impl<T: fmt::Display> fmt::Display for Gon<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}gon", self.0)
}
}
impl<T: fmt::Display> fmt::Display for Rad<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}r", self.0)
}
}
impl<T: fmt::Display> fmt::Display for Turns<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl<T: fmt::Display> fmt::Display for ArcMinutes<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}'", self.0)
}
}
impl<T: fmt::Display> fmt::Display for ArcSeconds<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}\"", self.0)
}
}
pub fn mean<T, Scalar, Out>(iter: T) -> Out
where T: IntoIterator,
T::Item: IntoAngle<Rad<Scalar>, OutputScalar=Scalar>,
Scalar: Float + AddAssign,
Out: Angle<Scalar=Scalar> + FromAngle<Rad<Scalar>>,
{
let mut sum_of_sines: Scalar = cast(0.0).unwrap();
let mut sum_of_cosines: Scalar = cast(0.0).unwrap();
for angle in iter.into_iter() {
let intermediate_angle: Rad<Scalar> = angle.into_angle();
let (sin, cos) = intermediate_angle.sin_cos();
sum_of_sines += sin;
sum_of_cosines += cos;
}
Out::atan2(sum_of_sines, sum_of_cosines).normalize()
}
fn cast<T: NumCast, U: NumCast>(from: T) -> Option<U> {
U::from(from)
}
#[cfg(test)]
mod test {
#[cfg(feature = "serde")]
extern crate serde_test;
use std::f64::consts;
use std::f64;
use super::*;
#[test]
fn test_construct() {
assert_relative_eq!(Deg(50.0), Deg::new(50.0));
let mut a1 = Deg(100.0);
let scalar = a1.scalar();
a1.set_scalar(scalar + 150.0);
assert_relative_eq!(a1, Deg(250.0));
}
#[test]
fn test_convert() {
assert_relative_eq!(ArcMinutes(120.0).into_angle(), Deg(2.0), epsilon=1e-6);
assert_relative_eq!(ArcMinutes(120.0).into_angle(), Gon(2.222222), epsilon=1e-6);
assert_relative_eq!(ArcSeconds(30.0).into_angle(), ArcMinutes(0.5), epsilon=1e-6);
assert_relative_eq!(Deg(30.0) + ArcMinutes(30.0) + ArcSeconds(30.0),
Deg(30.50833333333), epsilon=1e-6);
assert_relative_eq!(Rad(consts::PI).into_angle(), Deg(180.0), epsilon=1e-6);
assert_relative_eq!(Turns(0.25).into_angle(), Deg(90.0), epsilon=1e-6);
assert_relative_eq!(Turns(0.25).into_angle(), Rad(consts::PI / 2.0), epsilon=1e-6);
assert_relative_eq!(ArcMinutes(600.0).into_angle(), Deg(10.0), epsilon=1e-6);
assert_relative_eq!(ArcMinutes(5400.0).into_angle(), Rad(consts::PI / 2.0), epsilon=1e-6);
assert_relative_eq!(Gon(100.0).into_angle(), Deg(90.0), epsilon=1e-6);
assert_relative_eq!(Gon(50.0).into_angle(), Rad(consts::PI / 4.0), epsilon=1e-6);
}
#[test]
fn test_arithmetic() {
{
let a1 = Rad(2.0);
let a2 = Deg(100.0);
let a3 = a2 + a1;
assert_relative_eq!(a3.0, 214.59, epsilon=1e-2);
let a4 = Deg(50.0);
let a5 = a2 + a4;
assert_ulps_eq!(a5.0, 150.0);
let mut a6 = Deg(123.0);
a6 += Deg(10.0);
a6 += Rad::pi();
assert_ulps_eq!(a6.0, 313.0);
let a7 = Deg(50.0);
assert_ulps_eq!(a7 * 2.0, Deg(100.0));
}
{
let a1 = Rad(2.0);
let a2 = a1 % Rad(1.5);
assert_ulps_eq!(a2, Rad(0.5));
assert_ulps_eq!(Rad(1.0) * 2.0, Rad(2.0));
assert_ulps_eq!(Rad(consts::PI * 2.0) / 2.0, Rad(consts::PI));
}
{
let a10 = Gon(15.0);
let a11 = Deg(43.0);
let a12 = a11 + a10;
assert_relative_eq!(a12.0, 56.5, epsilon=1e-2);
let a13 = a10 + a11;
assert_relative_eq!(a13.0, 62.7778, epsilon=1e-2);
}
}
#[test]
fn test_trig() {
assert_ulps_eq!(Deg(0.0).sin(), 0.0);
assert_ulps_eq!(Gon(0.0).sin(), 0.0);
assert_ulps_eq!(Rad(consts::PI / 2.0).sin(), 1.0);
assert_ulps_eq!(Deg(90.0).sin(), 1.0);
assert_ulps_eq!(Deg(45.0).tan(), 1.0);
assert_relative_eq!(Deg(405.0).tan(), 1.0, epsilon=1e-6);
assert_relative_eq!(Gon(450.0).tan(), 1.0, epsilon=1e-6);
let a1 = Rad(consts::PI * 1.25);
assert_relative_eq!(a1.cos(), -f64::sqrt(2.0) / 2.0, epsilon=1e-6);
assert_relative_eq!(a1.cos(), Deg(135.0).cos(), epsilon=1e-6);
assert_relative_eq!(a1.cos(), Gon(150.0).cos(), epsilon=1e-6);
assert_relative_eq!(Deg::acos(1.0), Deg(0.0));
assert_relative_eq!(Deg::acos(0.0), Deg(90.0));
assert_relative_eq!(Deg::acos(0.0), Deg::from(Gon(100.0)));
assert_relative_eq!(Rad::acos(0.0), Rad::pi_over_2());
}
#[test]
fn test_equality() {
let a1 = Rad(2.0);
assert_ulps_eq!(a1, Rad(2.0));
assert_ulps_eq!(Deg(200.0), Deg(200.0));
assert!(!(Deg(200.0) == Deg(100.0)));
assert!(Deg(200.0) < Deg(300.0));
assert!(Deg(250.0) > Deg(100.0));
assert_relative_eq!(Deg(359.999999), Deg(0.0), epsilon=1e-4);
assert_ulps_eq!(Deg(359.999999), Deg(0.0), epsilon=1e-4);
assert_ulps_eq!(Deg(359.99999), Deg(0.0), epsilon=1e-4);
}
#[test]
fn test_normalize() {
let mut a1 = Deg(200.0);
a1 += Deg(300.0);
assert_ulps_eq!(a1, Deg(500.0));
a1 = a1.normalize();
assert_ulps_eq!(a1, Deg(140.0));
assert_ulps_eq!(a1.normalize(), a1);
let a2 = Deg(50.0);
assert_ulps_eq!(a2 - Deg(150.0), Deg(-100.0));
let a3 = a2 - Deg(100.0);
assert!(!a3.is_normalized());
assert_ulps_eq!(a3.normalize(), Deg(310.0));
assert_ulps_eq!(a3.normalize().normalize(), a3.normalize());
assert!(a3.normalize().is_normalized());
let a4 = Rad(consts::PI);
let a5 = a4 + Rad(consts::PI * 2.0);
assert_ulps_eq!(a5, Rad(consts::PI * 3.0));
assert!(!a3.is_normalized());
assert_ulps_eq!(a5.normalize(), Rad(consts::PI));
let a6 = a4 - Rad(consts::PI * 2.0);
assert_ulps_eq!(a6, Rad(consts::PI * -1.0));
assert!(!a6.is_normalized());
assert_ulps_eq!(a6.normalize(), a5.normalize());
assert_ulps_eq!(Deg(360.0).normalize(), Deg(0.0));
assert_ulps_eq!(Deg(-1.0).normalize(), Deg(359.0));
assert_ulps_eq!(Deg(-360.0).normalize(), Deg(0.0));
assert_relative_eq!(Deg(-359.9).normalize(), Deg(0.1), epsilon=1e-6);
assert_relative_eq!(Gon(725.0).normalize().into_angle(), Deg(292.5), epsilon=1e-6);
assert_relative_eq!(Gon(-275.0).normalize(), Gon(125.0), epsilon=1e-6);
}
#[test]
fn decompose() {
{
let (deg, min, sec) = Deg(50.25).decompose();
assert_ulps_eq!(deg, Deg(50.0));
assert_ulps_eq!(min, ArcMinutes(15.0));
assert_ulps_eq!(sec, ArcSeconds(0.0));
}
{
let (deg, min, sec) = Deg(90.3131).decompose();
assert_ulps_eq!(deg, Deg(90.0));
assert_ulps_eq!(min, ArcMinutes(18.0));
assert_relative_eq!(sec, ArcSeconds(47.16), epsilon=1e-6);
}
}
#[test]
fn test_interpolate() {
assert_relative_eq!(Deg(60.0).interpolate(&Deg(120.0), 0.5), Deg(90.0));
assert_relative_eq!(Deg(50.0).interpolate(&Rad(consts::PI), 0.75),
Deg(147.5), epsilon=1e-6);
assert_relative_eq!(Turns(0.50).interpolate(&Deg(30.0), 0.25),
Turns(0.39583333333), epsilon=1e-6);
assert_relative_eq!(Deg(100.0).interpolate(&Deg(310.0), 0.5).normalize(), Deg(25.0));
assert_relative_eq!(Deg(100.0).interpolate_forward(&Deg(310.0), 0.5).normalize(),
Deg(205.0));
assert_relative_eq!(Gon(66.6666667).interpolate(&Deg(120.0), 0.5), Gon(100.0), epsilon=1e-6);
assert_relative_eq!(Rad::pi_over_2().interpolate(&Rad(0.0), 0.5), Rad::pi_over_4());
}
#[test]
fn test_constants() {
assert_ulps_eq!(Deg::half_turn(), Deg(180.0));
assert_ulps_eq!(Deg::quarter_turn(), Deg(90.0));
assert_ulps_eq!(Rad::half_turn(), Rad(consts::PI));
assert_ulps_eq!(Rad::<f32>::full_turn(), Rad(Rad::period()));
assert_ulps_eq!(Gon::half_turn(), Gon(200.0));
assert_ulps_eq!(Gon::quarter_turn(), Gon(100.0));
}
#[test]
fn test_invert() {
assert_ulps_eq!(Deg(0.0).invert(), Deg(180.0));
assert_ulps_eq!(Deg(180.0).invert().normalize(), Deg(0.0));
assert_ulps_eq!(Gon(200.0).invert().normalize(), Gon(0.0));
assert_ulps_eq!(Deg(80.0).invert(), Deg(260.0));
assert_ulps_eq!(Gon(80.0).invert(), Gon(280.0));
}
#[test]
fn test_reflect_x() {
assert_relative_eq!(Deg(359.9999999999).reflect_x(),
Deg(0.0000000000001), epsilon=1e-5);
assert_relative_eq!(Deg(180.0).reflect_x(), Deg(180.0));
assert_relative_eq!(Deg(90.0).reflect_x(), Deg(90.0));
assert_relative_eq!(Deg(0.0).reflect_x(), Deg(0.0));
assert_relative_eq!(Deg(45.0).reflect_x(), Deg(315.0));
assert_relative_eq!(Deg(215.0).reflect_x(), Deg(145.0));
assert_relative_eq!(Gon(50.0).reflect_x(), Gon(350.0));
assert_relative_eq!(Gon(215.0).reflect_x(), Gon(185.0));
}
#[test]
fn test_mean() {
assert_relative_eq!(mean(vec![Deg(280.0), Deg(10.0)].into_iter()), Deg(325.0));
assert_relative_eq!(mean(vec![Turns(0.5), Turns(0.0)].into_iter()), Deg(90.0));
assert_relative_eq!(mean([Rad(0.0), Rad(0.0)].into_iter().cloned()), Rad(0.0));
}
#[cfg(feature = "serde")]
#[test]
fn test_serialize() {
use self::serde_test::{Token, assert_tokens};
assert_tokens(&Deg(90.0), &[Token::NewtypeStruct {name: "Deg"}, Token::F64(90.0)]);
assert_tokens(&Rad(0.5f32), &[Token::NewtypeStruct {name: "Rad"}, Token::F32(0.5f32)]);
assert_tokens(&Gon(300.0), &[Token::NewtypeStruct {name: "Gon"}, Token::F64(300.0)]);
assert_tokens(&Turns(0.666), &[Token::NewtypeStruct {name: "Turns"}, Token::F64(0.666)]);
}
#[cfg(feature = "serde")]
#[test]
fn test_deserialize() {
use self::serde_test::{Token, assert_de_tokens};
assert_de_tokens(&Deg(90.0), &[Token::NewtypeStruct {name: "Deg"}, Token::F64(90.0)]);
assert_de_tokens(&Rad(0.5f32), &[Token::NewtypeStruct {name: "Rad"}, Token::F32(0.5f32)]);
assert_de_tokens(&Gon(300.0), &[Token::NewtypeStruct {name: "Gon"}, Token::F64(300.0)]);
assert_de_tokens(&Turns(0.666), &[Token::NewtypeStruct {name: "Turns"}, Token::F64(0.666)]);
}
}