brillo-examples-2.0.0: picture/Styrene/Main.hs
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Actor (Actor (..))
import Advance (advanceWorld)
import Config (beadColor, leafColor, nodeColor, showBeadVelocity, simResolution)
import QuadTree (QuadTree (..))
import World (World (..), worldInit)
import Brillo (
Color,
Display (InWindow),
Picture (Blank, Color, Line, Pictures, Polygon, Scale, Translate),
black,
greyN,
rectangleWire,
red,
simulate,
)
import Brillo.Data.Vector (mulSV)
import Data.Map qualified as Map
main :: IO ()
main =
simulate
( InWindow
"Polystyrene - alt-left-click-drag rotates"
(600, 600) -- x and y size of window (in pixels).
(10, 10) -- position of window
)
black -- background color
simResolution -- simulation resolution
-- (number of steps to take for each second of time)
worldInit -- the initial world.
drawWorld -- a function to convert the world to a Picture.
advanceWorld -- a function to advance the world to
-- the next simulation step.
-- Draw ------------------------------------------------------------------------
-- | Draw this world as a picture.
drawWorld :: World -> Picture
drawWorld (World actors tree) = do
let
-- Split the list of actors into beads and walls.
-- This lets us draw all the beads at once without having to keep changing
-- the current color. (Which is a bit of a performance improvement.)
(theBeads, theWalls) = splitActors $ Map.elems actors
picBeads = Color beadColor $ Pictures $ map drawActor theBeads
picWalls = Pictures $ map drawActor theWalls
picTree = drawQuadTree tree
Scale 0.8 0.8 $
Pictures [picTree, picWalls, picBeads]
-- | Split actors into beads and walls
splitActors :: [Actor] -> ([Actor], [Actor])
splitActors =
splitActors' [] []
splitActors' :: [Actor] -> [Actor] -> [Actor] -> ([Actor], [Actor])
splitActors' accBeads accWalls [] =
(accBeads, accWalls)
splitActors' accBeads accWalls (a : as) =
case a of
Bead{} -> splitActors' (a : accBeads) accWalls as
Wall{} -> splitActors' accBeads (a : accWalls) as
-- | Draw an actor as a picture.
drawActor :: Actor -> Picture
drawActor actor =
case actor of
Bead _ix _mode radius (posX, posY) v -> do
let
bead = circleFilled radius 10
vel =
if showBeadVelocity
then Color red $ Line [(0, 0), mulSV 0.1 v]
else Blank
Translate posX posY $ Pictures [bead, vel]
Wall _ p1 p2 ->
Color (greyN 0.8) $ Line [p1, p2]
-- | Draw a quadtree as a picture
drawQuadTree :: QuadTree a -> Picture
drawQuadTree tree =
case tree of
QNode p size tTL tTR tBL tBR ->
Pictures
[ drawQuadTree tTL
, drawQuadTree tTR
, drawQuadTree tBL
, drawQuadTree tBR
, nodeBox p size nodeColor
]
QLeaf p size _elems ->
nodeBox p size leafColor
QNil (_x0, _y0) _size ->
Blank
nodeBox :: (Float, Float) -> Float -> Color -> Picture
nodeBox (x0, y0) size colr =
Color colr $
Translate x0 y0 $
rectangleWire (size * 2) (size * 2)
-- Make a circle of radius r consisting of n lines.
circleFilled :: Float -> Float -> Picture
circleFilled r n =
Scale r r $
Polygon (circlePoints n)
-- A list of n points spaced equally around the unit circle.
circlePoints :: Float -> [(Float, Float)]
circlePoints n =
map
(\d -> (cos d, sin d))
[0, 2 * pi / n .. 2 * pi]