gloss-examples-1.11.1.1: picture/Gravity/Main.hs
import Graphics.Gloss
import Graphics.Gloss.Interface.Environment
import System.Random
-- x, y, dx, dy
type Particle
= (Float, Float, Float, Float)
main :: IO ()
main
= do g <- getStdGen
(width,height) <- getScreenSize
let initialstate = generateParticles g width height
simulate window background fps initialstate render update
where
window = FullScreen
background = black
fps = 60
render xs = pictures $ map particleImage xs
update _ = updateParticles
-- | Generates particles from StdGen
generateParticles :: StdGen -> Int -> Int -> [Particle]
generateParticles gen widthInt heightInt
= map (g . f) tups
where
-- change range [0,1] -> [-s/2,s/2]
f = \(x,y) -> (x * width - width / 2, y * height - height / 2)
-- add speed of 0
g = \(x,y) -> (x,y,0,0)
-- 200 Random float tuples
tups = take 50 $ zip randoms1 randoms2
randoms1 = randoms gen1 :: [Float]
randoms2 = randoms gen2 :: [Float]
(gen1,gen2) = split gen
width = toEnum widthInt
height = toEnum heightInt
-- | Particle to its picture
particleImage :: Particle -> Picture
particleImage (x,y,_,_)
= translate x y $ color white $ circleSolid 2
-- | To update particles for next frame
updateParticles :: Float -> [Particle] -> [Particle]
updateParticles dt
= (accelerateParticles dt) . (moveParticles dt)
-- | Moves particles based on their speed
moveParticles :: Float -> [Particle] -> [Particle]
moveParticles dt
= map (\(x,y,dx,dy) -> (x+dx*dt,y+dy*dt,dx,dy))
-- | Accelerates particles based on gravity
accelerateParticles :: Float -> [Particle] -> [Particle]
accelerateParticles dt ps
= map (gravitate ps dt) ps
-- | Given particles to be gravitating to and for how long,
-- updates a single particle's speed
gravitate :: [Particle] -> Float -> Particle -> Particle
gravitate [] _ p = p
gravitate ((x',y',_,_):ps) dt p@(x,y,dx,dy)
= -- To dodge divByZero or near divByZero anomalies
if separated x x' && separated y y'
then gravitate ps dt p'
else gravitate ps dt p
where
p' = (x,y,dx+ddx,dy+ddy)
ddx = dirx * g
ddy = diry * g
(dirx,diry) = direction (x,y) (x',y')
g = gravitation (x,y) (x',y')
-- | Normalized vector from one point to another.
direction :: (Float, Float) -> (Float, Float) -> (Float, Float)
direction (x,y) (x',y')
= (dx * scale', dy * scale')
where
dx = x' - x
dy = y' - y
scale' = 1 / sqrt (dx ^ (2 :: Int) + dy ^ (2 :: Int))
-- | Checks if floats not too close to each other
separated :: Float -> Float -> Bool
separated x y
= 0.001 < abs (x - y)
-- | Gravitational force of one particle to another
gravitation :: (Float,Float) -> (Float,Float) -> Float
gravitation (x,y) (x',y')
= g / sqrt (dx ^ (2 :: Int) + dy ^ (2 :: Int))
where dx = x' - x
dy = y' - y
g = 1