module Graphics.Gloss.Data.ViewPort
        ( ViewPort(..)
        , viewPortInit
        , applyViewPortToPicture
        , invertViewPort )
where
import Graphics.Gloss.Data.Picture
import qualified Graphics.Gloss.Data.Point.Arithmetic as Pt


-- | 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) Pt.- vtrans


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


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