    --------------------------------------------------------------------------------
    -- |
    -- Module      :  Graphics.Rendering.OpenGL.GLU.Matrix
    -- Copyright   :  (c) Sven Panne 2002-2019
    -- License     :  BSD3
    --
    -- Maintainer  :  Sven Panne <svenpanne@gmail.com>
    -- Stability   :  stable
    -- Portability :  portable
    --
    -- This module corresponds to chapter 4 (Matrix Manipulation) of the GLU specs.
    --
    --------------------------------------------------------------------------------

    module Graphics.Rendering.OpenGL.GLU.Matrix (
       ortho2D, perspective, lookAt, pickMatrix,
       project, unProject, unProject4
    ) where

    import Foreign.Marshal.Alloc
    import Foreign.Marshal.Array
    import Foreign.Ptr
    import Foreign.Storable
    import Graphics.GLU
    import Graphics.Rendering.OpenGL.GL.CoordTrans
    import Graphics.Rendering.OpenGL.GL.GLboolean
    import Graphics.Rendering.OpenGL.GL.Tensor
    import Graphics.Rendering.OpenGL.GLU.ErrorsInternal
    import Graphics.GL

    --------------------------------------------------------------------------------
    -- matrix setup

    ortho2D :: GLdouble -> GLdouble -> GLdouble -> GLdouble -> IO ()
    ortho2D :: GLdouble -> GLdouble -> GLdouble -> GLdouble -> IO ()
    ortho2D = GLdouble -> GLdouble -> GLdouble -> GLdouble -> IO ()
    forall (m :: * -> *).
    MonadIO m =>
    GLdouble -> GLdouble -> GLdouble -> GLdouble -> m ()
    gluOrtho2D

    perspective :: GLdouble -> GLdouble -> GLdouble -> GLdouble -> IO ()
    perspective :: GLdouble -> GLdouble -> GLdouble -> GLdouble -> IO ()
    perspective = GLdouble -> GLdouble -> GLdouble -> GLdouble -> IO ()
    forall (m :: * -> *).
    MonadIO m =>
    GLdouble -> GLdouble -> GLdouble -> GLdouble -> m ()
    gluPerspective

    lookAt :: Vertex3 GLdouble -> Vertex3 GLdouble -> Vector3 GLdouble -> IO ()
    lookAt :: Vertex3 GLdouble -> Vertex3 GLdouble -> Vector3 GLdouble -> IO ()
    lookAt (Vertex3 GLdouble
    eyeX    GLdouble
    eyeY    GLdouble
    eyeZ)
           (Vertex3 GLdouble
    centerX GLdouble
    centerY GLdouble
    centerZ)
           (Vector3 GLdouble
    upX     GLdouble
    upY     GLdouble
    upZ) =
       GLdouble
    -> GLdouble
    -> GLdouble
    -> GLdouble
    -> GLdouble
    -> GLdouble
    -> GLdouble
    -> GLdouble
    -> GLdouble
    -> IO ()
    forall (m :: * -> *).
    MonadIO m =>
    GLdouble
    -> GLdouble
    -> GLdouble
    -> GLdouble
    -> GLdouble
    -> GLdouble
    -> GLdouble
    -> GLdouble
    -> GLdouble
    -> m ()
    gluLookAt GLdouble
    eyeX GLdouble
    eyeY GLdouble
    eyeZ GLdouble
    centerX GLdouble
    centerY GLdouble
    centerZ GLdouble
    upX GLdouble
    upY GLdouble
    upZ

    pickMatrix ::
       (GLdouble, GLdouble) -> (GLdouble, GLdouble) -> (Position, Size) -> IO ()
    pickMatrix :: (GLdouble, GLdouble)
    -> (GLdouble, GLdouble) -> (Position, Size) -> IO ()
    pickMatrix (GLdouble
    x, GLdouble
    y) (GLdouble
    w, GLdouble
    h) (Position, Size)
    viewPort =
       (Position, Size) -> (Ptr GLint -> IO ()) -> IO ()
    forall a. (Position, Size) -> (Ptr GLint -> IO a) -> IO a
    withViewport (Position, Size)
    viewPort ((Ptr GLint -> IO ()) -> IO ()) -> (Ptr GLint -> IO ()) -> IO ()
    forall a b. (a -> b) -> a -> b
    $ GLdouble -> GLdouble -> GLdouble -> GLdouble -> Ptr GLint -> IO ()
    forall (m :: * -> *).
    MonadIO m =>
    GLdouble -> GLdouble -> GLdouble -> GLdouble -> Ptr GLint -> m ()
    gluPickMatrix GLdouble
    x GLdouble
    y GLdouble
    w GLdouble
    h

    --------------------------------------------------------------------------------
    -- coordinate projection

    project ::
          Matrix m
       => Vertex3 GLdouble -> m GLdouble -> m GLdouble -> (Position, Size)
       -> IO (Vertex3 GLdouble)
    project :: forall (m :: * -> *).
    Matrix m =>
    Vertex3 GLdouble
    -> m GLdouble
    -> m GLdouble
    -> (Position, Size)
    -> IO (Vertex3 GLdouble)
    project (Vertex3 GLdouble
    objX GLdouble
    objY GLdouble
    objZ) m GLdouble
    model m GLdouble
    proj (Position, Size)
    viewPort =
       m GLdouble
    -> (Ptr GLdouble -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble)
    forall (m :: * -> *) c b.
    (Matrix m, MatrixComponent c) =>
    m c -> (Ptr c -> IO b) -> IO b
    withColumnMajor m GLdouble
    model ((Ptr GLdouble -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble))
    -> (Ptr GLdouble -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble)
    forall a b. (a -> b) -> a -> b
    $ \Ptr GLdouble
    modelBuf ->
       m GLdouble
    -> (Ptr GLdouble -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble)
    forall (m :: * -> *) c b.
    (Matrix m, MatrixComponent c) =>
    m c -> (Ptr c -> IO b) -> IO b
    withColumnMajor m GLdouble
    proj ((Ptr GLdouble -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble))
    -> (Ptr GLdouble -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble)
    forall a b. (a -> b) -> a -> b
    $ \Ptr GLdouble
    projBuf ->
       (Position, Size)
    -> (Ptr GLint -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble)
    forall a. (Position, Size) -> (Ptr GLint -> IO a) -> IO a
    withViewport (Position, Size)
    viewPort ((Ptr GLint -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble))
    -> (Ptr GLint -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble)
    forall a b. (a -> b) -> a -> b
    $ \Ptr GLint
    viewBuf ->
       (Ptr GLdouble -> Ptr GLdouble -> Ptr GLdouble -> IO GLint)
    -> IO (Vertex3 GLdouble)
    getVertex3 ((Ptr GLdouble -> Ptr GLdouble -> Ptr GLdouble -> IO GLint)
     -> IO (Vertex3 GLdouble))
    -> (Ptr GLdouble -> Ptr GLdouble -> Ptr GLdouble -> IO GLint)
    -> IO (Vertex3 GLdouble)
    forall a b. (a -> b) -> a -> b
    $ GLdouble
    -> GLdouble
    -> GLdouble
    -> Ptr GLdouble
    -> Ptr GLdouble
    -> Ptr GLint
    -> Ptr GLdouble
    -> Ptr GLdouble
    -> Ptr GLdouble
    -> IO GLint
    forall (m :: * -> *).
    MonadIO m =>
    GLdouble
    -> GLdouble
    -> GLdouble
    -> Ptr GLdouble
    -> Ptr GLdouble
    -> Ptr GLint
    -> Ptr GLdouble
    -> Ptr GLdouble
    -> Ptr GLdouble
    -> m GLint
    gluProject GLdouble
    objX GLdouble
    objY GLdouble
    objZ Ptr GLdouble
    modelBuf Ptr GLdouble
    projBuf Ptr GLint
    viewBuf

    unProject ::
          Matrix m
       => Vertex3 GLdouble -> m GLdouble -> m GLdouble -> (Position, Size)
       -> IO (Vertex3 GLdouble)
    unProject :: forall (m :: * -> *).
    Matrix m =>
    Vertex3 GLdouble
    -> m GLdouble
    -> m GLdouble
    -> (Position, Size)
    -> IO (Vertex3 GLdouble)
    unProject (Vertex3 GLdouble
    objX GLdouble
    objY GLdouble
    objZ) m GLdouble
    model m GLdouble
    proj (Position, Size)
    viewPort =
       m GLdouble
    -> (Ptr GLdouble -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble)
    forall (m :: * -> *) c b.
    (Matrix m, MatrixComponent c) =>
    m c -> (Ptr c -> IO b) -> IO b
    withColumnMajor m GLdouble
    model ((Ptr GLdouble -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble))
    -> (Ptr GLdouble -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble)
    forall a b. (a -> b) -> a -> b
    $ \Ptr GLdouble
    modelBuf ->
       m GLdouble
    -> (Ptr GLdouble -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble)
    forall (m :: * -> *) c b.
    (Matrix m, MatrixComponent c) =>
    m c -> (Ptr c -> IO b) -> IO b
    withColumnMajor m GLdouble
    proj ((Ptr GLdouble -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble))
    -> (Ptr GLdouble -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble)
    forall a b. (a -> b) -> a -> b
    $ \Ptr GLdouble
    projBuf ->
       (Position, Size)
    -> (Ptr GLint -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble)
    forall a. (Position, Size) -> (Ptr GLint -> IO a) -> IO a
    withViewport (Position, Size)
    viewPort ((Ptr GLint -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble))
    -> (Ptr GLint -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble)
    forall a b. (a -> b) -> a -> b
    $ \Ptr GLint
    viewBuf ->
       (Ptr GLdouble -> Ptr GLdouble -> Ptr GLdouble -> IO GLint)
    -> IO (Vertex3 GLdouble)
    getVertex3 ((Ptr GLdouble -> Ptr GLdouble -> Ptr GLdouble -> IO GLint)
     -> IO (Vertex3 GLdouble))
    -> (Ptr GLdouble -> Ptr GLdouble -> Ptr GLdouble -> IO GLint)
    -> IO (Vertex3 GLdouble)
    forall a b. (a -> b) -> a -> b
    $ GLdouble
    -> GLdouble
    -> GLdouble
    -> Ptr GLdouble
    -> Ptr GLdouble
    -> Ptr GLint
    -> Ptr GLdouble
    -> Ptr GLdouble
    -> Ptr GLdouble
    -> IO GLint
    forall (m :: * -> *).
    MonadIO m =>
    GLdouble
    -> GLdouble
    -> GLdouble
    -> Ptr GLdouble
    -> Ptr GLdouble
    -> Ptr GLint
    -> Ptr GLdouble
    -> Ptr GLdouble
    -> Ptr GLdouble
    -> m GLint
    gluUnProject GLdouble
    objX GLdouble
    objY GLdouble
    objZ Ptr GLdouble
    modelBuf Ptr GLdouble
    projBuf Ptr GLint
    viewBuf

    unProject4 ::
          Matrix m
       => Vertex4 GLdouble -> m GLdouble -> m GLdouble -> (Position, Size)
       -> GLclampd -> GLclampd
       -> IO (Vertex4 GLdouble)
    unProject4 :: forall (m :: * -> *).
    Matrix m =>
    Vertex4 GLdouble
    -> m GLdouble
    -> m GLdouble
    -> (Position, Size)
    -> GLdouble
    -> GLdouble
    -> IO (Vertex4 GLdouble)
    unProject4 (Vertex4 GLdouble
    objX GLdouble
    objY GLdouble
    objZ GLdouble
    clipW) m GLdouble
    model m GLdouble
    proj (Position, Size)
    viewPort GLdouble
    near GLdouble
    far =
       m GLdouble
    -> (Ptr GLdouble -> IO (Vertex4 GLdouble)) -> IO (Vertex4 GLdouble)
    forall (m :: * -> *) c b.
    (Matrix m, MatrixComponent c) =>
    m c -> (Ptr c -> IO b) -> IO b
    withColumnMajor m GLdouble
    model ((Ptr GLdouble -> IO (Vertex4 GLdouble)) -> IO (Vertex4 GLdouble))
    -> (Ptr GLdouble -> IO (Vertex4 GLdouble)) -> IO (Vertex4 GLdouble)
    forall a b. (a -> b) -> a -> b
    $ \Ptr GLdouble
    modelBuf ->
       m GLdouble
    -> (Ptr GLdouble -> IO (Vertex4 GLdouble)) -> IO (Vertex4 GLdouble)
    forall (m :: * -> *) c b.
    (Matrix m, MatrixComponent c) =>
    m c -> (Ptr c -> IO b) -> IO b
    withColumnMajor m GLdouble
    proj ((Ptr GLdouble -> IO (Vertex4 GLdouble)) -> IO (Vertex4 GLdouble))
    -> (Ptr GLdouble -> IO (Vertex4 GLdouble)) -> IO (Vertex4 GLdouble)
    forall a b. (a -> b) -> a -> b
    $ \Ptr GLdouble
    projBuf ->
       (Position, Size)
    -> (Ptr GLint -> IO (Vertex4 GLdouble)) -> IO (Vertex4 GLdouble)
    forall a. (Position, Size) -> (Ptr GLint -> IO a) -> IO a
    withViewport (Position, Size)
    viewPort ((Ptr GLint -> IO (Vertex4 GLdouble)) -> IO (Vertex4 GLdouble))
    -> (Ptr GLint -> IO (Vertex4 GLdouble)) -> IO (Vertex4 GLdouble)
    forall a b. (a -> b) -> a -> b
    $ \Ptr GLint
    viewBuf ->
       (Ptr GLdouble
     -> Ptr GLdouble -> Ptr GLdouble -> Ptr GLdouble -> IO GLint)
    -> IO (Vertex4 GLdouble)
    getVertex4 ((Ptr GLdouble
      -> Ptr GLdouble -> Ptr GLdouble -> Ptr GLdouble -> IO GLint)
     -> IO (Vertex4 GLdouble))
    -> (Ptr GLdouble
        -> Ptr GLdouble -> Ptr GLdouble -> Ptr GLdouble -> IO GLint)
    -> IO (Vertex4 GLdouble)
    forall a b. (a -> b) -> a -> b
    $
          GLdouble
    -> GLdouble
    -> GLdouble
    -> GLdouble
    -> Ptr GLdouble
    -> Ptr GLdouble
    -> Ptr GLint
    -> GLdouble
    -> GLdouble
    -> Ptr GLdouble
    -> Ptr GLdouble
    -> Ptr GLdouble
    -> Ptr GLdouble
    -> IO GLint
    forall (m :: * -> *).
    MonadIO m =>
    GLdouble
    -> GLdouble
    -> GLdouble
    -> GLdouble
    -> Ptr GLdouble
    -> Ptr GLdouble
    -> Ptr GLint
    -> GLdouble
    -> GLdouble
    -> Ptr GLdouble
    -> Ptr GLdouble
    -> Ptr GLdouble
    -> Ptr GLdouble
    -> m GLint
    gluUnProject4 GLdouble
    objX GLdouble
    objY GLdouble
    objZ GLdouble
    clipW Ptr GLdouble
    modelBuf Ptr GLdouble
    projBuf Ptr GLint
    viewBuf GLdouble
    near GLdouble
    far

    --------------------------------------------------------------------------------

    withViewport :: (Position, Size) -> (Ptr GLint -> IO a ) -> IO a
    withViewport :: forall a. (Position, Size) -> (Ptr GLint -> IO a) -> IO a
    withViewport (Position GLint
    x GLint
    y, Size GLint
    w GLint
    h) =
       [GLint] -> (Ptr GLint -> IO a) -> IO a
    forall a b. Storable a => [a] -> (Ptr a -> IO b) -> IO b
    withArray [ GLint
    x, GLint
    y, GLint -> GLint
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral GLint
    w, GLint -> GLint
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral GLint
    h ]

    withColumnMajor :: (Matrix m, MatrixComponent c) => m c -> (Ptr c -> IO b) -> IO b
    withColumnMajor :: forall (m :: * -> *) c b.
    (Matrix m, MatrixComponent c) =>
    m c -> (Ptr c -> IO b) -> IO b
    withColumnMajor m c
    mat Ptr c -> IO b
    act = m c -> (MatrixOrder -> Ptr c -> IO b) -> IO b
    forall c a.
    MatrixComponent c =>
    m c -> (MatrixOrder -> Ptr c -> IO a) -> IO a
    forall (m :: * -> *) c a.
    (Matrix m, MatrixComponent c) =>
    m c -> (MatrixOrder -> Ptr c -> IO a) -> IO a
    withMatrix m c
    mat MatrixOrder -> Ptr c -> IO b
    juggle
       where juggle :: MatrixOrder -> Ptr c -> IO b
    juggle MatrixOrder
    ColumnMajor Ptr c
    p = Ptr c -> IO b
    act Ptr c
    p
             juggle MatrixOrder
    RowMajor    Ptr c
    p = do
                transposedElems <- (Int -> IO c) -> [Int] -> IO [c]
    forall (t :: * -> *) (m :: * -> *) a b.
    (Traversable t, Monad m) =>
    (a -> m b) -> t a -> m (t b)
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
    mapM (Ptr c -> Int -> IO c
    forall a. Storable a => Ptr a -> Int -> IO a
    peekElemOff Ptr c
    p) [ Int
    0, Int
    4,  Int
    8, Int
    12,
                                                          Int
    1, Int
    5,  Int
    9, Int
    13,
                                                          Int
    2, Int
    6, Int
    10, Int
    14,
                                                          Int
    3, Int
    7, Int
    11, Int
    15 ]
                withArray transposedElems act

    getVertex3 ::
          (Ptr GLdouble -> Ptr GLdouble -> Ptr GLdouble -> IO GLint)
       -> IO (Vertex3 GLdouble)
    getVertex3 :: (Ptr GLdouble -> Ptr GLdouble -> Ptr GLdouble -> IO GLint)
    -> IO (Vertex3 GLdouble)
    getVertex3 Ptr GLdouble -> Ptr GLdouble -> Ptr GLdouble -> IO GLint
    act =
       (Ptr GLdouble -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble)
    forall a b. Storable a => (Ptr a -> IO b) -> IO b
    alloca ((Ptr GLdouble -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble))
    -> (Ptr GLdouble -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble)
    forall a b. (a -> b) -> a -> b
    $ \Ptr GLdouble
    xBuf ->
       (Ptr GLdouble -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble)
    forall a b. Storable a => (Ptr a -> IO b) -> IO b
    alloca ((Ptr GLdouble -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble))
    -> (Ptr GLdouble -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble)
    forall a b. (a -> b) -> a -> b
    $ \Ptr GLdouble
    yBuf ->
       (Ptr GLdouble -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble)
    forall a b. Storable a => (Ptr a -> IO b) -> IO b
    alloca ((Ptr GLdouble -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble))
    -> (Ptr GLdouble -> IO (Vertex3 GLdouble)) -> IO (Vertex3 GLdouble)
    forall a b. (a -> b) -> a -> b
    $ \Ptr GLdouble
    zBuf -> do
       ok <- Ptr GLdouble -> Ptr GLdouble -> Ptr GLdouble -> IO GLint
    act Ptr GLdouble
    xBuf Ptr GLdouble
    yBuf Ptr GLdouble
    zBuf
       if unmarshalGLboolean ok
          then do x <- peek xBuf
                  y <- peek yBuf
                  z <- peek zBuf
                  return $ Vertex3 x y z
          else do recordInvalidValue
                  return $ Vertex3 0 0 0

    getVertex4 ::
          (Ptr GLdouble -> Ptr GLdouble -> Ptr GLdouble -> Ptr GLdouble -> IO GLint)
       -> IO (Vertex4 GLdouble)
    getVertex4 :: (Ptr GLdouble
     -> Ptr GLdouble -> Ptr GLdouble -> Ptr GLdouble -> IO GLint)
    -> IO (Vertex4 GLdouble)
    getVertex4 Ptr GLdouble
    -> Ptr GLdouble -> Ptr GLdouble -> Ptr GLdouble -> IO GLint
    act =
       (Ptr GLdouble -> IO (Vertex4 GLdouble)) -> IO (Vertex4 GLdouble)
    forall a b. Storable a => (Ptr a -> IO b) -> IO b
    alloca ((Ptr GLdouble -> IO (Vertex4 GLdouble)) -> IO (Vertex4 GLdouble))
    -> (Ptr GLdouble -> IO (Vertex4 GLdouble)) -> IO (Vertex4 GLdouble)
    forall a b. (a -> b) -> a -> b
    $ \Ptr GLdouble
    xBuf ->
       (Ptr GLdouble -> IO (Vertex4 GLdouble)) -> IO (Vertex4 GLdouble)
    forall a b. Storable a => (Ptr a -> IO b) -> IO b
    alloca ((Ptr GLdouble -> IO (Vertex4 GLdouble)) -> IO (Vertex4 GLdouble))
    -> (Ptr GLdouble -> IO (Vertex4 GLdouble)) -> IO (Vertex4 GLdouble)
    forall a b. (a -> b) -> a -> b
    $ \Ptr GLdouble
    yBuf ->
       (Ptr GLdouble -> IO (Vertex4 GLdouble)) -> IO (Vertex4 GLdouble)
    forall a b. Storable a => (Ptr a -> IO b) -> IO b
    alloca ((Ptr GLdouble -> IO (Vertex4 GLdouble)) -> IO (Vertex4 GLdouble))
    -> (Ptr GLdouble -> IO (Vertex4 GLdouble)) -> IO (Vertex4 GLdouble)
    forall a b. (a -> b) -> a -> b
    $ \Ptr GLdouble
    zBuf ->
       (Ptr GLdouble -> IO (Vertex4 GLdouble)) -> IO (Vertex4 GLdouble)
    forall a b. Storable a => (Ptr a -> IO b) -> IO b
    alloca ((Ptr GLdouble -> IO (Vertex4 GLdouble)) -> IO (Vertex4 GLdouble))
    -> (Ptr GLdouble -> IO (Vertex4 GLdouble)) -> IO (Vertex4 GLdouble)
    forall a b. (a -> b) -> a -> b
    $ \Ptr GLdouble
    wBuf -> do
       ok <- Ptr GLdouble
    -> Ptr GLdouble -> Ptr GLdouble -> Ptr GLdouble -> IO GLint
    act Ptr GLdouble
    xBuf Ptr GLdouble
    yBuf Ptr GLdouble
    zBuf Ptr GLdouble
    wBuf
       if unmarshalGLboolean ok
          then do x <- peek xBuf
                  y <- peek yBuf
                  z <- peek zBuf
                  w <- peek wBuf
                  return $ Vertex4 x y z w
          else do recordInvalidValue
                  return $ Vertex4 0 0 0 0