-- |

-- Module:      Data.Geo.Jord.NVector

-- Copyright:   (c) 2018 Cedric Liegeois

-- License:     BSD3

-- Maintainer:  Cedric Liegeois <ofmooseandmen@yahoo.fr>

-- Stability:   experimental

-- Portability: portable

--

-- Types and functions for working with n-vectors.

--

module Data.Geo.Jord.NVector

    ( NVector(x, y, z)

    , nvector

    , cross

    , dot

    , norm

    , scale

    , unit

    , zero

    ) where



import Data.Geo.Jord.Quantity



-- | Represents a position as the normal vector to the sphere.

data NVector = NVector

    { x :: Double

    , y :: Double

    , z :: Double

    } deriving (Eq, Show)



-- | Add and subtract 'NVector's.

instance Quantity NVector where

    add a b = NVector x' y' z'

      where

        x' = x a + x b

        y' = y a + y b

        z' = z a + z b

    sub a b = NVector x' y' z'

      where

        x' = x a - x b

        y' = y a - y b

        z' = z a - z b



-- | Smart 'NVector' constructor. The returned 'NVector' is a 'unit' vector.

nvector :: Double -> Double -> Double -> NVector

nvector x' y' z' = unit (NVector x' y' z')



-- | Computes the cross product of the two given 'NVector's.

cross :: NVector -> NVector -> NVector

cross a b = NVector x' y' z'

  where

    x' = y a * z b - z a * y b

    y' = z a * x b - x a * z b

    z' = x a * y b - y a * x b



-- | Computes the dot product of the two given 'NVector's.

dot :: NVector -> NVector -> Double

dot a b = x a * x b + y a * y b + z a * z b



-- | Computes the norm of the given 'NVector'.

norm :: NVector -> Double

norm a = sqrt ((x a * x a) + (y a * y a) + (z a * z a))



-- | Multiplies each component of the given 'NVector' by the given value.

scale :: NVector -> Double -> NVector

scale a s = NVector x' y' z'

  where

    x' = x a * s

    y' = y a * s

    z' = z a * s



-- | Normalises the given 'NVector'.

unit :: NVector -> NVector

unit a = scale a s

  where

    s = 1.0 / norm a



-- | [0, 0, 0] - not a valid 'NVector', but can be used as the identity value during reduction.

zero :: NVector

zero = NVector 0.0 0.0 0.0