minilight-0.1.0: examples/boids.hs
{-# LANGUAGE OverloadedStrings #-}
import MiniLight
import Control.Monad.State
import qualified Data.Vector as V
import qualified Data.Vector.Mutable as VM
import Lens.Micro
import Linear (_x, _y)
import qualified System.Random.MWC as Random
import qualified SDL.Vect as Vect
average :: (Fractional a, Num a) => V.Vector a -> a
average xs = (/ fromIntegral (V.length xs)) $ V.sum xs
data Object = Object {
position :: Vect.V2 Double,
velocity :: Vect.V2 Double
}
distance :: Object -> Object -> Double
distance o1 o2 = Vect.distance (position o1) (position o2)
data Game = Game {
objects :: VM.IOVector Object,
pic :: Figure
}
numberOfObjects = 100
width :: Int
width = 800
height :: Int
height = 600
mainloop :: StateT Game MiniLight ()
mainloop = do
Game { objects = objects, pic = pic } <- get
lift $ (renders =<<) $ forM [0 .. VM.length objects - 1] $ \i -> do
object <- liftIO $ VM.read objects i
return
$ translate (fmap floor $ position object)
$ rotate (let Vect.V2 x y = velocity object in pi - atan2 x y)
$ pic
lift $ forM_ [0 .. VM.length objects - 1] $ \i -> do
object <- liftIO $ VM.read objects i
let position' = position object + 0.2 Vect.*^ velocity object
let Vect.V2 vx vy = velocity object
liftIO $ VM.write objects i $ do
object
{ position = position'
, velocity = Vect.V2
( if position' ^. _x < 0
then abs vx
else if position' ^. _x > fromIntegral width then -abs vx else vx
)
( if position' ^. _y < 0
then abs vy
else if position' ^. _y > fromIntegral height then -abs vy else vy
)
}
objs <- liftIO $ V.freeze objects
lift $ forM_ [0 .. VM.length objects - 1] $ \i -> do
let radius = 20
let neighbors = V.filter
( \o ->
0 < distance o (objs V.! i) && distance o (objs V.! i) < radius
)
objs
object <- liftIO $ VM.read objects i
when (not $ null neighbors) $ do
-- separation
let v1 = average $ V.map
( \n ->
let diff = position object - position n
in (1 / Vect.dot diff diff) Vect.*^ diff
)
neighbors
-- alignment
let v2 = average $ V.map velocity neighbors
-- cohesion
let center = average $ V.map position neighbors
let v3 = (1 / radius / radius) Vect.*^ (center - position object)
liftIO $ VM.write objects i $ object
{ velocity = Vect.normalize $ velocity object + v1 + v2 + v3
}
main :: IO ()
main = do
vec <- Random.withSystemRandom . Random.asGenIO $ \gen ->
Random.uniformVector gen numberOfObjects :: IO
(V.Vector (Double, Double, Double, Double))
objs <- V.thaw $ V.map
( \(p1, p2, v1, v2) -> Object
( fmap fromIntegral $ Vect.V2
(floor (p1 * fromIntegral width) `mod` width)
(floor (p2 * fromIntegral height) `mod` height)
)
(Vect.normalize $ Vect.V2 v1 v2)
)
vec
runLightT id $ do
pic <- triangleOutline (Vect.V4 100 100 100 255) (Vect.V2 10 20)
runMainloop
(defConfig { appConfigFile = Nothing, additionalComponents = [] })
(Game {objects = objs, pic = pic})
(\_ -> execStateT mainloop)