use num_traits::NumCast;
use std::ops::{Mul, Div, Add, Sub, Neg};
use float_cmp::{Ulps, ApproxEq};
use FullFloat;
use vector::Vec2;
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[derive(Serialize, Deserialize)]
pub struct Angle<F>(F);
impl<F: FullFloat> Angle<F>
{
#[inline]
pub fn new_radians(radians: F) -> Angle<F>
{
Angle::<F>::from_radians(radians)
}
pub fn from_radians(radians: F) -> Angle<F>
{
Angle(radians)
}
pub fn as_radians(&self) -> F {
self.0
}
#[inline]
pub fn new_degrees(degrees: F) -> Angle<F>
{
Angle::<F>::from_degrees(degrees)
}
pub fn from_degrees(degrees: F) -> Angle<F>
{
let one_eighty: F = NumCast::from(180.0_f32).unwrap();
Angle(F::PI() * degrees / one_eighty)
}
pub fn as_degrees(&self) -> F {
let one_eighty: F = NumCast::from(180.0_f32).unwrap();
self.0 * one_eighty / F::PI()
}
#[inline]
pub fn new_cycles(cycles: F) -> Angle<F>
{
Angle::<F>::from_cycles(cycles)
}
pub fn from_cycles(cycles: F) -> Angle<F>
{
let two: F = NumCast::from(2.0_f32).unwrap();
Angle(two * F::PI() * cycles)
}
pub fn as_cycles(&self) -> F {
let two: F = NumCast::from(2.0_f32).unwrap();
self.0 / (two * F::PI())
}
pub fn of_vector(vec: &Vec2<F>) -> Angle<F>
{
Angle(vec.y.atan2(vec.x))
}
}
impl<F: FullFloat> Mul<F> for Angle<F>
{
type Output = Angle<F>;
fn mul(self, rhs: F) -> Angle<F> {
Angle(self.0 * rhs)
}
}
impl<F: FullFloat> Div<F> for Angle<F>
{
type Output = Angle<F>;
fn div(self, rhs: F) -> Angle<F> {
Angle(self.0 / rhs)
}
}
impl<F: FullFloat> Add<Angle<F>> for Angle<F>
{
type Output = Angle<F>;
fn add(self, rhs: Angle<F>) -> Angle<F> {
Angle(self.0 + rhs.0)
}
}
impl<F: FullFloat> Sub<Angle<F>> for Angle<F>
{
type Output = Angle<F>;
fn sub(self, rhs: Angle<F>) -> Angle<F> {
Angle(self.0 - rhs.0)
}
}
impl<F: FullFloat> Neg for Angle<F> {
type Output = Angle<F>;
fn neg(self) -> Angle<F> {
Angle(-self.0)
}
}
impl<F: FullFloat> ApproxEq for Angle<F> {
type Flt = F;
fn approx_eq(&self, other: &Self,
epsilon: <F as ApproxEq>::Flt,
ulps: <<F as ApproxEq>::Flt as Ulps>::U) -> bool
{
self.0.approx_eq(&other.0, epsilon, ulps)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::f32::consts::PI;
use std::f32::EPSILON;
use vector::Vec2;
#[test]
fn test_radians() {
let f: f32 = 1.234;
let a = Angle::from_radians(f);
assert_eq!(a.as_radians(), f);
}
#[test]
fn test_degrees() {
let f: f32 = 1.234;
let a = Angle::from_degrees(f);
assert_eq!(a.as_degrees(), f);
}
#[test]
fn test_cycles() {
let f: f32 = 1.234;
let a = Angle::from_cycles(f);
assert_eq!(a.as_cycles(), f);
}
#[test]
fn test_relations() {
let h1 = Angle::from_radians(PI);
let h2 = Angle::from_degrees(180.0);
let h3 = Angle::from_cycles(0.5);
assert!(h1.approx_eq(&h2, 2.0 * EPSILON, 2));
assert!(h1.approx_eq(&h3, 2.0 * EPSILON, 2));
assert!(h2.approx_eq(&h3, 2.0 * EPSILON, 2));
}
#[test]
fn test_vector_angle() {
let q1 = Vec2::new(1.0, 1.0);
let q2 = Vec2::new(-1.0, 1.0);
let q3 = Vec2::new(-1.0, -1.0);
let q4 = Vec2::new(1.0, -1.0);
assert!(Angle::of_vector(&q1).approx_eq(
&Angle::from_cycles(1.0/8.0), 2.0 * EPSILON, 2));
assert!(Angle::of_vector(&q2).approx_eq(
&Angle::from_cycles(3.0/8.0), 2.0 * EPSILON, 2));
assert!(Angle::of_vector(&q3).approx_eq(
&Angle::from_cycles(-3.0/8.0), 2.0 * EPSILON, 2));
assert!(Angle::of_vector(&q4).approx_eq(
&Angle::from_cycles(-1.0/8.0), 2.0 * EPSILON, 2));
}
}