packages feed

brillo-1.13.3: Brillo/Data/ViewPort.hs

module Brillo.Data.ViewPort (
  ViewPort (..),
  viewPortInit,
  applyViewPortToPicture,
  invertViewPort,
)
where

import Brillo.Data.Picture
import Brillo.Data.Point.Arithmetic qualified 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
  { viewPortTranslate :: !(Float, Float)
  -- ^ Global translation.
  , viewPortRotate :: !Float
  -- ^ Global rotation (in degrees).
  , viewPortScale :: !Float
  -- ^ Global scaling (of both x and y coordinates).
  }


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