{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE MonoLocalBinds  #-}
{-# LANGUAGE TypeFamilies    #-}

-----------------------------------------------------------------------------
-- |
-- Module      :  Diagrams.Transform.Matrix
-- Copyright   :  (c) 2014 diagrams team (see LICENSE)
-- License     :  BSD-style (see LICENSE)
-- Maintainer  :  diagrams-discuss@googlegroups.com
--
-- Functions for converting between 'Transformation's and matricies.
--
-----------------------------------------------------------------------------

module Diagrams.Transform.Matrix where

import           Control.Arrow           ((&&&))
import           Control.Lens
import           Data.Distributive
import qualified Data.Foldable           as F
import           Data.Functor.Rep

import           Diagrams.Core.Transform as D
import           Diagrams.ThreeD.Types
import           Diagrams.TwoD.Types

import           Linear.Matrix
import           Linear.Vector

-- | Build a matrix from a 'Transformation', ignoring the translation.
mkMat :: (HasBasis v, Num n) => Transformation v n -> v (v n)
mkMat :: Transformation v n -> v (v n)
mkMat Transformation v n
t = v (v n) -> v (v n)
forall (g :: * -> *) (f :: * -> *) a.
(Distributive g, Functor f) =>
f (g a) -> g (f a)
distribute (v (v n) -> v (v n))
-> ((E v -> v n) -> v (v n)) -> (E v -> v n) -> v (v n)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (E v -> v n) -> v (v n)
forall (f :: * -> *) a. Representable f => (Rep f -> a) -> f a
tabulate ((E v -> v n) -> v (v n)) -> (E v -> v n) -> v (v n)
forall a b. (a -> b) -> a -> b
$ Transformation v n -> v n -> v n
forall (v :: * -> *) n. Transformation v n -> v n -> v n
apply Transformation v n
t (v n -> v n) -> (E v -> v n) -> E v -> v n
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ASetter' (v n) n -> v n
forall (t :: * -> *) a.
(Additive t, Num a) =>
ASetter' (t a) a -> t a
unit (ASetter' (v n) n -> v n)
-> (E v -> ASetter' (v n) n) -> E v -> v n
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (\E v
x -> E v
-> forall x (f :: * -> *).
   Functor f =>
   (x -> f x) -> v x -> f (v x)
forall (t :: * -> *).
E t
-> forall x (f :: * -> *).
   Functor f =>
   (x -> f x) -> t x -> f (t x)
el E v
x)

-- | Build a 3D transformation matrix in homogeneous coordinates from
--   a 'Transformation V3'.
mkMatHomo :: Num n => Transformation V3 n -> M44 n
mkMatHomo :: Transformation V3 n -> M44 n
mkMatHomo Transformation V3 n
t = M33 n -> V3 n -> M44 n
forall a. Num a => M33 a -> V3 a -> M44 a
mkTransformationMat (Transformation V3 n -> M33 n
forall (v :: * -> *) n.
(HasBasis v, Num n) =>
Transformation v n -> v (v n)
mkMat Transformation V3 n
t) (Transformation V3 n -> V3 n
forall (v :: * -> *) n. Transformation v n -> v n
transl Transformation V3 n
t)

-- | Make a 2D transformation from a 2x2 transform matrix and a
--   translation vector. Does not check if the matrix is not invertible
--   (in which case the 'T2' will be invalid).
fromMat22 :: Floating n => M22 n -> V2 n -> T2 n
fromMat22 :: M22 n -> V2 n -> T2 n
fromMat22 M22 n
m V2 n
v = M22 n -> M22 n -> V2 n -> T2 n
forall (v :: * -> *) n.
(Additive v, Distributive v, Foldable v, Num n) =>
v (v n) -> v (v n) -> v n -> Transformation v n
fromMatWithInv M22 n
m (M22 n -> M22 n
forall a. Fractional a => M22 a -> M22 a
inv22 M22 n
m) V2 n
v

-- | Make a 3D transformation from a 3x3 transform matrix and a
--   translation vector. Does not check if the matrix is not invertible
--   (in which case the 'T3' will be invalid).
fromMat33 :: Floating n => M33 n -> V3 n -> T3 n
fromMat33 :: M33 n -> V3 n -> T3 n
fromMat33 M33 n
m V3 n
v = M33 n -> M33 n -> V3 n -> T3 n
forall (v :: * -> *) n.
(Additive v, Distributive v, Foldable v, Num n) =>
v (v n) -> v (v n) -> v n -> Transformation v n
fromMatWithInv M33 n
m (M33 n -> M33 n
forall a. Fractional a => M33 a -> M33 a
inv33 M33 n
m) V3 n
v

-- | Build a transform with a maxtrix along with its inverse.
fromMatWithInv :: (Additive v, Distributive v, F.Foldable v, Num n)
  => v (v n) -- ^ matrix
  -> v (v n) -- ^ inverse
  -> v n     -- ^ translation
  -> Transformation v n
fromMatWithInv :: v (v n) -> v (v n) -> v n -> Transformation v n
fromMatWithInv v (v n)
m v (v n)
m_ v n
v =
  (v n :-: v n) -> (v n :-: v n) -> v n -> Transformation v n
forall (v :: * -> *) n.
(v n :-: v n) -> (v n :-: v n) -> v n -> Transformation v n
Transformation ((v (v n)
m v (v n) -> v n -> v n
forall (m :: * -> *) (r :: * -> *) a.
(Functor m, Foldable r, Additive r, Num a) =>
m (r a) -> r a -> m a
!*)            (v n -> v n) -> (v n -> v n) -> v n :-: v n
forall u v. (u -> v) -> (v -> u) -> u :-: v
<-> (v (v n)
m_ v (v n) -> v n -> v n
forall (m :: * -> *) (r :: * -> *) a.
(Functor m, Foldable r, Additive r, Num a) =>
m (r a) -> r a -> m a
!*))
                 ((v (v n) -> v (v n)
forall (g :: * -> *) (f :: * -> *) a.
(Distributive g, Functor f) =>
f (g a) -> g (f a)
distribute v (v n)
m v (v n) -> v n -> v n
forall (m :: * -> *) (r :: * -> *) a.
(Functor m, Foldable r, Additive r, Num a) =>
m (r a) -> r a -> m a
!*) (v n -> v n) -> (v n -> v n) -> v n :-: v n
forall u v. (u -> v) -> (v -> u) -> u :-: v
<-> (v (v n) -> v (v n)
forall (g :: * -> *) (f :: * -> *) a.
(Distributive g, Functor f) =>
f (g a) -> g (f a)
distribute v (v n)
m_ v (v n) -> v n -> v n
forall (m :: * -> *) (r :: * -> *) a.
(Functor m, Foldable r, Additive r, Num a) =>
m (r a) -> r a -> m a
!*))
                 v n
v

-- | Prism onto a 2D transformation from a 2x2 transform matrix and
--   translation vector. Does not check if the matrix is invertible (in
--   which case the 'T2' will be invalid).
mat22 :: Floating n => Iso' (M22 n, V2 n) (T2 n)
mat22 :: Iso' (M22 n, V2 n) (T2 n)
mat22 = ((M22 n, V2 n) -> T2 n)
-> (T2 n -> (M22 n, V2 n)) -> Iso' (M22 n, V2 n) (T2 n)
forall s a b t. (s -> a) -> (b -> t) -> Iso s t a b
iso ((M22 n -> V2 n -> T2 n) -> (M22 n, V2 n) -> T2 n
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry M22 n -> V2 n -> T2 n
forall n. Floating n => M22 n -> V2 n -> T2 n
fromMat22) (T2 n -> M22 n
forall (v :: * -> *) n.
(HasBasis v, Num n) =>
Transformation v n -> v (v n)
mkMat (T2 n -> M22 n) -> (T2 n -> V2 n) -> T2 n -> (M22 n, V2 n)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& T2 n -> V2 n
forall (v :: * -> *) n. Transformation v n -> v n
transl)

-- | Prism onto a 3D transformation from a 3x3 transform matrix and
--   translation vector. Does not check if the matrix is invertible
--   (in which case the 'T3' will be invalid).
mat33 :: Floating n => Iso' (M33 n, V3 n) (T3 n)
mat33 :: Iso' (M33 n, V3 n) (T3 n)
mat33 = ((M33 n, V3 n) -> T3 n)
-> (T3 n -> (M33 n, V3 n)) -> Iso' (M33 n, V3 n) (T3 n)
forall s a b t. (s -> a) -> (b -> t) -> Iso s t a b
iso ((M33 n -> V3 n -> T3 n) -> (M33 n, V3 n) -> T3 n
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry M33 n -> V3 n -> T3 n
forall n. Floating n => M33 n -> V3 n -> T3 n
fromMat33) (T3 n -> M33 n
forall (v :: * -> *) n.
(HasBasis v, Num n) =>
Transformation v n -> v (v n)
mkMat (T3 n -> M33 n) -> (T3 n -> V3 n) -> T3 n -> (M33 n, V3 n)
forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& T3 n -> V3 n
forall (v :: * -> *) n. Transformation v n -> v n
transl)