```{- |
3-dimensional linear transformations.
-}

module Data.Vector.Transform.T3 where

import Data.Monoid

import Data.Vector.Class
import Data.Vector.V3

{- |
The type of 3D linear transformations.

Note the @Monoid@ instance, which gives you access to the identity transform (@mempty@) and the ability to combine a series of transforms into a single transform (@mappend@).
-}
data Transform3 =
Transform3
{
t3_XX, t3_YX, t3_ZX, t3_1X,
t3_XY, t3_YY, t3_ZY, t3_1Y,
t3_XZ, t3_YZ, t3_ZZ, t3_1Z :: {-# UNPACK #-} !Scalar
}
deriving (Eq, Show)

instance Monoid Transform3 where
mempty = Transform3  1 0 0 0  0 1 0 0  0 0 1 0
mappend a b =
Transform3
{
t3_XX = t3_XX a * t3_XX b  +  t3_XY a * t3_YX b  +  t3_XZ a * t3_ZX b,
t3_YX = t3_YX a * t3_XX b  +  t3_YY a * t3_YX b  +  t3_YZ a * t3_ZX b,
t3_ZX = t3_ZX a * t3_XX b  +  t3_ZY a * t3_YX b  +  t3_ZZ a * t3_ZX b,
t3_1X = t3_1X a * t3_XX b  +  t3_1Y a * t3_YX b  +  t3_1Z a * t3_ZX b  +  t3_1X b,

t3_XY = t3_XX a * t3_XY b  +  t3_XY a * t3_YY b  +  t3_XZ a * t3_ZY b,
t3_YY = t3_YX a * t3_XY b  +  t3_YY a * t3_YY b  +  t3_YZ a * t3_ZY b,
t3_ZY = t3_ZX a * t3_XY b  +  t3_ZY a * t3_YY b  +  t3_ZZ a * t3_ZY b,
t3_1Y = t3_1X a * t3_XY b  +  t3_1Y a * t3_YY b  +  t3_1Z a * t3_ZY b  +  t3_1Y b,

t3_XZ = t3_XX a * t3_XZ b  +  t3_XY a * t3_YZ b  +  t3_XZ a * t3_ZZ b,
t3_YZ = t3_YX a * t3_XZ b  +  t3_YY a * t3_YZ b  +  t3_YZ a * t3_ZZ b,
t3_ZZ = t3_ZX a * t3_XZ b  +  t3_ZY a * t3_YZ b  +  t3_ZZ a * t3_ZZ b,
t3_1Z = t3_1X a * t3_XZ b  +  t3_1Y a * t3_YZ b  +  t3_1Z a * t3_ZZ b  +  t3_1Z b
}

-- | Apply a 3D transformation to a 3D point, yielding a new 3D point.
transformP3 :: Transform3 -> Vector3 -> Vector3
transformP3 a (Vector3 x y z) =
Vector3
{
v3x = t3_XX a * x + t3_YX a * y + t3_ZX a * z + t3_1X a,
v3y = t3_XY a * x + t3_YY a * y + t3_ZY a * z + t3_1Y a,
v3z = t3_XZ a * x + t3_YZ a * y + t3_ZZ a * z + t3_1Z a
}
```