packages feed

accelerate-examples-1.1.0.0: examples/fluid/src-acc/Event.hs

{-# LANGUAGE PatternGuards #-}
--
-- Event handling
--

module Event where

import Config
import World

import Data.Label
import System.Exit
import Graphics.Gloss.Interface.IO.Game
import Data.Array.Accelerate                                        ( Z(..), (:.)(..) )


-- Event locations are returned as window coordinates, where the origin is in
-- the centre of the window and increases to the right and up. If the simulation
-- size is (100,100) with scale factor of 4, then the event coordinates are
-- returned in the range [-200,200].
--
react :: Config -> Event -> World -> IO World
react opt event world =
  case event of
    EventKey (Char c) s m _                     -> return $ keyboard c s m
    EventKey (MouseButton LeftButton) s m uv    -> return $ mouse m s (coord uv)
    EventMotion uv                              -> return $ motion (coord uv)
    EventKey (SpecialKey KeyEsc) Down _ _       -> exitSuccess
    _                                           -> return world
  where
    -- Inject a new density source when the left button is clicked.
    --
    -- If the shift key is held, remember the location and add a new velocity
    -- source between the old and new points as the mouse moves.
    --
    mouse key button xy
      | Up   <- shift key, Down <- button
      = world { currentSource = Density xy
              , densitySource = addDensity xy }

      | Down <- shift key, Down <- button
      = world { currentSource = Velocity xy }

      | Down <- shift key, Up   <- button
      = case currentSource world of
          Velocity x0y0 -> world { currentSource  = None
                                 , velocitySource = addVelocity x0y0 xy }
          _             -> world { currentSource  = None}

      | otherwise
      = world { currentSource = None }

    -- Handle key presses
    --
    keyboard 'r' Down _         = initialise opt
    keyboard 'd' Down _         = world { displayDensity  = not (displayDensity  world) }
    keyboard 'v' Down _         = world { displayVelocity = not (displayVelocity world) }
    keyboard _   _    _         = world

    -- As the mouse moves, keep inserting density sources, or adding source
    -- velocities
    --
    motion xy =
      case currentSource world of
        Density _       -> world { currentSource  = Density xy
                                 , densitySource  = addDensity xy }
        Velocity x0y0   -> world { currentSource  = Velocity xy
                                 , velocitySource = addVelocity x0y0 xy }
        _               -> world
    --
    inbounds (x,y)              = x >= 0 && x < get simulationWidth  opt
                               && y >= 0 && y < get simulationHeight opt

    addDensity ix@(x,y)
      | inbounds ix             = (Z:.y:.x, density) : densitySource world
      | otherwise               = densitySource world

    addVelocity ix@(x0,y0) (x1,y1)
      | inbounds ix             = let u = fromIntegral (x1-x0)
                                      v = fromIntegral (y1-y0)
                                  in (Z:.y0:.x0, (u * velocity, v * velocity)) : velocitySource world
      | otherwise               = velocitySource world
    --
    density     = get inputDensity opt
    velocity    = get inputVelocity opt
    zoom        = fromIntegral $ get displayScale opt
    width       = fromIntegral $ get simulationWidth  opt
    height      = fromIntegral $ get simulationHeight opt
    scaleX      = width  / (width  * zoom + 1)
    scaleY      = height / (height * zoom + 1)
    coord (u,v) = ( truncate $ u * scaleX + width /2
                  , truncate $ v * scaleY + height/2)