\section{RSAGL.Angle}

RSAGL.Angle supports manipulation of angular values, including
angular arithmetic and trigonometry.

\begin{code}
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}
module RSAGL.Angle
    (Angle,
     BoundAngle(..),
     fromDegrees,
     fromRadians,
     fromRotations,
     sine,
     arcSine,
     cosine,
     arcCosine,
     tangent,
     arcTangent,
     toRadians,
     toRadians_,
     toDegrees,
     toDegrees_,
     toRotations,
     toRotations_,
     scaleAngle,
     zero_angle,
     angularIncrements,
     angleAdd,
     angleSubtract,
     angleNegate,
     absoluteAngle,
     unboundAngle)
    where

import Data.Fixed
import RSAGL.AbstractVector

newtype Angle = Radians Double deriving (Show)
newtype BoundAngle = BoundAngle Angle deriving (Show)

zero_angle :: Angle
zero_angle = Radians 0

instance Eq Angle where
    (==) a b = case (toRadians_ a,toRadians_ b) of
                   (x,y) | abs x == pi && abs y == pi -> True
                   (x,y) | x == y -> True
                   _ -> False

instance Ord Angle where
    compare x y = case () of
                      _ | x == y -> EQ
                      _ -> compare (toRadians_ x) (toRadians_ y)

instance AbstractZero Angle where
    zero = zero_angle

instance AbstractZero BoundAngle where
    zero = BoundAngle zero_angle

instance AbstractAdd Angle Angle where
    add = angleAdd

instance AbstractAdd BoundAngle Angle where
    add (BoundAngle a) x = BoundAngle $ boundAngle $ a `add` x

instance AbstractSubtract Angle Angle where
    sub = angleSubtract

instance AbstractSubtract BoundAngle Angle where
    sub (BoundAngle a) (BoundAngle b) = boundAngle $ a `sub` b

instance AbstractScale Angle where
    scalarMultiply = scaleAngle

instance AbstractVector Angle

instance AbstractMagnitude Angle where
    magnitude = toRotations_ . absoluteAngle
\end{code}

angularIncrements answers n equa-angular values from 0 to 2*pi.

\begin{code}
angularIncrements :: Integer -> [Angle]
angularIncrements subdivisions = map (fromRadians . (2*pi*) . (/ fromInteger subdivisions) . fromInteger) [0 .. subdivisions - 1]
\end{code}

\subsection{Type coercion for Angles}

\begin{code}
fromRadians :: Double -> Angle
fromRadians = Radians

fromDegrees :: Double -> Angle
fromDegrees = Radians . ((*) (pi/180))

fromRotations :: Double -> Angle
fromRotations = Radians . ((*) (2*pi))
\end{code}

\texttt{toDegrees} answers the angle in the range of -180 to 180, inclusive.

\texttt{toDegrees\_} answers the angle in degrees with no range limitation.

\begin{code}
toDegrees :: Angle -> Double
toDegrees x = let x' = toRadians x
                  in x' * 180 / pi

toDegrees_ :: Angle -> Double
toDegrees_ (Radians x) = x * 180 / pi
\end{code}

\texttt{toRadians} answers the angle in the range of -pi .. pi, inclusive.

\texttt{toRadians\_} answers the angle in radians with no range limitation.

\begin{code}
toRadians :: Angle -> Double
toRadians x = let (Radians x') = boundAngle x
                  in x'

toRadians_ :: Angle -> Double
toRadians_ (Radians x) = x
\end{code}

\texttt{toRotations} answers the angle in the range of -0.5 to 0.5, inclusive.

\texttt{toRotations\_} answers the angle in rotations with no range limitation.

\begin{code}
toRotations :: Angle -> Double
toRotations x= let x' = toRadians x
                   in x' / pi / 2

toRotations_ :: Angle -> Double
toRotations_ (Radians x) = x / pi / 2
\end{code}

\subsection{Manipulating Angular values}

\begin{code}
scaleAngle :: Double -> Angle -> Angle
scaleAngle x = Radians . (*x) . toRadians_

angleAdd :: Angle -> Angle -> Angle
angleAdd (Radians x) (Radians y) = Radians $ x + y

angleSubtract :: Angle -> Angle -> Angle
angleSubtract (Radians x) (Radians y) = Radians $ x - y

angleNegate :: Angle -> Angle
angleNegate (Radians x) = Radians $ negate x

absoluteAngle :: Angle -> Angle
absoluteAngle (Radians x) = Radians $ abs x

sine :: Angle -> Double
sine (Radians x) = sin x

arcSine :: Double -> Angle
arcSine = fromRadians . asin

cosine :: Angle -> Double
cosine (Radians x) = cos x

arcCosine :: Double -> Angle
arcCosine = fromRadians . acos

tangent :: Angle -> Double
tangent (Radians x) = tan x

arcTangent :: Double -> Angle
arcTangent = fromRadians . atan
\end{code}

\texttt{boundAngle} forces the angle into the range (-pi..pi).

\begin{code}
boundAngle :: Angle -> Angle
boundAngle (Radians x) = Radians $ if bounded > pi then bounded - 2*pi else bounded
    where bounded = x `mod'` (2*pi)

unboundAngle :: BoundAngle -> Angle
unboundAngle (BoundAngle a) = a
\end{code}