packages feed

geomancy-0.4.0.0: src/Geomancy/DMat4.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE UnliftedFFITypes #-}
{-# LANGUAGE ViewPatterns #-}

{-# OPTIONS_GHC -Wno-orphans #-}

module Geomancy.DMat4
  ( DMat4
    -- * 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

    -- * Precision conversion
  , fromMat4
  , toMat4
  ) where

import Prelude hiding (zipWith)
import GHC.Exts hiding (VecCount(..), toList)
import GHC.Float (double2Float, float2Double)
import Geomancy.DMat4.Internal

import System.IO.Unsafe (unsafePerformIO)
import Text.Printf (printf)

import qualified Data.Foldable as Foldable

import Geomancy.DVec4 (DVec4(..), newDVec4)
import Geomancy.Mat4.Internal (Mat4)
import qualified Geomancy.Mat4.Internal as Mat4

{-# INLINE fromMat4 #-}
fromMat4 :: Coercible a Mat4 => a -> DMat4
fromMat4 m = Mat4.withMemory (coerce m)
  \ e0 e1 e2 e3
    e4 e5 e6 e7
    e8 e9 eA eB
    eC eD eE eF ->
  fromMemory
    (float2Double e0) (float2Double e1) (float2Double e2) (float2Double e3)
    (float2Double e4) (float2Double e5) (float2Double e6) (float2Double e7)
    (float2Double e8) (float2Double e9) (float2Double eA) (float2Double eB)
    (float2Double eC) (float2Double eD) (float2Double eE) (float2Double eF)

{-# INLINE toMat4 #-}
toMat4 :: DMat4 -> Mat4
toMat4 m = withMemory m
  \ e0 e1 e2 e3
    e4 e5 e6 e7
    e8 e9 eA eB
    eC eD eE eF ->
  Mat4.fromMemory
    (double2Float e0) (double2Float e1) (double2Float e2) (double2Float e3)
    (double2Float e4) (double2Float e5) (double2Float e6) (double2Float e7)
    (double2Float e8) (double2Float e9) (double2Float eA) (double2Float eB)
    (double2Float eC) (double2Float eD) (double2Float eE) (double2Float eF)

toListColMajor :: Coercible a DMat4 => a -> [Double]
toListColMajor = toListTrans . coerce

toListColMajor2d :: Coercible a DMat4 => a -> [[Double]]
toListColMajor2d = toList2dTrans . coerce

rowMajor
  :: Coercible DMat4 a
  => Double -> Double -> Double -> Double
  -> Double -> Double -> Double -> Double
  -> Double -> Double -> Double -> Double
  -> Double -> Double -> Double -> Double
  -> a
rowMajor
  e0 e1 e2 e3
  e4 e5 e6 e7
  e8 e9 eA eB
  eC eD eE eF =
    coerce $ fromMemory
      e0 e1 e2 e3
      e4 e5 e6 e7
      e8 e9 eA eB
      eC eD eE eF

fromRowMajor2d
  :: forall t a
  .  ( Foldable t
     , Coercible DMat4 a
     )
  => t (t Double)
  -> 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

{-# INLINE withRowMajor #-}
withRowMajor
  :: Coercible a DMat4
  => a
  ->
    ( Double -> Double -> Double -> Double ->
      Double -> Double -> Double -> Double ->
      Double -> Double -> Double -> Double ->
      Double -> Double -> Double -> Double ->
      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
    e0 e1 e2 e3
    e4 e5 e6 e7
    e8 e9 eA eB
    eC eD eE eF

{-# INLINE colMajor #-}
colMajor
  :: forall a . Coercible DMat4 a
  => Double -> Double -> Double -> Double
  -> Double -> Double -> Double -> Double
  -> Double -> Double -> Double -> Double
  -> Double -> Double -> Double -> Double
  -> 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

{-# INLINE withColMajor #-}
withColMajor
  :: Coercible a DMat4
  => a
  ->
    ( Double -> Double -> Double -> Double ->
      Double -> Double -> Double -> Double ->
      Double -> Double -> Double -> Double ->
      Double -> Double -> Double -> Double ->
      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
    e0 e1 e2 e3
    e4 e5 e6 e7
    e8 e9 eA eB
    eC eD eE eF

toListRowMajor :: Coercible a DMat4 => a -> [Double]
toListRowMajor = toListMemory . coerce

toListRowMajor2d :: Coercible a DMat4 => a -> [[Double]]
toListRowMajor2d = toList2dMemory . coerce

det :: Coercible DMat4 a => a -> Double
det m =
  withRowMajor 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

inverse :: Coercible DMat4 a => a -> a
inverse m =
  withRowMajor 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 "DMat4xDMat4_SIMD" m4m4simd :: ByteArray# -> ByteArray# -> ByteArray# -> IO ()

{-# INLINE matrixProduct #-}
matrixProduct :: DMat4 -> DMat4 -> DMat4
matrixProduct (DMat4 l) (DMat4 r) = unsafePerformIO do
  result@(DMat4 m) <- newDMat4
  m4m4simd l r m
  pure result

foreign import ccall unsafe "DMat4xDVec4_SIMD" m4v4simd :: ByteArray# -> ByteArray# -> ByteArray# -> IO ()

(!*) :: Coercible a DMat4 => a -> DVec4 -> DVec4
(!*) (coerce -> DMat4 m) (DVec4 v) = unsafePerformIO do
  result@(DVec4 o) <- newDVec4
  !() <- m4v4simd m v o
  pure result

infixr 5 !*

instance Semigroup DMat4 where
  {-# INLINE (<>) #-}
  (<>) = matrixProduct

instance Monoid DMat4 where
  {-# INLINE mempty #-}
  mempty = identity

instance Show DMat4 where
  show = showColumns

showColumns :: DMat4 -> 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 :: DMat4 -> 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
    ]