    module Graphics.Gloss.Data.ViewPort
            ( ViewPort(..)
            , viewPortInit
            , applyViewPortToPicture
            , invertViewPort )
    where
    import Graphics.Gloss.Data.Picture

    -- | The 'ViewPort' represents the global transformation applied to the displayed picture.
    --      When the user pans, zooms, or rotates the display then this changes the 'ViewPort'.
    data ViewPort
            = ViewPort {
            -- | Global translation.
              viewPortTranslate     :: !(Float, Float)

            -- | Global rotation (in degrees).
            , viewPortRotate        :: !Float

            -- | Global scaling (of both x and y coordinates).
            , viewPortScale         :: !Float
            }

    -- | The initial state of the viewport.
    viewPortInit :: ViewPort
    viewPortInit
            = ViewPort
            { viewPortTranslate     = (0, 0)
            , viewPortRotate        = 0
            , viewPortScale         = 1
            }

    -- | Translates, rotates, and scales an image according to the 'ViewPort'.
    applyViewPortToPicture :: ViewPort  -> Picture -> Picture
    applyViewPortToPicture
            ViewPort { viewPortScale        = vscale
                     , viewPortTranslate    = (transX, transY)
                     , viewPortRotate       = vrotate }
            = Scale vscale vscale . Rotate vrotate . Translate transX transY

    -- | Takes a point using screen coordinates, and uses the `ViewPort` to convert
    --   it to Picture coordinates. This is the inverse of `applyViewPortToPicture`
    --   for points.
    invertViewPort :: ViewPort -> Point -> Point
    invertViewPort
            ViewPort { viewPortScale        = vscale
                     , viewPortTranslate    = vtrans
                     , viewPortRotate       = vrotate }
            pos
            = rotateV (degToRad vrotate) (mulSV (1 / vscale) pos) - vtrans

    -- | Convert degrees to radians
    {-# INLINE degToRad #-}
    degToRad :: Float -> Float
    degToRad d      = d * pi / 180

    -- | Multiply a vector by a scalar.
    mulSV :: Float -> Vector -> Vector
    mulSV s (x, y)
            = (s * x, s * y)
    {-# INLINE mulSV #-}

    -- | Rotate a vector by an angle (in radians). +ve angle is counter-clockwise.
    rotateV :: Float -> Vector -> Vector
    rotateV r (x, y)
     =      (  x * cos r - y * sin r
            ,  x * sin r + y * cos r)
    {-# INLINE rotateV #-}