{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}
-- |
-- Module      :  FRP.Animas.Vector2
-- Copyright   :  (c) Antony Courtney and Henrik Nilsson, Yale University, 2003
-- License     :  BSD-style (see the LICENSE file in the distribution)
--
-- Maintainer  :  nilsson@cs.yale.edu
-- Stability   :  provisional
-- Portability :  non-portable (GHC extensions)
--
-- 2D vector abstraction (R^2).
--
-- ToDo: Deriving Show, or provide dedicated show instance?

module FRP.Animas.Vector2 (
    Vector2,
    vector2,
    vector2X,
    vector2Y,
    vector2XY,
    vector2Polar,
    vector2Rho,
    vector2Theta,
    vector2RhoTheta,
    vector2Rotate
) where

import FRP.Animas.VectorSpace
import FRP.Animas.Forceable

-- | 2-dimensional vector type
data RealFloat a => Vector2 a = Vector2 !a !a deriving (Eq,Show)

-- | Create a 2D vector
vector2 :: RealFloat a 
           => a -- ^ X magnitude
           -> a -- ^ Y magnitude
           -> Vector2 a -- ^ Vector
vector2 x y = Vector2 x y

-- ^ Obtain the X-magnitude of a vector
vector2X :: RealFloat a => Vector2 a -> a
vector2X (Vector2 x _) = x

-- ^ Obtain the Y-magnitude of a vector
vector2Y :: RealFloat a => Vector2 a -> a
vector2Y (Vector2 _ y) = y

-- ^ Obtain the X and Y magnitudes of a vector as an ordered pair
vector2XY :: RealFloat a => 
             Vector2 a 
             -> (a, a) -- ^ (X, Y)
vector2XY (Vector2 x y) = (x, y)

-- ^ Create a vector from polar coordinates (magnitude/rho, direction/theta (radians))
vector2Polar :: RealFloat a => 
                a -- ^ Rho
                -> a -- ^ Theta
                -> Vector2 a -- ^ Vector
vector2Polar rho theta = Vector2 (rho * cos theta) (rho * sin theta) 

-- ^ Obtain the magnitude of a vector
vector2Rho :: RealFloat a => Vector2 a -> a
vector2Rho (Vector2 x y) = sqrt (x * x + y * y)

-- ^ Obtain the direction of a vector
vector2Theta :: RealFloat a => Vector2 a -> a
vector2Theta (Vector2 x y) = atan2 y x

-- ^ Obtain the magnitude and direction of a vector as an ordered pair
vector2RhoTheta :: RealFloat a => 
                   Vector2 a 
                   -> (a, a) -- ^ (Rho, Theta)
vector2RhoTheta v = (vector2Rho v, vector2Theta v)

instance RealFloat a => VectorSpace (Vector2 a) a where
    zeroVector = Vector2 0 0

    a *^ (Vector2 x y) = Vector2 (a * x) (a * y)

    (Vector2 x y) ^/ a = Vector2 (x / a) (y / a)

    negateVector (Vector2 x y) = (Vector2 (-x) (-y))

    (Vector2 x1 y1) ^+^ (Vector2 x2 y2) = Vector2 (x1 + x2) (y1 + y2)

    (Vector2 x1 y1) ^-^ (Vector2 x2 y2) = Vector2 (x1 - x2) (y1 - y2)

    (Vector2 x1 y1) `dot` (Vector2 x2 y2) = x1 * x2 + y1 * y2


-- ^ Rotate a vector by some angle theta
vector2Rotate :: RealFloat a => a -- ^ Theta (radians)
                 -> Vector2 a -- ^ Initial vector
                 -> Vector2 a -- ^ Rotated vector
vector2Rotate theta' v = vector2Polar (vector2Rho v) (vector2Theta v + theta')

instance RealFloat a => Forceable (Vector2 a) where
     force = id