{-# LANGUAGE CPP #-}

-----------------------------------------------------------------------------
-- |
-- 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

#if __GLASGOW_HASKELL__ < 710
import           Control.Applicative
#endif
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.Epsilon
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 . el

-- | 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. If the matrix is not invertible, 'Nothing' is
--   returned.
fromMat22 :: (Epsilon n, Floating n) => M22 n -> V2 n -> Maybe (T2 n)
fromMat22 m v = flip (fromMatWithInv m) v <$> inv22 m

-- | Make a 3D transformation from a 3x3 transform matrix and a
--   translation vector. If the matrix is not invertible, 'Nothing' is
--   returned.
fromMat33 :: (Epsilon n, Floating n) => M33 n -> V3 n -> Maybe (T3 n)
fromMat33 m v = flip (fromMatWithInv m) v <$> inv33 m

-- | 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.
mat22 :: (Epsilon n, Floating n) => Prism' (M22 n, V2 n) (T2 n)
mat22 = prism' (mkMat &&& transl) (uncurry fromMat22)

-- | Prism onto a 2D transformation from a 2x2 transform matrix and
--   translation vector.
mat33 :: (Epsilon n, Floating n) => Prism' (M33 n, V3 n) (T3 n)
mat33 = prism' (mkMat &&& transl) (uncurry fromMat33)