module Graphics.DrawingCombinators.Affine
    ( R, R2, Affine
    , compose, apply, identity, translate, rotate, scale, inverse
    , multGLmatrix
    )
where
import qualified Graphics.Rendering.OpenGL.GL as GL
import Data.Monoid
type R = GL.GLdouble
type R2 = (R,R)
data Affine = M !R !R !R
                !R !R !R
             
instance Monoid Affine where
    mempty = identity
    mappend = compose
compose :: Affine -> Affine -> Affine
M x11 x12 x13 x21 x22 x23 `compose` M y11 y12 y13 y21 y22 y23 =
    M (x11*y11+x12*y21) (x11*y12+x12*y22) (x11*y13+x12*y23+x13)
      (x21*y11+x22*y21) (x21*y12+x22*y22) (x21*y13+x22*y23+x23)
apply :: Affine -> R2 -> R2
apply (M x11 x12 x13 x21 x22 x23) (y1,y2) = 
    (x11*y1+x12*y2+x13, x21*y1+x22*y2+x23)
identity :: Affine
identity = M 1 0 0
             0 1 0
inverse :: Affine -> Affine
inverse (M x11 x12 x13 x21 x22 x23) = 
    M (s*x22)   (s*x12)  (s*x22*x13 + s*x12*x23)
      (s*x21)  (s*x11)   ( s*x21*x13  s*x11*x23)
    where
    s = 1 / (x11*x22  x12*x21)
translate :: R2 -> Affine
translate (x,y) = M 1 0 x
                    0 1 y
rotate :: R -> Affine
rotate t = M cost (sint) 0
             sint cost    0
    where
    cost = cos t
    sint = sin t
scale :: R -> R -> Affine
scale x y = M x 0 0
              0 y 0
multGLmatrix :: Affine -> IO ()
multGLmatrix (M x11 x12 x13 x21 x22 x23) = do
    m <- GL.newMatrix GL.ColumnMajor [ x11 , x21 , 0 , 0
                                     , x12 , x22 , 0 , 0
                                     , 0   , 0   , 1 , 0
                                     , x13 , x23 , 0 , 1 ]
    GL.multMatrix (m :: GL.GLmatrix R)