packages feed

geomancy-0.4.0.0: test/Support/Reference/Transform.hs

{-# LANGUAGE ImportQualifiedPost #-}

module Support.Reference.Transform
  ( (!.)
  , translate
  , translateV
  , scale3
  , scaleV
  , rotateX
  , rotateY
  , rotateZ
  , rotateQ
  , dirPos
  , node
  ) where

import Linear (M44, Quaternion(..), V3(..), V4(..))

import Support.Ball (Ball, isExactly)
import Support.Reference.Mat4 (colMajor)
import Support.Reference.Mat4 qualified as Mat4

(!.) :: RealFloat p => M44 (Ball p) -> V3 (Ball p) -> V3 (Ball p)
(!.) mat (V3 vx vy vz) = V3 (x / w) (y / w) (z / w)
  where
    V4 x y z w = Mat4.apply mat (V4 vx vy vz 1)

infixr 5 !.

translate :: Num a => a -> a -> a -> M44 a
translate x y z = colMajor
  1 0 0 x
  0 1 0 y
  0 0 1 z
  0 0 0 1

translateV :: Num a => V3 a -> M44 a
translateV (V3 x y z) = translate x y z

scale3 :: Num a => a -> a -> a -> M44 a
scale3 x y z = colMajor
  x 0 0 0
  0 y 0 0
  0 0 z 0
  0 0 0 1

scaleV :: Num a => V3 a -> M44 a
scaleV (V3 x y z) = scale3 x y z

rotateX :: Floating a => a -> M44 a
rotateX rads =
  colMajor
    1 0   0  0
    0 c (-s) 0
    0 s   c  0
    0 0   0  1
  where
    c = cos rads
    s = sin rads

rotateY :: Floating a => a -> M44 a
rotateY rads =
  colMajor
    c  0 s 0
    0  1 0 0
  (-s) 0 c 0
    0  0 0 1
  where
    c = cos rads
    s = sin rads

rotateZ :: Floating a => a -> M44 a
rotateZ rads =
  colMajor
    c (-s) 0 0
    s   c  0 0
    0   0  1 0
    0   0  0 1
  where
    c = cos rads
    s = sin rads

rotateQ :: Num a => Quaternion a -> M44 a
rotateQ dir = dirPos dir 0

dirPos :: Num a => Quaternion a -> V3 a -> M44 a
dirPos (Quaternion w (V3 x y z)) (V3 tx ty tz) =
  colMajor
    (1 - 2 * (y2 + z2)) (    2 * (xy - zw)) (    2 * (xz + yw)) tx
    (    2 * (xy + zw)) (1 - 2 * (x2 + z2)) (    2 * (yz - xw)) ty
    (    2 * (xz - yw)) (    2 * (yz + xw)) (1 - 2 * (x2 + y2)) tz
    0                   0                   0                    1
  where
    x2 = x * x
    y2 = y * y
    z2 = z * z
    xy = x * y
    xz = x * z
    xw = x * w
    yz = y * z
    yw = y * w
    zw = z * w

node :: RealFloat p => V3 (Ball p) -> Quaternion (Ball p) -> V3 (Ball p) -> M44 (Ball p)
node t r s
  | all (isExactly 1) s = dirPos r t
  | otherwise = dirPos r t `Mat4.compose` scaleV s