{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE FlexibleContexts #-}
module Graphics.Liveplot.Window where
import Prelude hiding (init)
import Control.Monad
import Control.Lens ((^.), contains, _Left, _Right)
import Data.IORef
import Data.Maybe (isNothing)
import Data.Set (Set)
import qualified Data.Set as S
import Data.Time.Clock
import Graphics.UI.GLFW
import Linear
import MVC
import qualified MVC.Prelude as MVC
import Graphics.Rendering.OpenGL
import Graphics.GLUtil.Camera2D
import Data.Vinyl
import Control.Concurrent (threadDelay)
import Control.Concurrent.STM
import Pipes.Extras ((+++))
import Graphics.Liveplot.Types
import Graphics.Liveplot.Utils
import Graphics.Liveplot.Line
initGraph :: String -> (Float, Float) -> (Int, Int)-> GraphInfo
initGraph name scale' offset = defGI {
graph_name = name
, graph_offset = offset
, graph_scale = scale'
}
lineGraph :: String -> (Float, Float) -> (Int, Int) -> (GraphInfo, GLfloat)
lineGraph name scale' offset = (defGI
{ graph_name = name
, graph_offset = offset
, graph_scale = scale'
}, 0)
lineGraphN :: Int -> String -> (Float, Float) -> (Int, Int) -> (GraphInfo, GLfloat)
lineGraphN points name scale' offset = (defGI
{ graph_name = name
, graph_points = points
, graph_samples = points
, graph_resolution = points
, graph_offset = offset
, graph_scale = scale'
}, 0)
ogl :: (Plottable a) => [(GraphInfo, a)]
-> Managed (View (Either (SensorReading a) GLApp), Controller Event)
ogl parts = join $ managed $ \k -> do
let simpleErrorCallback e s = putStrLn $ unwords [show e, show s]
let width = 600
height = 300
windowTitle = "lala"
setErrorCallback $ Just simpleErrorCallback
r <- init
when (not r) (error "Error initializing GLFW!")
windowHint $ WindowHint'ClientAPI ClientAPI'OpenGL
windowHint $ WindowHint'OpenGLForwardCompat True
windowHint $ WindowHint'OpenGLProfile OpenGLProfile'Core
windowHint $ WindowHint'ContextVersionMajor 3
windowHint $ WindowHint'ContextVersionMinor 3
m@(~(Just w)) <- createWindow width height windowTitle Nothing Nothing
when (isNothing m) (error "Couldn't create window!")
makeContextCurrent m
kbState <- newIORef S.empty
mbState <- newIORef S.empty
mpState <- getCursorPos w >>= newIORef . uncurry V2
wsState <- getWindowSize w >>= newIORef . uncurry V2
lastTick <- getCurrentTime >>= newIORef
setKeyCallback w (Just $ keyCallback kbState)
setMouseButtonCallback w (Just $ mbCallback mbState)
setCursorPosCallback w (Just $ mpCallback mpState)
setWindowSizeCallback w $ Just $ \win x y -> do
wsCallback wsState win x y
viewport $= (Position 0 0, Size (fromIntegral x) (fromIntegral y))
blend $= Enabled
blendFunc $= (SrcAlpha, OneMinusSrcAlpha)
redraw <- atomically $ newTVar $ Just True
inits <- flip mapM parts $ \(gi, (_init :: a)) -> initplot gi :: IO (PlotInit a)
graphInfoTVars <- flip mapM parts $ \(gi, (_init :: a)) -> atomically $ newTVar gi
let (dataTVars, storefns, drawfns) = unzip3 inits
let tvars = zip dataTVars graphInfoTVars
let setRedraw = atomically $ writeTVar redraw $ Just True
let resetRedraw = atomically $ writeTVar redraw $ Nothing
let tick = do pollEvents
t <- getCurrentTime
dt <- realToFrac . diffUTCTime t <$> readIORef lastTick
writeIORef lastTick t
keys <- readIORef kbState
buttons <- readIORef mbState
pos <- readIORef mpState
wsize <- readIORef wsState
mredraw <- readTVarIO redraw
case mredraw of
Nothing -> threadDelay 100 >> return ()
Just _ -> do
clear [ColorBuffer, DepthBuffer]
zipWithM_ (\draw (tvar, gtvar) -> do
mdat <- readTVarIO tvar
gi <- readTVarIO gtvar
draw mdat gi
) drawfns tvars
swapBuffers w
threadDelay 10
resetRedraw
return $ [
Timestep dt
, Keys keys
, Buttons buttons
, MousePos pos
, WinSize wsize
]
let steptick :: Producer Event IO ()
steptick = forever $ lift tick >>= mapM_ yield
let handleAppInfo :: View (GLApp)
handleAppInfo = asSink $ \(GLApp ai vp) -> do
let updategi = \x -> x {
graph_appinfo = ai
, graph_viewport = vp
}
mapM_ (\v -> atomically $ modifyTVar v updategi) graphInfoTVars
setRedraw
let
handleData = asSink $ \dat -> do
mapM_ ($ dat) storefns
setRedraw
k $ do
evts <- MVC.producer (bounded 1) (steptick)
let hdat = handles _Left handleData
hapi = handles _Right handleAppInfo
return (hapi <> hdat, evts)
campipe :: (Monad m)
=> AppInfo
-> Camera GLfloat
-> Viewport
-> MVC.Proxy () (Event) () (GLApp) m ()
campipe initai initcam initviewport = go initai initcam initviewport
where
fwd ai c vp = yield (GLApp ai vp) >> go ai c vp
go ai c vp = do
evt <- await
case evt of
Keys k | k ^. contains Key'Escape -> return ()
| k ^. contains Key'Q -> return ()
| otherwise -> do
let newCam = moveCam k c
let newAi = SField =: (camMatrix newCam)
fwd newAi newCam vp
WinSize (V2 sx sy) -> fwd ai c (fst vp, Size (fromIntegral sx) (fromIntegral sy))
_ -> go ai c vp
defaultCamPipe :: Monad m => MVC.Proxy () (Event) () (GLApp) m ()
defaultCamPipe = campipe defaultAppInfo defaultCam defaultViewport
defaultPipe :: forall m a . (Monad m, Plottable a)
=> Pipe (Either (SensorReading a) Event)
(Either (SensorReading a) GLApp) m ()
defaultPipe = cat +++ defaultCamPipe
keyCallback :: IORef (Set Key) -> KeyCallback
keyCallback keys _w k _ KeyState'Pressed _mods = modifyIORef' keys (S.insert k)
keyCallback keys _w k _ KeyState'Released _mods = modifyIORef' keys (S.delete k)
keyCallback _ _ _ _ _ _ = return ()
mbCallback :: IORef (Set MouseButton) -> MouseButtonCallback
mbCallback mbs _w b MouseButtonState'Pressed _ = modifyIORef' mbs (S.insert b)
mbCallback mbs _w b MouseButtonState'Released _ = modifyIORef' mbs (S.delete b)
mpCallback :: IORef (V2 Double) -> CursorPosCallback
mpCallback mp _w x y = writeIORef mp (V2 x y)
wsCallback :: IORef (V2 Int) -> WindowSizeCallback
wsCallback ws _w w h = writeIORef ws (V2 w h)