packages feed

miso-examples-1.2.0.0: three/Main.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards   #-}
module Main where

import           Control.Monad
import           Data.IORef
import qualified Data.Map      as M
import           GHCJS.Types

import           Miso
import           Miso.String

data Action
  = GetTime
  | Init
  | SetTime !Double

withStats :: JSVal -> IO () -> IO ()
withStats stats m = do
  statsBegin stats >> m
  statsEnd stats

data Context = Context
  { rotateCube :: IO ()
  , renderScene :: IO ()
  , stats :: JSVal
  }

initContext :: IORef Context -> IO ()
initContext ref = do
  canvas <- getElementById "canvas"
  scene <- newScene
  camera <- newCamera
  renderer <- newRenderer canvas
  setSize renderer
  cube <- join $ newMesh
    <$> newBoxGeometry 1 1 1
    <*> newMeshBasicMaterial
  addToScene scene cube
  positionCamera camera 5
  stats <- newStats
  statsContainer <- getElementById "stats"
  addStatsToDOM statsContainer stats
  writeIORef ref Context {
    stats = stats
  , rotateCube = do
      rotateX cube 0.1
      rotateY cube 0.1
  , renderScene =
      render renderer scene camera
  }

main :: IO ()
main = do
  stats <- newStats
  ref <- newIORef $ Context (pure ()) (pure ()) stats
  m <- now
  startApp App { model = m
               , initialAction = Init
               , update = updateModel ref
               , mountPoint = Nothing
               , ..
               }
    where
      events = defaultEvents
      view   = viewModel
      subs   = []

viewModel :: Double -> View action
viewModel _ = div_ [] [
    div_ [ id_ "stats"
         , style_ $ M.singleton "position" "absolute"
         ] []
  , canvas_ [ id_ "canvas"
            , width_ "400"
            , height_ "300"
            ] []
  ]

updateModel
  :: IORef Context
  -> Action
  -> Double
  -> Effect Action Double
updateModel ref Init m = m <# do
  initContext ref
  pure GetTime

updateModel ref GetTime m = m <# do
  Context {..} <- readIORef ref
  withStats stats $ do
    rotateCube
    renderScene
  SetTime <$> now

updateModel _ (SetTime m) _ =
  m <# pure GetTime

foreign import javascript unsafe "$r = new Stats();"
  newStats :: IO JSVal

foreign import javascript unsafe "$1.begin();"
  statsBegin :: JSVal -> IO ()

foreign import javascript unsafe "$1.end();"
  statsEnd :: JSVal -> IO ()

foreign import javascript unsafe "$1.showPanel(0);"
  showPanel :: JSVal -> IO ()

foreign import javascript unsafe "$r = new THREE.Scene();"
  newScene :: IO JSVal

foreign import javascript unsafe "$r = new THREE.BoxGeometry( $1, $2, $3 );"
  newBoxGeometry :: Int -> Int -> Int -> IO JSVal

foreign import javascript unsafe "$r = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );"
  newCamera :: IO JSVal

foreign import javascript unsafe "$r = new THREE.Mesh( $1, $2 );"
  newMesh :: JSVal -> JSVal -> IO JSVal

foreign import javascript unsafe "$r = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );"
  newMeshBasicMaterial :: IO JSVal

foreign import javascript unsafe "$r = new THREE.WebGLRenderer({canvas:$1, antialias : true});"
  newRenderer :: JSVal -> IO JSVal

foreign import javascript unsafe "$1.setSize( window.innerWidth, window.innerHeight );"
  setSize :: JSVal -> IO ()

foreign import javascript unsafe "$1.add($2);"
  addToScene :: JSVal -> JSVal -> IO ()

foreign import javascript unsafe "$1.position.z = $2;"
  cameraZ :: JSVal -> Int -> IO ()

foreign import javascript unsafe "$1.rotation.x += $2;"
  rotateX :: JSVal -> Double -> IO ()

foreign import javascript unsafe "$1.rotation.y += $2;"
  rotateY :: JSVal -> Double -> IO ()

foreign import javascript unsafe "$1.render($2, $3);"
  render :: JSVal -> JSVal -> JSVal -> IO ()

foreign import javascript unsafe "$1.position.z = $2;"
  positionCamera :: JSVal -> Double -> IO ()

foreign import javascript unsafe "$1.appendChild( $2.domElement );"
  addStatsToDOM :: JSVal -> JSVal -> IO ()