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)