packages feed

yampa-canvas-0.2: example/DropBalls.hs

{-# LANGUAGE OverloadedStrings, TupleSections #-}

module Main where

-- set browser to: http://localhost:3000/
import Graphics.Blank hiding (Event)
import qualified Graphics.Blank as Blank
import FRP.Yampa
import FRP.Yampa.Vector2

import FRP.Yampa.Canvas
import BouncingBalls hiding (main,renderScene)

---------------------------------------------------

main :: IO ()
main = blankCanvas 3000 { events = ["click"] } $ animateDroppingBalls

-- | Display an animation of multiple falling balls.
animateDroppingBalls :: DeviceContext -> IO ()
animateDroppingBalls = reactimateSFinContext detectClick renderScene dropBalls

-- | A Canvas action to render the entire scene.
renderScene :: [Ball] -> Canvas ()
renderScene bs = renderInstructions >> scaleScene >> renderBalls bs

-- | A Canvas action to render the instruction text.
renderInstructions :: Canvas ()
renderInstructions =
 do font "20pt Comic Sans MS"
    fillText ("Click on the canvas to add a ball",50,50)

---------------------------------------------------

-- | Detect a mouse click in the canvas.
detectClick :: Blank.Event -> Canvas (Event Position)
detectClick ev = case ePageXY ev of
                   Nothing     -> return NoEvent
                   Just (x,y)  -> fmap Event (toXYCo (x,y))

-- | Convert a Blank Canvas co-ordinate into a Yampa Position.
toXYCo :: (Double,Double) -> Canvas Position
toXYCo (i,j) =
  do context <- myCanvasContext
     let w = width context
         h = height context
     return $ vector2 (i / w) (1 - j / h)

---------------------------------------------------

-- | Construct a bouncing ball model that allows responds to input events by adding new balls.
dropBalls :: SF (Event Position) [Ball]
dropBalls = ballGenerator >>> ballCollection []

-- | A collection of active balls into which new balls can be dropped.
ballCollection :: [SF (Event Ball) Ball] -> SF (Event Ball) [Ball]
ballCollection bs = notYet >>> pSwitchB bs (arr fst) (\ sfs b -> ballCollection (bouncingBall1d b : sfs))

-- | Convert events carrying co-ordinates into events carrying new balls.
ballGenerator :: SF (Event Position) (Event Ball)
ballGenerator = arr (fmap newBallAt)

-- | Create a new ball of the specified colour, at the specified co-ordinates.
newBallAt :: Position -> Ball
newBallAt p = MkBall
          { pos    = p,
            vel    = vector2 0 0,
            radius = 0.02,
            colour = "purple"
          }

-------------------------------------------------------------------