Skip to content
This repository has been archived by the owner on Aug 29, 2023. It is now read-only.

Commit

Permalink
clean up and doc for GUI
Browse files Browse the repository at this point in the history
  • Loading branch information
anna-jana committed Jan 17, 2016
1 parent 7e04a1f commit ef8a10e
Showing 1 changed file with 47 additions and 26 deletions.
73 changes: 47 additions & 26 deletions GUI.hs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module GUI (
Color,
white, black, grey, red, green, blue, cyan, brown, yellow, orange,
getColorFromRGB255,
white, black, grey, red, green, blue, cyan, brown, yellow, orange,
runCellularAutomata2D) where

import Control.Monad (void, when)
Expand All @@ -19,13 +19,15 @@ import CellularAutomata2D
-- using bitoperations.
type Color = SDL.Pixel

-- | get a color value from a red, a green and a blue component of type Word8 (0..255)
getColorFromRGB255 :: Word8 -> Word8 -> Word8 -> Color
getColorFromRGB255 r g b = SDL.Pixel $
shiftL (fromIntegral r) 24 .|.
shiftL (fromIntegral g) 16 .|.
shiftL (fromIntegral b) 8 .|.
255

-- | basic colors
red, green, blue, yellow, cyan, brown, orange, black, white, grey :: Color
red = getColorFromRGB255 255 0 0
green = getColorFromRGB255 0 255 0
Expand All @@ -38,6 +40,7 @@ cyan = getColorFromRGB255 0 255 255
brown = getColorFromRGB255 165 42 42
orange = getColorFromRGB255 255 165 0

-- | the size of the display window we try to get
targetScreenSize :: Int
targetScreenSize = 500

Expand All @@ -51,29 +54,32 @@ targetScreenSize = 500
runCellularAutomata2D :: (Eq a) => Torus a -> [a] -> (a -> Color) ->
Rule a -> IO ()
runCellularAutomata2D space states colors updateCell = do
-- compute our window dimensions
let (spaceHeight, spaceWidth) = getSpaceSize space
let cellSize' = if spaceHeight > spaceWidth
then targetScreenSize `div` spaceHeight
else targetScreenSize `div` spaceWidth
let screenHeight = cellSize' * spaceHeight
let screenWidth = cellSize' * spaceWidth
-- setup SDL and open a window
SDL.init []
screen <- SDL.setVideoMode screenWidth screenHeight 32 [SDL.DoubleBuf]
-- start the game loop
loop $ SimulationState screen colors cellSize'
updateCell space 0 False states 3 0 0 1
(div screenWidth 2) (div screenHeight 2)
[] False 0 0

data SimulationState a = SimulationState
{ _screen :: SDL.Surface
, _colors :: a -> SDL.Pixel
{ getScreen :: SDL.Surface
, getColor :: a -> SDL.Pixel
, cellSize :: Int
, updateCellFn :: Rule a
, _space :: Torus a
, getSpace :: Torus a
, accColor :: Int
, running :: Bool
, possibleStates :: [a]
, _fps :: Int
, getFPS :: Int
, transX, transY :: Int -- ^ number of cells
, zoom :: Float
, halfWidth, halfHeight :: Int
Expand All @@ -82,20 +88,24 @@ data SimulationState a = SimulationState
, moveX :: Int, moveY :: Int
}

-- | the game loop
loop :: (Eq a) => SimulationState a -> IO ()
loop state = do
start <- SDL.getTicks
-- get an event and process it
event <- SDL.pollEvent
case event of
SDL.Quit -> SDL.quit
SDL.MouseButtonUp _ _ SDL.ButtonLeft -> loop state { inserting = False, inserted = [] }
SDL.MouseButtonUp _ _ SDL.ButtonLeft -> loop state { inserting = False, inserted = [] } -- stop changing cell states
SDL.MouseMotion x y _ _
| inserting state -> insert state x y
| inserting state -> insert state x y -- inserting new cells
| otherwise -> loop state
SDL.MouseButtonDown x y SDL.ButtonLeft -> insert state { inserting = True } x y
SDL.MouseButtonDown x y SDL.ButtonLeft -> insert state { inserting = True } x y -- start changing cells
SDL.MouseButtonDown _ _ SDL.ButtonRight -> loop state { accColor = (accColor state + 1) `mod`
length (possibleStates state) }
-- toogle running (update of the world)
SDL.KeyDown (SDL.Keysym SDL.SDLK_SPACE _ _) -> loop state { running = not (running state) }
-- move the users view on the world
SDL.KeyDown (SDL.Keysym SDL.SDLK_LEFT _ _) -> loop state { moveX = 1 }
SDL.KeyDown (SDL.Keysym SDL.SDLK_RIGHT _ _) -> loop state { moveX = -1 }
SDL.KeyDown (SDL.Keysym SDL.SDLK_UP _ _) -> loop state { moveY = 1 }
Expand All @@ -104,56 +114,67 @@ loop state = do
SDL.KeyUp (SDL.Keysym SDL.SDLK_RIGHT _ _) -> loop state { moveX = 0 }
SDL.KeyUp (SDL.Keysym SDL.SDLK_UP _ _) -> loop state { moveY = 0 }
SDL.KeyUp (SDL.Keysym SDL.SDLK_DOWN _ _) -> loop state { moveY = 0 }
-- zoom in/out
SDL.KeyDown (SDL.Keysym SDL.SDLK_PLUS _ _) -> loop state { zoom = zoom state + 0.25 }
SDL.KeyDown (SDL.Keysym SDL.SDLK_MINUS _ _) -> loop state { zoom = zoom state - 0.25 }
-- reset our view (no zoom or translation)
SDL.KeyDown (SDL.Keysym SDL.SDLK_h _ _) -> loop state { transX = 0, transY = 0, zoom = 1, accColor = 0 }
-- advance for one generation (only if we aren't running)
SDL.KeyDown (SDL.Keysym SDL.SDLK_RETURN _ _) -> if running state then loop state else
update (_space state) (updateCellFn state) >>= \space' ->
loop state { _space = space' }
update (getSpace state) (updateCellFn state) >>= \space' ->
loop state { getSpace = space' }
-- donw processing the events
SDL.NoEvent -> do
draw state
-- if we are running then update the world
newSpace <- if running state
then update (_space state) (updateCellFn state)
else return (_space state)
then update (getSpace state) (updateCellFn state)
else return (getSpace state)
-- delay to get the right FPS
stop <- SDL.getTicks
let toDelay = 1 / realToFrac (_fps state) - realToFrac (stop - start) / 1000 :: Double
let toDelay = 1 / realToFrac (getFPS state) - realToFrac (stop - start) / 1000 :: Double
when (toDelay > 0) $ threadDelay $ round $ 10^(5::Int) * toDelay
loop state { _space = newSpace
, transX = moveX state + transX state
loop state { getSpace = newSpace
, transX = moveX state + transX state -- apply moves
, transY = moveY state + transY state }
_ -> loop state
where
-- FIXME: if you have states witch do not appeare in the possibleStates list,
-- next goes into an infinit loop.
next x = tail (dropWhile (/= x) $ cycle (possibleStates state)) !! accColor state
-- change the cell state at a given pixel coordinate
insert state' x y = if not isOutside && cellIndex `notElem` inserted state'
then loop state' { _space = setCell (_space state') cellIndex
(next (flip getCell cellIndex $ _space state')),
then loop state' { getSpace = setCell (getSpace state') cellIndex
(next (flip getCell cellIndex $ getSpace state')),
inserted = cellIndex : inserted state' }
else loop state'
where cellIndex = ((floor (fromIntegral (fromIntegral y - halfHeight state)/zoom state) + halfHeight state) `div` cellSize state - transY state,
(floor (fromIntegral (fromIntegral x - halfWidth state)/zoom state) + halfWidth state) `div` cellSize state - transX state)
isOutside = fst cellIndex < 0 || fst cellIndex >= fst (getSpaceSize $ _space state) ||
snd cellIndex < 0 || snd cellIndex >= snd (getSpaceSize $ _space state)
isOutside = fst cellIndex < 0 || fst cellIndex >= fst (getSpaceSize $ getSpace state) ||
snd cellIndex < 0 || snd cellIndex >= snd (getSpaceSize $ getSpace state)

-- x -> col
-- col = ((x - w/2)/zoom + w/2)/cellSize - transX
-- col -> x
-- x = zoom*((col + transX)*cellSize - w/2) + w/2


-- | draw the state of the automata to the window
draw :: (Eq a) => SimulationState a -> IO ()
draw state = do
SDL.fillRect (_screen state) Nothing (SDL.Pixel 0)
forSpace (_space state) $ \(row, col) cell -> do
let color = _colors state cell
-- clear the window
SDL.fillRect (getScreen state) Nothing (SDL.Pixel 0)
forSpace (getSpace state) $ \(row, col) cell -> do
-- get the rect of the cell
let top = cellSize state * row + transY state * cellSize state
let left = cellSize state * col + transX state * cellSize state
let rect = SDL.Rect
(round (zoom state * fromIntegral (left - halfWidth state)) + halfWidth state)
(round (zoom state * fromIntegral (top - halfHeight state)) + halfHeight state)
(round (zoom state * fromIntegral (left + cellSize state - halfWidth state)) + halfWidth state)
(round (zoom state * fromIntegral (top + cellSize state - halfHeight state)) + halfHeight state)
void $ Draw.box (_screen state) rect color
void $ Draw.rectangle (_screen state) rect black
SDL.flip (_screen state)
-- draw cell
let color = getColor state cell
void $ Draw.box (getScreen state) rect color
-- draw the grid
void $ Draw.rectangle (getScreen state) rect black
SDL.flip (getScreen state)

0 comments on commit ef8a10e

Please sign in to comment.