packages feed

pang-a-lambda-0.2.0.0: Experiments/splitting-boxes/Main.hs

{-# LANGUAGE Arrows #-}
import Data.IORef
import Data.List
import Debug.Trace
import FRP.Yampa       as Yampa
import FRP.Yampa.Switches as Yampa
import Graphics.UI.SDL as SDL

width  = 640
height = 480

main = do
  timeRef <- newIORef (0 :: Int)
  reactimate initGraphs
             (\_ -> do
                dtSecs <- yampaSDLTimeSense timeRef
                return (dtSecs, Nothing))
             (\_ e -> display e >> return False)
             mainSF

-- | Updates the time in an IO Ref and returns the time difference
updateTime :: IORef Int -> Int -> IO Int
updateTime timeRef newTime = do
  previousTime <- readIORef timeRef
  writeIORef timeRef newTime
  return (newTime - previousTime)

yampaSDLTimeSense :: IORef Int -> IO Yampa.DTime
yampaSDLTimeSense timeRef = do
  -- Get time passed since SDL init
  newTime <- fmap fromIntegral SDL.getTicks

  -- Obtain time difference
  dt <- updateTime timeRef newTime
  let dtSecs = fromIntegral dt / 1000
  return dtSecs

initGraphs :: IO ()
initGraphs = do
  -- Initialise SDL
  SDL.init [InitVideo]

  -- Create window
  screen <- setVideoMode width height 16 [SWSurface]
  setCaption "Test" ""

display :: [(Double,Double)] -> IO()
display xs = do
  -- Obtain surface
  screen <- getVideoSurface

  -- Paint screen green
  let format = surfaceGetPixelFormat screen
  green <- mapRGB format 0 0xFF 0
  fillRect screen Nothing green

  -- Paint small red square, at an angle 'angle' with respect to the center
  red <- mapRGB format 0xFF 0 0
  let side = 10
  let paintSquare (x,y) =
        fillRect screen (Just (Rect (round x) (round y) side side)) red

  mapM_ paintSquare xs

  -- Double buffering
  SDL.flip screen

  SDL.delay 10

-- | Main animation
mainSF :: SF () [(Double, Double)]
mainSF = dlSwitch initialList

-- | A list of position-producing forking-dying SFs
initialList :: [ListSF () (Double, Double)]
initialList = [ inCircles (320, 240) ]

-- | Describe a movement in circles, forking every few samples.
inCircles :: (Double, Double) -> ListSF () (Double, Double)
inCircles (baseX, baseY) = ListSF $ proc () -> do
   t <- localTime -< ()
   let radius = 30
       x = baseX + (cos t * radius)
       y = baseY + (sin t * radius)

   -- OffSpring
   split <- isEvent ^<< spike 300 -< ()
   let offspring = if split
                     then [inCircles (y*2, x/2), inCircles (x/2, y*2)]
                     else []

   -- Time to die?
   die   <- isEvent ^<< spike 350 -< ()

   returnA -< ((x,y), die, offspring)

   -- () <- arr id -< trace ( "Time : " ++ show t
   --                       ++ " mod: " ++ show (round t `mod` 10)
   --                       ++ " s: "   ++ show split
   --                       )
   --                       ()

-- | Produce a spike every few samples.
spike :: Int -> SF () (Yampa.Event ())
spike m = spikeBool m >>> edge
 where spikeBool m = resetCounter m >>> arr (== 0)

-- | Create a decreasing counter that is reset to the starting value when it
-- reaches 0.
resetCounter :: Int -> SF () Int
resetCounter m = loopPre m $ arr (snd >>> decR m >>> dup)
 where decR m n = if n == 0 then m else n - 1
       dup  x   = (x, x)

-- Version of list splitting that works in traditional Yampa
-- inCirclesL' ips = inCirclesL ips >>> arr (map fst)
--
-- inCirclesL :: [SF () ((Double, Double), Bool)]
--            -> SF () [((Double, Double), Bool)]
-- inCirclesL ips = dpSwitchB ips evProd addToList
--
--  where
--      evProd :: SF ((), [((Double, Double), Bool)]) (Yampa.Event [(Double, Double)])
--      evProd = noEvent --> arr (snd >>> splitBalls)
--
-- initialList = [ inCircles (320, 240) ]
-- initialList' = [ inCircles' (320, 240) ]
--
-- addToList :: [SF () ((Double, Double), Bool)]
--           -> [(Double, Double)]
--           -> SF () [((Double, Double), Bool)]
-- addToList sfs ips = trace ("Adding new circles: " ++ show ips)
--                   $ inCirclesL (sfs ++ map inCircles ips)
--
-- splitBalls :: [((Double, Double), Bool)] -> Yampa.Event [(Double, Double)]
-- splitBalls ps
--   | null ls   = noEvent
--   | otherwise = Yampa.Event ls
--  where ls = [ (y-20, x+20) | ((x,y),True) <- ps ]
--
-- inCircles :: (Double, Double) -> SF () ((Double, Double), Bool)
-- inCircles (baseX, baseY) = proc () -> do
--    t <- localTime -< ()
--    let radius = 30
--        x = baseX + (cos t * radius)
--        y = baseY + (sin t * radius)
--
--    split <- noEvent --> spike 100 -< ()
--
--    () <- arr id -< trace ( "Time : " ++ show t
--                          ++ " mod: " ++ show (round t `mod` 10)
--                          ++ " s: "   ++ show split
--                          )
--                          ()
--
--    returnA -< ((x,y), isEvent split)