packages feed

accelerate-examples-0.15.0.0: examples/ray/Vec3.hs

{-# LANGUAGE DeriveDataTypeable    #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables   #-}
{-# LANGUAGE TypeFamilies          #-}
{-# LANGUAGE UndecidableInstances  #-}
{-# OPTIONS -fno-warn-missing-methods #-}

module Vec3
  where

import Prelude
import Data.Typeable
import Data.Array.Accelerate
import Data.Array.Accelerate.Smart
import Data.Array.Accelerate.Tuple
import Data.Array.Accelerate.Array.Sugar


-- | Points and vectors in 3D Space
--
type Position   = Vec3
type Direction  = Vec3
type Normal     = (Position, Direction)

-- | A parameterised point in XYZ space.
--
type Vec3       = XYZ Float

-- | Make a Vec3
--
makeVec3 :: Exp Float -> Exp Float -> Exp Float -> Exp Vec3
makeVec3 x y z = lift (XYZ x y z)


-- | Extract the components of the Vec3
--
xyzOfVec :: Exp Vec3 -> (Exp Float, Exp Float, Exp Float)
xyzOfVec v
  = let XYZ x y z       = unlift v
    in  (x, y, z)


-- | Yield the magnitude of a vector.
--
magnitude :: Exp Vec3 -> Exp Float
magnitude v
  = let XYZ x y z       = unlift v
    in
    sqrt (x * x + y * y + z * z)


-- | Normalise a vector to have unit length.
--
normalise :: Exp Vec3 -> Exp Vec3
normalise v = (1.0 / magnitude v) .* v


-- | Component-wise multiply a vector by a scalar.
--
infixl 7 .*
(.*) :: Exp Float -> Exp Vec3 -> Exp Vec3
(.*) s v
  = let XYZ x y z       = unlift v
    in
    makeVec3 (s * x) (s * y) (s * z)


-- | Compute the dot product of two vectors.
--
dot :: Exp Vec3 -> Exp Vec3 -> Exp Float
dot v1 v2
  = let XYZ x1 y1 z1    = unlift v1
        XYZ x2 y2 z2    = unlift v2
    in
    x1 * x2 + y1 * y2 + z1 * z2


-- | Clamp a vectors components to some minimum and maximum values.
--
clamp :: Exp Vec3 -> Exp Float -> Exp Float -> Exp Vec3
clamp v minVal maxVal =
  makeVec3 (go x) (go y) (go z)
  where
    XYZ x y z   = unlift v
    go u        = minVal `max` u `min` maxVal


-- | Clip a vector's components to some maximum value.
--
clip :: Exp Vec3 -> Exp Float -> Exp Vec3
clip v maxVal =
  makeVec3 (go x) (go y) (go z)
  where
    XYZ x y z   = unlift v
    go u        = u `min` maxVal


-- Get a Vec3 into Accelerate --------------------------------------------------

data XYZ a = XYZ a a a
  deriving (Eq, Show, Typeable)

type instance EltRepr  (XYZ a)  = EltRepr  (a, a, a)
type instance EltRepr' (XYZ a)  = EltRepr' (a, a, a)

instance Elt a => Elt (XYZ a) where
  eltType (_ :: XYZ a)  = eltType (undefined :: (a, a, a))
  toElt p               = let (x,y,z) = toElt p in XYZ x y z
  fromElt (XYZ x y z)   = fromElt (x, y, z)

  eltType' (_ :: XYZ a) = eltType (undefined :: (a, a, a))
  toElt' p              = let (x,y,z) = toElt p in XYZ x y z
  fromElt' (XYZ x y z)  = fromElt (x, y, z)

instance IsTuple (XYZ a) where
  type TupleRepr (XYZ a) = TupleRepr (a, a, a)
  fromTuple (XYZ x y z) = fromTuple (x, y, z)
  toTuple t             = let (x,y,z) = toTuple t in XYZ x y z

instance (Lift Exp a, Elt (Plain a)) => Lift Exp (XYZ a) where
  type Plain (XYZ a) = XYZ (Plain a)
  lift (XYZ x y z)      = Exp . Tuple $ NilTup `SnocTup` lift x `SnocTup` lift y `SnocTup` lift z

instance Elt a => Unlift Exp (XYZ (Exp a)) where
  unlift t      = let x = Exp $ SuccTupIdx (SuccTupIdx ZeroTupIdx) `Prj` t
                      y = Exp $ SuccTupIdx ZeroTupIdx `Prj` t
                      z = Exp $ ZeroTupIdx `Prj` t
                  in XYZ x y z

-- | Pretend a Vec3 is a number
--
instance Num a => Num (XYZ a) where
 (+) (XYZ x1 x2 x3) (XYZ y1 y2 y3)
        = XYZ (x1 + y1) (x2 + y2) (x3 + y3)

 (-) (XYZ x1 x2 x3) (XYZ y1 y2 y3)
        = XYZ (x1 - y1) (x2 - y2) (x3 - y3)

 (*) (XYZ x1 x2 x3) (XYZ y1 y2 y3)
        = XYZ (x1 * y1) (x2 * y2) (x3 * y3)


instance (Elt a, IsNum a) => Num (Exp (XYZ a)) where
  (+)   = lift2 ((+) :: XYZ (Exp a) -> XYZ (Exp a) -> XYZ (Exp a))
  (-)   = lift2 ((-) :: XYZ (Exp a) -> XYZ (Exp a) -> XYZ (Exp a))
  (*)   = lift2 ((*) :: XYZ (Exp a) -> XYZ (Exp a) -> XYZ (Exp a))