dph-examples-0.6.0.1: examples/real/NBody/MainGloss.hs
{-# LANGUAGE ParallelListComp, BangPatterns #-}
import Gloss.MainArgs
import Gloss.Draw
import Gloss.Config
import Common.Dump
import Common.World
import Common.Body
import Common.Util
import Solver
import Timing
import Points2D.Generate
import Graphics.Gloss
import Graphics.Gloss.Interface.Simulate
import System.Environment
import System.Console.ParseArgs
import System.IO.Unsafe
import Control.Monad
import Data.Maybe
import qualified Data.Vector.Unboxed as V
main :: IO ()
main
= do args <- parseArgsIO ArgsComplete mainArgs
when (gotArg args ArgHelp)
$ usageError args ""
mainWithArgs args
mainWithArgs :: Args MainArg -> IO ()
mainWithArgs args
= let config = loadConfig args
-- The solver we're using to calculate the acclerations.
solverName = configSolverName config
calcAccels = fromMaybe (error $ unlines
[ "unknown solver " ++ show solverName
, "choose one of " ++ (show $ map fst solvers) ])
$ lookup solverName solvers
-- Setup initial world
vPoints = genPointsDisc
(configBodyCount config)
(0, 0)
(configStartDiscSize config)
vBodies = V.map (setStartVelOfBody $ configStartSpeed config)
$ V.map (setMassOfBody $ configBodyMass config)
$ V.map (uncurry unitBody)
$ vPoints
worldStart = World
{ worldBodies = vBodies
, worldSteps = 0 }
in case configWindowSize config of
Just windowSize -> mainGloss config calcAccels worldStart windowSize
Nothing -> mainBatch config calcAccels worldStart
-- | Run the simulation in a gloss window.
mainGloss
:: Config
-> Solver -- ^ Fn to calculate accels of each point.
-> World -- ^ Initial world.
-> Int -- ^ Size of window.
-> IO ()
mainGloss config calcAccels worldStart windowSize
= let draw = drawWorld (configShouldDrawTree config)
advance _viewport time world
= let world' = advanceWorld
(calcAccels $ configEpsilon config)
(configTimeStep config)
world
-- if we've done enough steps then bail out now.
in case configMaxSteps config of
Nothing -> world'
Just maxSteps
| worldSteps world' < maxSteps -> world'
-- Gloss doesn't provide a clean way to end the animation...
| otherwise
-> unsafePerformIO (mainEnd (configDumpFinal config) world' >> (error $ "done"))
`seq` error "advanceWorld: we're finished, stop calling me."
in simulate
(InWindow "Barnes-Hut" -- window name
(windowSize, windowSize) -- window size
(10, 10)) -- window position
black -- background color
(configRate config) -- number of iterations per second
worldStart -- initial world
draw -- fn to convert a world to a picture
advance -- fn to advance the world
-- | Run the simulation in batch mode, not displaying anything to the screen.
mainBatch
:: Config
-> Solver -- ^ Fn to calculate accels of each point.
-> World -- ^ Initial world.
-> IO ()
mainBatch config calcAccels worldStart
= do
worldStart `seq` return ()
(world', tElapsed)
<- time
$ let world = mainBatchRun config calcAccels worldStart
in world `seq` return world
putStr $ prettyTime tElapsed
mainEnd (configDumpFinal config) world'
mainBatchRun config calcAccels worldStart
= go worldStart
where go !world
= let world' = advanceWorld
(calcAccels $ configEpsilon config)
(configTimeStep config)
world
in case configMaxSteps config of
Nothing -> go world'
Just maxSteps
| worldSteps world' < maxSteps -> go world'
| otherwise -> world'
-- | Called at end of run to dump final world state.
mainEnd
:: Maybe FilePath -- ^ Write final bodies to this file.
-> World -- ^ Final world state.
-> IO ()
mainEnd mDumpFinal world
= do -- Dump the final world state to file if requested.
maybe (return ()) (dumpWorld world) mDumpFinal