packages feed

dph-examples-0.6.0.1: examples/real/NBody/Common/Body.hs

{-# LANGUAGE BangPatterns #-}

-- | Massful bodies in the simulation.
module Common.Body
	( Velocity
	, Accel
	, MassPoint
	, Body
	
	, accel

	, unitBody
	, massPointOfBody
	, setMassOfBody
	, setAccelOfBody
	, setStartVelOfBody
	, advanceBody)
where
import Common.Util


-- Types ----------------------------------------------------------------------
-- We're using tuples instead of ADTs so we can put them in unboxed vectors.

-- | The velocity of a point.
type Velocity	= (Double, Double)

-- | The acceleration of a point.
type Accel	= (Double, Double)

-- | A point in 2D space with its mass.
type MassPoint	= (Double, Double, Double)

-- | Bodies consist of a MassPoint, but also carry their velocity
--   and acceleration between steps of the simulation.
type Body	= (MassPoint, Velocity, Accel)


-- Acceleration ---------------------------------------------------------------
-- | Calculate the acceleration on a point due to some other point.
accel 	:: Double 	-- ^ Smoothing parameter.
	-> MassPoint	-- ^ The point being acclerated.
	-> MassPoint	-- ^ Neibouring point.
	-> Accel

{-# INLINE accel #-}
accel epsilon (!x1, !y1, _) (!x2, !y2, !m)  
 = (aabs * dx / r , aabs * dy / r)  
 where	dx   = x1 - x2 
	dy   = y1 - y2 
	rsqr = (dx * dx) + (dy * dy) + epsilon * epsilon
	r    = sqrt rsqr 
	aabs = m / rsqr 


-- Body -----------------------------------------------------------------------
-- | Make a body with unit mass and zero vel and acc.
unitBody :: Double -> Double -> Body
unitBody x y
	= ((x, y, 1), (0, 0), (0, 0))


-- | Take the MassPoint of a body.
massPointOfBody :: Body -> MassPoint
massPointOfBody (mp, vel, acc)	
	= mp


-- | Set the mass of a body.
setMassOfBody :: Double -> Body -> Body
setMassOfBody mass ((x, y, _), vel, acc)
	= ((x, y, mass), vel, acc)


-- | Set the acceleration of a body.
setAccelOfBody :: Accel -> Body -> Body
setAccelOfBody acc' (mp, vel, _)	
	= (mp, vel, acc')

	
-- | Set the starting velocity of a body.
--   It is set to rotate around the origin, with the speed proportional
--   to the sqrt of the distance from it. This seems to make nice simulations.
setStartVelOfBody :: Double -> Body -> Body
setStartVelOfBody startVel (mp@(x, y, mass), vel, acc)
 = let	pos		= (x, y)
	(x', y')	= normaliseV (x, y)
   	vel'		= (y', -x')
	vel''		= mulSV (sqrt (magV pos) * startVel) vel'
	
   in	(mp, vel'', acc)


-- | Advance a body forwards in time.
advanceBody :: Double -> Body -> Body
advanceBody time 
	( (px, py, mass) 
	, (vx, vy) 
	, acc@(ax, ay))

  =	( (px + time * vx, py + time * vy, mass)
	, (vx + time * ax, vy + time * ay)
	, acc)