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


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


-- | Translates, rotates, and scales an image according to the 'ViewPort'.
applyViewPortToPicture :: ViewPort -> Picture -> Picture
applyViewPortToPicture :: ViewPort -> Picture -> Picture
applyViewPortToPicture
  ViewPort
    { viewPortScale :: ViewPort -> Float
viewPortScale = Float
vscale
    , viewPortTranslate :: ViewPort -> (Float, Float)
viewPortTranslate = (Float
transX, Float
transY)
    , viewPortRotate :: ViewPort -> Float
viewPortRotate = Float
vrotate
    } =
    Float -> Float -> Picture -> Picture
Scale Float
vscale Float
vscale (Picture -> Picture) -> (Picture -> Picture) -> Picture -> Picture
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Float -> Picture -> Picture
Rotate Float
vrotate (Picture -> Picture) -> (Picture -> Picture) -> Picture -> Picture
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Float -> Float -> Picture -> Picture
Translate Float
transX Float
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 -> (Float, Float) -> (Float, Float)
invertViewPort
  ViewPort
    { viewPortScale :: ViewPort -> Float
viewPortScale = Float
vscale
    , viewPortTranslate :: ViewPort -> (Float, Float)
viewPortTranslate = (Float, Float)
vtrans
    , viewPortRotate :: ViewPort -> Float
viewPortRotate = Float
vrotate
    }
  (Float, Float)
pos =
    Float -> (Float, Float) -> (Float, Float)
rotateV (Float -> Float
degToRad Float
vrotate) (Float -> (Float, Float) -> (Float, Float)
mulSV (Float
1 Float -> Float -> Float
forall a. Fractional a => a -> a -> a
/ Float
vscale) (Float, Float)
pos) (Float, Float) -> (Float, Float) -> (Float, Float)
Pt.- (Float, Float)
vtrans


-- | Convert degrees to radians
degToRad :: Float -> Float
degToRad :: Float -> Float
degToRad Float
d = Float
d Float -> Float -> Float
forall a. Num a => a -> a -> a
* Float
forall a. Floating a => a
pi Float -> Float -> Float
forall a. Fractional a => a -> a -> a
/ Float
180
{-# INLINE degToRad #-}


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


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