content: {-# 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 t = distribute . tabulate $ apply t . unit . (\x -> el x)
  
  -- | Build a 3D transformation matrix in homogeneous coordinates from
  --   a 'Transformation V3'.
  mkMatHomo :: Num n => Transformation V3 n -> M44 n
  mkMatHomo t = mkTransformationMat (mkMat t) (transl 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 m v = fromMatWithInv m (inv22 m) 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 m v = fromMatWithInv m (inv33 m) 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 m m_ v =
    Transformation ((m !*)            <-> (m_ !*))
                   ((distribute m !*) <-> (distribute m_ !*))
                   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 (uncurry fromMat22) (mkMat &&& 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 (uncurry fromMat33) (mkMat &&& transl)
  