geomancy-0.3.0.0: src/Geomancy/Mat4.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE UnliftedFFITypes #-}
{-# LANGUAGE ViewPatterns #-}
{-# OPTIONS_GHC -Wno-orphans #-} -- XXX: until there are order-specific types
-- | General matrix storage and operations.
module Geomancy.Mat4
( Mat4
-- * Operations
, identity
, transpose
, inverse
, det
, pointwise
, zipWith
, matrixProduct
, scalarMultiply
, (!*)
-- * Construction
-- ** Col-major
, colMajor
, withColMajor
, toListColMajor
, toListColMajor2d
, showColumns
-- ** Row-major
, rowMajor
, withRowMajor
, toListRowMajor
, toListRowMajor2d
, fromRowMajor2d
, showRows
) where
import Prelude hiding (zipWith)
import GHC.Exts hiding (VecCount(..), toList)
import Geomancy.Mat4.Internal
import System.IO.Unsafe (unsafePerformIO)
import Text.Printf (printf)
import qualified Data.Foldable as Foldable
import Geomancy.Vec4 (Vec4(..), newVec4)
toListColMajor :: Coercible a Mat4 => a -> [Float]
toListColMajor = toListTrans . coerce
toListColMajor2d :: Coercible a Mat4 => a -> [[Float]]
toListColMajor2d = toList2dTrans . coerce
{- | Construct 'Mat4' from @row@ notation.
-}
rowMajor
:: Coercible Mat4 a
=> Float -> Float -> Float -> Float
-> Float -> Float -> Float -> Float
-> Float -> Float -> Float -> Float
-> Float -> Float -> Float -> Float
-> a
rowMajor
e0 e1 e2 e3
e4 e5 e6 e7
e8 e9 eA eB
eC eD eE eF =
-- just store
coerce $ fromMemory
e0 e1 e2 e3
e4 e5 e6 e7
e8 e9 eA eB
eC eD eE eF
{- |
Build a Mat4 from a list-of-lists kind of container
with row-major ordering of elements.
@
fromRowMajor2d (Linear.mkTransformation dir pos) :: Transform
@
-}
fromRowMajor2d
:: forall t a
. ( Foldable t
, Coercible Mat4 a
)
=> t (t Float)
-> Maybe a
fromRowMajor2d rows =
case Foldable.toList rows of
[r0, r1, r2, r3] ->
withRow r0 \e0 e1 e2 e3 ->
withRow r1 \e4 e5 e6 e7 ->
withRow r2 \e8 e9 eA eB ->
withRow r3 \eC eD eE eF ->
Just . coerce @a $ rowMajor
e0 e1 e2 e3
e4 e5 e6 e7
e8 e9 eA eB
eC eD eE eF
_ ->
Nothing
where
withRow row f =
case Foldable.toList row of
[c0, c1, c2, c3] ->
f c0 c1 c2 c3
_ ->
Nothing
{- | Reduce 'Mat4' with a function with @row@ notation for arguments.
-}
{-# INLINE withRowMajor #-}
withRowMajor
:: Coercible a Mat4
=> a
->
( Float -> Float -> Float -> Float ->
Float -> Float -> Float -> Float ->
Float -> Float -> Float -> Float ->
Float -> Float -> Float -> Float ->
r
)
-> r
withRowMajor m f = withMemory (coerce m)
\ e0 e1 e2 e3
e4 e5 e6 e7
e8 e9 eA eB
eC eD eE eF ->
f -- preserved element order
e0 e1 e2 e3
e4 e5 e6 e7
e8 e9 eA eB
eC eD eE eF
{- | Construct a 'Mat4' from @column@ notation.
-}
{-# INLINE colMajor #-}
colMajor
:: forall a . Coercible Mat4 a
=> Float -> Float -> Float -> Float
-> Float -> Float -> Float -> Float
-> Float -> Float -> Float -> Float
-> Float -> Float -> Float -> Float
-> a
colMajor
e0 e4 e8 eC
e1 e5 e9 eD
e2 e6 eA eE
e3 e7 eB eF =
coerce $ fromMemory
e0 e1 e2 e3
e4 e5 e6 e7
e8 e9 eA eB
eC eD eE eF
{- | Reduce 'Mat4' with a function with @column@ notation for arguments.
-}
{-# INLINE withColMajor #-}
withColMajor
:: Coercible a Mat4
=> a
->
( Float -> Float -> Float -> Float ->
Float -> Float -> Float -> Float ->
Float -> Float -> Float -> Float ->
Float -> Float -> Float -> Float ->
r
)
-> r
withColMajor m f = withMemory (coerce m)
\ e0 e4 e8 eC
e1 e5 e9 eD
e2 e6 eA eE
e3 e7 eB eF ->
f -- transposed element order
e0 e1 e2 e3
e4 e5 e6 e7
e8 e9 eA eB
eC eD eE eF
toListRowMajor :: Coercible a Mat4 => a -> [Float]
toListRowMajor = toListMemory . coerce
toListRowMajor2d :: Coercible a Mat4 => a -> [[Float]]
toListRowMajor2d = toList2dMemory . coerce
det :: forall a . (Coercible Mat4 a, Coercible Mat4 a) => a -> Float
det m =
withRowMajor (coerce @_ @a m)
\ m00 m01 m02 m03
m10 m11 m12 m13
m20 m21 m22 m23
m30 m31 m32 m33 ->
let
s0 = m00 * m11 - m10 * m01
s1 = m00 * m12 - m10 * m02
s2 = m00 * m13 - m10 * m03
s3 = m01 * m12 - m11 * m02
s4 = m01 * m13 - m11 * m03
s5 = m02 * m13 - m12 * m03
c5 = m22 * m33 - m32 * m23
c4 = m21 * m33 - m31 * m23
c3 = m21 * m32 - m31 * m22
c2 = m20 * m33 - m30 * m23
c1 = m20 * m32 - m30 * m22
c0 = m20 * m31 - m30 * m21
in
s0 * c5
- s1 * c4
+ s2 * c3
+ s3 * c2
- s4 * c1
+ s5 * c0
{- | Compute an inverse matrix, slowly.
-}
inverse :: forall a . (Coercible Mat4 a, Coercible Mat4 a) => a -> a
inverse m =
coerce @a $ withRowMajor (coerce @_ @a m)
\ m00 m01 m02 m03
m10 m11 m12 m13
m20 m21 m22 m23
m30 m31 m32 m33 ->
let
invDet = recip $
s0 * c5
- s1 * c4
+ s2 * c3
+ s3 * c2
- s4 * c1
+ s5 * c0
s0 = m00 * m11 - m10 * m01
s1 = m00 * m12 - m10 * m02
s2 = m00 * m13 - m10 * m03
s3 = m01 * m12 - m11 * m02
s4 = m01 * m13 - m11 * m03
s5 = m02 * m13 - m12 * m03
c5 = m22 * m33 - m32 * m23
c4 = m21 * m33 - m31 * m23
c3 = m21 * m32 - m31 * m22
c2 = m20 * m33 - m30 * m23
c1 = m20 * m32 - m30 * m22
c0 = m20 * m31 - m30 * m21
i00 = ( m11 * c5 - m12 * c4 + m13 * c3) * invDet
i01 = (-m01 * c5 + m02 * c4 - m03 * c3) * invDet
i02 = ( m31 * s5 - m32 * s4 + m33 * s3) * invDet
i03 = (-m21 * s5 + m22 * s4 - m23 * s3) * invDet
i10 = (-m10 * c5 + m12 * c2 - m13 * c1) * invDet
i11 = ( m00 * c5 - m02 * c2 + m03 * c1) * invDet
i12 = (-m30 * s5 + m32 * s2 - m33 * s1) * invDet
i13 = ( m20 * s5 - m22 * s2 + m23 * s1) * invDet
i20 = ( m10 * c4 - m11 * c2 + m13 * c0) * invDet
i21 = (-m00 * c4 + m01 * c2 - m03 * c0) * invDet
i22 = ( m30 * s4 - m31 * s2 + m33 * s0) * invDet
i23 = (-m20 * s4 + m21 * s2 - m23 * s0) * invDet
i30 = (-m10 * c3 + m11 * c1 - m12 * c0) * invDet
i31 = ( m00 * c3 - m01 * c1 + m02 * c0) * invDet
i32 = (-m30 * s3 + m31 * s1 - m32 * s0) * invDet
i33 = ( m20 * s3 - m21 * s1 + m22 * s0) * invDet
in
rowMajor
i00 i01 i02 i03
i10 i11 i12 i13
i20 i21 i22 i23
i30 i31 i32 i33
foreign import ccall unsafe "Mat4xMat4_SIMD" m4m4simd :: ByteArray# -> ByteArray# -> ByteArray# -> IO ()
{-# INLINE matrixProduct #-}
matrixProduct :: Mat4 -> Mat4 -> Mat4
matrixProduct (Mat4 l) (Mat4 r) = unsafePerformIO do
result@(Mat4 m) <- newMat4
m4m4simd l r m
pure result
foreign import ccall unsafe "Mat4xVec4_SIMD" m4v4simd :: ByteArray# -> ByteArray# -> ByteArray# -> IO ()
{- | Matrix - column vector multiplication (post)
@
vOut = p <> v <> m !* vIn
@
-}
(!*) :: Coercible a Mat4 => a -> Vec4 -> Vec4
(!*) (coerce -> Mat4 m) (Vec4 v) = unsafePerformIO do
result@(Vec4 o) <- newVec4
!() <- m4v4simd m v o
pure result
infixr 5 !*
instance Semigroup Mat4 where
{-# INLINE (<>) #-}
(<>) = matrixProduct
instance Monoid Mat4 where
{-# INLINE mempty #-}
mempty = identity
instance Show Mat4 where
show = showColumns
showColumns :: Mat4 -> String
showColumns cm = withColMajor cm
\ e0 e4 e8 eC
e1 e5 e9 eD
e2 e6 eA eE
e3 e7 eB eF ->
unlines
[ printf "/ %.4f %.4f %.4f %.4f \\" e0 e4 e8 eC
, printf "| %.4f %.4f %.4f %.4f |" e1 e5 e9 eD
, printf "| %.4f %.4f %.4f %.4f |" e2 e6 eA eE
, printf "\\ %.4f %.4f %.4f %.4f /" e3 e7 eB eF
]
showRows :: Mat4 -> String
showRows rm = withRowMajor rm
\ e0 e1 e2 e3
e4 e5 e6 e7
e8 e9 eA eB
eC eD eE eF ->
unlines
[ printf "[ %.4f %.4f %.4f %.4f |" e0 e1 e2 e3
, printf "| %.4f %.4f %.4f %.4f |" e4 e5 e6 e7
, printf "| %.4f %.4f %.4f %.4f |" e8 e9 eA eB
, printf "| %.4f %.4f %.4f %.4f ]" eC eD eE eF
]