crocodile-0.1: app/src/Vector.hs
-- Vector library for 3d graphics
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE MagicHash #-}
module Vector where
import Data.List
import Misc
import GHC.Prim
import GHC.Types
import Control.DeepSeq
data Vector = Vector { vecX :: {-# UNPACK #-} !Double,
vecY :: {-# UNPACK #-} !Double,
vecZ :: {-# UNPACK #-} !Double,
vecW :: {-# UNPACK #-} !Double } deriving (Ord, Eq)
type Position = Vector
type Direction = Vector
type Normal = Direction
type TangentSpace = (Normal, Normal, Normal)
type SurfaceLocation = (Position, TangentSpace)
instance Num Vector where
{-# SPECIALIZE INLINE (+) :: Vector -> Vector -> Vector #-}
(Vector !(D# x) !(D# y) !(D# z) !(D# w)) + (Vector !(D# x') !(D# y') !(D# z') !(D# w')) = Vector (D# $ x +## x') (D# $ y +## y') (D# $ z +## z') (D# $ w +## w')
{-# SPECIALIZE INLINE (-) :: Vector -> Vector -> Vector #-}
(Vector !(D# x) !(D# y) !(D# z) !(D# w)) - (Vector !(D# x') !(D# y') !(D# z') !(D# w')) = Vector (D# $ x -## x') (D# $ y -## y') (D# $ z -## z') (D# $ w -## w')
{-# SPECIALIZE INLINE (*) :: Vector -> Vector -> Vector #-}
(Vector !(D# x) !(D# y) !(D# z) !(D# w)) * (Vector !(D# x') !(D# y') !(D# z') !(D# w')) = Vector (D# $ x *## x') (D# $ y *## y') (D# $ z *## z') (D# $ w *## w')
abs (Vector !x !y !z !w) = Vector absX absY absZ absW
where
!absX = abs x
!absY = abs y
!absZ = abs z
!absW = abs w
signum (Vector !x !y !z !w) = Vector signumX signumY signumZ signumW
where
!signumX = signum x
!signumY = signum y
!signumZ = signum z
!signumW = signum w
fromInteger x = Vector x' x' x' x'
where
!x' = fromInteger x
instance Fractional Vector where
{-# SPECIALIZE INLINE (/) :: Vector -> Vector -> Vector #-}
(Vector !(D# x) !(D# y) !(D# z) !(D# w)) / (Vector !(D# x') !(D# y') !(D# z') !(D# w')) = Vector (D# $ x /## x') (D# $ y /## y') (D# $ z /## z') (D# $ w /## w')
fromRational x = Vector x' x' x' x'
where
!x' = fromRational x
instance Show Vector where
show (Vector !x !y !z !w) = "(" ++ show x ++ ", " ++ show y ++ ", " ++ show z ++ ", " ++ show w ++ ")"
instance NFData Vector where
rnf (Vector x y z w) = rnf x `seq` rnf y `seq` rnf z `seq` rnf w
tsTangent :: TangentSpace -> Normal
tsTangent (t, _, _) = t
tsBinormal :: TangentSpace -> Normal
tsBinormal (_, b, _) = b
tsNormal :: TangentSpace -> Normal
tsNormal = thr
xaxis :: Vector
xaxis = Vector 1 0 0 0
yaxis :: Vector
yaxis = Vector 0 1 0 0
zaxis :: Vector
zaxis = Vector 0 0 1 0
waxis :: Vector
waxis = Vector 0 0 0 1
zeroVector :: Vector
zeroVector = Vector 0 0 0 0
setWTo1 :: Vector -> Vector
{-# SPECIALIZE INLINE setWTo1 :: Vector -> Vector #-}
setWTo1 (Vector !x !y !z _) = Vector x y z 1
setWTo0 :: Vector -> Vector
{-# SPECIALIZE INLINE setWTo0 :: Vector -> Vector #-}
setWTo0 (Vector !x !y !z _) = Vector x y z 0
restoreOriginalW :: Vector -> Vector -> Vector
{-# SPECIALIZE INLINE restoreOriginalW :: Vector -> Vector -> Vector #-}
restoreOriginalW (Vector _ _ _ !w') (Vector !x !y !z _) = Vector x y z w'
madd :: Position -> Direction -> Double -> Vector
{-# SPECIALIZE INLINE madd :: Vector -> Vector -> Double -> Vector #-}
madd (Vector !(D# x) !(D# y) !(D# z) !(D# w)) (Vector !(D# x') !(D# y') !(D# z') !(D# w')) !(D# scalar) = Vector x'' y'' z'' w''
where
x'' = D# (x +## (x' *## scalar))
y'' = D# (y +## (y' *## scalar))
z'' = D# (z +## (z' *## scalar))
w'' = D# (w +## (w' *## scalar))
negate :: Direction -> Direction
{-# SPECIALIZE INLINE Vector.negate :: Vector -> Vector #-}
negate (Vector !x !y !z !w) = Vector (-x) (-y) (-z) (-w)
vectorScalarMul :: Vector -> Double -> Vector
{-# SPECIALIZE INLINE vectorScalarMul :: Vector -> Double -> Vector #-}
(Vector !x !y !z !w) `vectorScalarMul` k = Vector (x * k) (y * k) (z * k) (w * k)
infixl 7 <*>
infixl 7 </>
--infixl 6 <+>
--infixl 6 <->
(</>) :: Vector -> Double -> Vector
{-# SPECIALIZE INLINE (</>) :: Vector -> Double -> Vector #-}
a </> b = a `vectorScalarMul` (1 / b)
(<*>) :: Vector -> Double -> Vector
{-# SPECIALIZE INLINE (<*>) :: Vector -> Double -> Vector #-}
a <*> b = a `vectorScalarMul` b
dot3 :: Vector -> Vector -> Double
{-# SPECIALIZE INLINE dot3 :: Vector -> Vector -> Double #-}
(Vector !(D# x) !(D# y) !(D# z) _) `dot3` (Vector !(D# x') !(D# y') !(D# z') _) = D# $ (x *## x') +## (y *## y') +## (z *## z')
dot4 :: Vector -> Vector -> Double
{-# SPECIALIZE INLINE dot4 :: Vector -> Vector -> Double #-}
(Vector !(D# x) !(D# y) !(D# z) !(D# w)) `dot4` (Vector !(D# x') !(D# y') !(D# z') !(D# w')) = D# $ (x *## x') +## (y *## y') +## (z *## z') +## (w *## w')
sdot3 :: Vector -> Vector -> Double
{-# SPECIALIZE INLINE dot3 :: Vector -> Vector -> Double #-}
(Vector !(D# x) !(D# y) !(D# z) _) `sdot3` (Vector !(D# x') !(D# y') !(D# z') _) = D# $ saturate## ((x *## x') +## (y *## y') +## (z *## z'))
sdot4 :: Vector -> Vector -> Double
{-# SPECIALIZE INLINE sdot4 :: Vector -> Vector -> Double #-}
(Vector !(D# x) !(D# y) !(D# z) !(D# w)) `sdot4` (Vector !(D# x') !(D# y') !(D# z') !(D# w')) = D# $ saturate## ((x *## x') +## (y *## y') +## (z *## z') +## (w *## w'))
cross :: Direction -> Direction -> Direction
{-# SPECIALIZE INLINE cross :: Vector -> Vector -> Vector #-}
(Vector !(D# x1) !(D# y1) !(D# z1) _) `cross` (Vector !(D# x2) !(D# y2) !(D# z2) _) = Vector x y z 0
where
!x = D# ((y1 *## z2) -## (y2 *## z1))
!y = D# ((z1 *## x2) -## (z2 *## x1))
!z = D# ((x1 *## y2) -## (x2 *## y1))
magnitude :: Vector -> Double
{-# SPECIALIZE INLINE magnitude :: Vector -> Double #-}
magnitude !vec = sqrt (magnitudeSq vec)
magnitudeSq :: Vector -> Double
{-# SPECIALIZE INLINE magnitudeSq :: Vector -> Double #-}
magnitudeSq (Vector !(D# x#) !(D# y#) !(D# z#) _) = D# ((x# *## x#) +## (y# *## y#) +## (z# *## z#))
normalise :: Direction -> Direction
{-# SPECIALIZE INLINE normalise :: Direction -> Direction #-}
normalise !a = setWTo0 (a `vectorScalarMul` (1 / magnitude a))
distance :: Position -> Position -> Double
{-# SPECIALIZE INLINE distance :: Position -> Position -> Double #-}
distance !a !b = magnitude (a - b)
distanceSq :: Position -> Position -> Double
{-# SPECIALIZE INLINE distanceSq :: Position -> Position -> Double #-}
distanceSq !a !b = magnitudeSq (a - b)
reflect :: Direction -> Direction -> Direction
{-# SPECIALIZE INLINE reflect :: Direction -> Direction -> Direction #-}
reflect !incoming !normal = restoreOriginalW incoming $ (normal `vectorScalarMul` (2 * (normal `dot3` incoming))) - incoming
refract :: Direction -> Direction -> Double -> Direction
{-# SPECIALIZE INLINE refract :: Vector -> Vector -> Double -> Vector #-}
refract !incoming !normal !eta
| cosTheta1 >## 0.0## = setWTo0 $ (l `vectorScalarMul` eta) + (normal `vectorScalarMul` D# (eta# *## cosTheta1 -## cosTheta2))
| otherwise = setWTo0 $ (l `vectorScalarMul` eta) + (normal `vectorScalarMul` D# (eta# *## cosTheta1 +## cosTheta2))
where !(D# cosTheta1) = normal `dot3` incoming
!cosTheta2 = sqrtDouble# (1.0## -## eta# **## 2.0## *## (1.0## -## cosTheta1 **## 2.0##))
!l = Vector.negate incoming
!(D# eta#) = eta
largestAxis :: Vector -> Int
largestAxis (Vector !x !y !z _)
| abs x >= abs y && abs x >= abs z = 0
| abs y >= abs x && abs y >= abs z = 1
| abs z >= abs x && abs z >= abs y = 2
| otherwise = error "largestAxis: Undefined case"
nthLargestAxis :: Vector -> Int -> Int
nthLargestAxis (Vector !x !y !z _) order
| order < 3 = snd (sort [(abs x, 0), (abs y, 1), (abs z, 2)] !! order)
| otherwise = error "nthLargestAXis: Undefined case"
min :: Vector -> Vector -> Vector
{-# SPECIALIZE INLINE Vector.min :: Vector -> Vector -> Vector #-}
min (Vector !x1 !y1 !z1 !w1) (Vector !x2 !y2 !z2 !w2) = Vector x y z w
where
!x = Prelude.min x1 x2
!y = Prelude.min y1 y2
!z = Prelude.min z1 z2
!w = Prelude.min w1 w2
max :: Vector -> Vector -> Vector
{-# SPECIALIZE INLINE Vector.max :: Vector -> Vector -> Vector #-}
max (Vector !x1 !y1 !z1 !w1) (Vector !x2 !y2 !z2 !w2) = Vector x y z w
where
!x = Prelude.max x1 x2
!y = Prelude.max y1 y2
!z = Prelude.max z1 z2
!w = Prelude.max w1 w2
directionToSpherical :: Direction -> (Double, Double)
directionToSpherical (Vector !x !y !z _) = (theta, phi)
where
theta = acos z / pi
phi = (atan2 y x + pi) / (2.0 * pi)
sphericalToDirection :: Double -> Double -> Direction
sphericalToDirection (D# !theta) (D# !phi) = Vector (D# $ sinDouble# theta *## cosDouble# phi) (D# $ sinDouble# theta *## sinDouble# phi) (D# $ cosDouble# theta) 1
component :: Vector -> Int -> Double
{-# SPECIALIZE INLINE component :: Vector -> Int -> Double #-}
component (Vector !x _ _ _) 0 = x
component (Vector _ !y _ _) 1 = y
component (Vector _ _ !z _) 2 = z
component (Vector _ _ _ !w) 3 = w
component _ _ = error "Invalid component index"
transformDir :: Direction -> TangentSpace -> Direction
{-# SPECIALIZE INLINE transformDir :: Direction -> TangentSpace -> Direction #-}
transformDir (Vector !x !y !z _) !(tangent, binormal, normal) = setWTo0 ((tangent <*> x) + (binormal <*> y) + (normal <*> z))