{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleInstances         #-}
{-# LANGUAGE MultiParamTypeClasses     #-}
{-# LANGUAGE StandaloneDeriving        #-}
{-# OPTIONS_GHC -fno-warn-warnings-deprecations #-}
-- |
-- Module      :  Data.Vector2
-- Copyright   :  (c) Antony Courtney and Henrik Nilsson, Yale University, 2003
-- License     :  BSD-style (see the LICENSE file in the distribution)
--
-- Maintainer  :  ivan.perez@keera.co.uk
-- Stability   :  provisional
-- Portability :  non-portable (GHC extensions)
--
-- 2D vector abstraction (R^2).
module Data.Vector2
    ( Vector2         -- Abstract, instance of VectorSpace
    , vector2         -- :: RealFloat a => a -> a -> Vector2 a
    , vector2X        -- :: RealFloat a => Vector2 a -> a
    , vector2Y        -- :: RealFloat a => Vector2 a -> a
    , vector2XY       -- :: RealFloat a => Vector2 a -> (a, a)
    , vector2Polar    -- :: RealFloat a => a -> a -> Vector2 a
    , vector2Rho      -- :: RealFloat a => Vector2 a -> a
    , vector2Theta    -- :: RealFloat a => Vector2 a -> a
    , vector2RhoTheta -- :: RealFloat a => Vector2 a -> (a, a)
    , vector2Rotate   -- :: RealFloat a => a -> Vector2 a -> Vector2 a
    )
  where

-- External imports
import Control.DeepSeq (NFData(..))

-- Internal imports
import Data.VectorSpace

-- * 2D vector, constructors and selectors

-- | 2D Vector.

-- Restrict coefficient space to RealFloat (rather than Floating) for now.
-- While unclear if a complex coefficient space would be useful (and if the
-- result really would be a 2d vector), the only thing causing trouble is the
-- use of atan2 in vector2Theta. Maybe atan2 can be generalized?

data Vector2 a = RealFloat a => Vector2 !a !a

deriving instance Eq a => Eq (Vector2 a)

deriving instance Show a => Show (Vector2 a)

instance NFData a => NFData (Vector2 a) where
  rnf :: Vector2 a -> ()
rnf (Vector2 a
x a
y) = forall a. NFData a => a -> ()
rnf a
x seq :: forall a b. a -> b -> b
`seq` forall a. NFData a => a -> ()
rnf a
y seq :: forall a b. a -> b -> b
`seq` ()

-- | Creates a 2D vector from the cartesian coordinates.
vector2 :: RealFloat a => a -> a -> Vector2 a
vector2 :: forall a. RealFloat a => a -> a -> Vector2 a
vector2 = forall a. RealFloat a => a -> a -> Vector2 a
Vector2

-- | X cartesian coordinate.
vector2X :: RealFloat a => Vector2 a -> a
vector2X :: forall a. RealFloat a => Vector2 a -> a
vector2X (Vector2 a
x a
_) = a
x

-- | Y cartesian coordinate.
vector2Y :: RealFloat a => Vector2 a -> a
vector2Y :: forall a. RealFloat a => Vector2 a -> a
vector2Y (Vector2 a
_ a
y) = a
y

-- | Returns a vector's cartesian coordinates.
vector2XY :: RealFloat a => Vector2 a -> (a, a)
vector2XY :: forall a. RealFloat a => Vector2 a -> (a, a)
vector2XY (Vector2 a
x a
y) = (a
x, a
y)

-- | Creates a 2D vector from the polar coordinates.
vector2Polar :: RealFloat a => a -> a -> Vector2 a
vector2Polar :: forall a. RealFloat a => a -> a -> Vector2 a
vector2Polar a
rho a
theta = forall a. RealFloat a => a -> a -> Vector2 a
Vector2 (a
rho forall a. Num a => a -> a -> a
* forall a. Floating a => a -> a
cos a
theta) (a
rho forall a. Num a => a -> a -> a
* forall a. Floating a => a -> a
sin a
theta)

-- | Calculates the vector's radial distance (magnitude).
vector2Rho :: RealFloat a => Vector2 a -> a
vector2Rho :: forall a. RealFloat a => Vector2 a -> a
vector2Rho (Vector2 a
x a
y) = forall a. Floating a => a -> a
sqrt (a
x forall a. Num a => a -> a -> a
* a
x forall a. Num a => a -> a -> a
+ a
y forall a. Num a => a -> a -> a
* a
y)

-- | Calculates the vector's azimuth (angle).
vector2Theta :: RealFloat a => Vector2 a -> a
vector2Theta :: forall a. RealFloat a => Vector2 a -> a
vector2Theta (Vector2 a
x a
y) = forall a. RealFloat a => a -> a -> a
atan2 a
y a
x

-- | Polar coordinate representation of a 2D vector.
vector2RhoTheta :: RealFloat a => Vector2 a -> (a, a)
vector2RhoTheta :: forall a. RealFloat a => Vector2 a -> (a, a)
vector2RhoTheta Vector2 a
v = (forall a. RealFloat a => Vector2 a -> a
vector2Rho Vector2 a
v, forall a. RealFloat a => Vector2 a -> a
vector2Theta Vector2 a
v)

-- * Vector space instance

instance RealFloat a => VectorSpace (Vector2 a) a where
  zeroVector :: Vector2 a
zeroVector = forall a. RealFloat a => a -> a -> Vector2 a
Vector2 a
0 a
0

  a
a *^ :: a -> Vector2 a -> Vector2 a
*^ (Vector2 a
x a
y) = forall a. RealFloat a => a -> a -> Vector2 a
Vector2 (a
a forall a. Num a => a -> a -> a
* a
x) (a
a forall a. Num a => a -> a -> a
* a
y)

  (Vector2 a
x a
y) ^/ :: Vector2 a -> a -> Vector2 a
^/ a
a = forall a. RealFloat a => a -> a -> Vector2 a
Vector2 (a
x forall a. Fractional a => a -> a -> a
/ a
a) (a
y forall a. Fractional a => a -> a -> a
/ a
a)

  negateVector :: Vector2 a -> Vector2 a
negateVector (Vector2 a
x a
y) = forall a. RealFloat a => a -> a -> Vector2 a
Vector2 (-a
x) (-a
y)

  (Vector2 a
x1 a
y1) ^+^ :: Vector2 a -> Vector2 a -> Vector2 a
^+^ (Vector2 a
x2 a
y2) = forall a. RealFloat a => a -> a -> Vector2 a
Vector2 (a
x1 forall a. Num a => a -> a -> a
+ a
x2) (a
y1 forall a. Num a => a -> a -> a
+ a
y2)

  (Vector2 a
x1 a
y1) ^-^ :: Vector2 a -> Vector2 a -> Vector2 a
^-^ (Vector2 a
x2 a
y2) = forall a. RealFloat a => a -> a -> Vector2 a
Vector2 (a
x1 forall a. Num a => a -> a -> a
- a
x2) (a
y1 forall a. Num a => a -> a -> a
- a
y2)

  (Vector2 a
x1 a
y1) dot :: Vector2 a -> Vector2 a -> a
`dot` (Vector2 a
x2 a
y2) = a
x1 forall a. Num a => a -> a -> a
* a
x2 forall a. Num a => a -> a -> a
+ a
y1 forall a. Num a => a -> a -> a
* a
y2

-- * Additional operations

-- | Rotates a vector with a given angle.
vector2Rotate :: RealFloat a => a -> Vector2 a -> Vector2 a
vector2Rotate :: forall a. RealFloat a => a -> Vector2 a -> Vector2 a
vector2Rotate a
theta' Vector2 a
v = forall a. RealFloat a => a -> a -> Vector2 a
vector2Polar (forall a. RealFloat a => Vector2 a -> a
vector2Rho Vector2 a
v) (forall a. RealFloat a => Vector2 a -> a
vector2Theta Vector2 a
v forall a. Num a => a -> a -> a
+ a
theta')