gloss-examples-1.7.5.1: raster/Fluid/src-repa/Model.hs
{-# LANGUAGE ScopedTypeVariables, BangPatterns #-}
module Model
( Delta
, Rate
, Field
, DensityField
, VelocityField
, SourceDensity (..)
, CurrentButton (..)
, Model (..)
, initModel
, pictureOfModel
, pixel32OfDensity
, pixel8OfDensity
, outputBMP
, outputPPM)
where
import Data.Array.Repa as R
import Data.Array.Repa.IO.BMP as R
import Data.Array.Repa.Repr.ForeignPtr as R
import Data.Bits
import Data.Word
import Unsafe.Coerce
import Prelude as P
import qualified Graphics.Gloss as G
-- | Time delta (seconds)
type Delta = Float
-- | Time rate (or period) (1/seconds)
type Rate = Float
-- | A 2d field.
type Field a
= Array U DIM2 a
-- | Scalar density field.
type DensityField
= Field Float
-- | Vector velocity field.
type VelocityField
= Field (Float, Float)
-- | Button being pressed by the user.
data CurrentButton
= LeftButton
| RightButton
| None
-- | A density source added by the user.
data SourceDensity a
= SourceDensity DIM2 a
-- Model ----------------------------------------------------------------------
-- | The world model.
data Model
= Model
{ densityField :: DensityField
, densitySource :: Maybe (SourceDensity Float)
, velocityField :: VelocityField
, velocitySource :: Maybe (SourceDensity (Float, Float))
, clickLoc :: Maybe (Int, Int)
, stepsPassed :: Int
, currButton :: CurrentButton
, drawVelocity :: Bool
}
-- | Creates an initial blank model
initModel
:: Array U DIM2 Float
-> Array U DIM2 (Float, Float)
-> Model
initModel density velocity
| extent density /= extent velocity
= error "Fluid: initModel density and velocity extents do not match"
| otherwise
= Model
{ densityField = density
, densitySource = Nothing
, velocityField = velocity
, velocitySource = Nothing
, clickLoc = Nothing
, stepsPassed = 0
, currButton = None
, drawVelocity = False }
{-# INLINE initModel #-}
-- Picture --------------------------------------------------------------------
-- | Function to convert the Model into a Bitmap for displaying in Gloss
pictureOfModel :: Monad m => (Float, Float) -> Model -> m G.Picture
pictureOfModel (scaleX, scaleY) m
= let (Z :. height' :. width') = R.extent $ densityField m
width = fromIntegral width'
height = fromIntegral height'
in do
(arrDensity :: Array F DIM2 Word32)
<- computeP $ R.map pixel32OfDensity $ densityField m
let picVel :: G.Picture
picVel
| drawVelocity m
= G.Translate (- width / 2) (- height / 2)
$ G.Color (G.light $ G.light G.red)
$ G.Pictures
[ G.Line [(xf, yf), (xf + vx', yf + vy')]
| x <- [0, 5 .. width' - 1]
, y <- [0, 5 .. height' - 1]
, let xf = fromIntegral x
, let yf = fromIntegral y
, let (vx0, vy0) = velocityField m R.! (Z :. y :. x)
, let vx' = vx0 * 100
, let vy' = vy0 * 100 ]
| otherwise
= G.blank
let picDens :: G.Picture
picDens = G.bitmapOfForeignPtr width' height'
(R.toForeignPtr $ unsafeCoerce arrDensity)
False
return $ G.Scale scaleX scaleY
$ G.pictures [picDens, picVel ]
{-# NOINLINE pictureOfModel #-}
-- | Converts Float value to Word32 for pixel data
pixel32OfDensity :: Float -> Word32
pixel32OfDensity f
= let !fsat
| f < 0 = 0
| f >= 1 = 1
| otherwise = f
!x = truncate $ fsat * 255
!a = 255
in unsafeShiftL x 24 .|. unsafeShiftL x 16
.|. unsafeShiftL x 8 .|. a
{-# INLINE pixel32OfDensity #-}
-- | Converts Float value to a tuple of pixel components.
pixel8OfDensity :: Float -> (Word8, Word8, Word8)
pixel8OfDensity f
= let !fsat
| f < 0 = 0
| f >= 1 = 1
| otherwise = f
!x = truncate $ fsat * 255
in (x, x, x)
{-# INLINE pixel8OfDensity #-}
-- Dump -----------------------------------------------------------------------
-- Writes bitmap data to test batch-mode ran correctly
outputBMP :: FilePath -> Int -> DensityField -> IO ()
outputBMP path step df
= do arr <- computeUnboxedP $ R.map pixel8OfDensity df
let step' = replicate (6 - length (show step)) '0' P.++ show step
R.writeImageToBMP (path P.++ step' P.++ ".bmp") arr
outputPPM :: Int -> String -> Float -> Array U DIM2 Float -> IO ()
outputPPM step name scale df
= do let (Z :. h :. w) = extent df
let step'
= replicate (4 - length (show step)) '0' P.++ show step
let getVal x y
= let v = df R.! (Z :. y :. x)
in truncate (v * 255 * scale) :: Int
let showVal x y
= let v = getVal x y
in replicate (4 - length (show v)) ' ' P.++ show v
let mx
= maximum [getVal x y | x <- [0..w-1], y <- [0..h-1]]
let out = unlines $
[ "P2"
, show h P.++ " " P.++ show w
, show mx]
P.++ [ concat [ showVal x y P.++ " "
| x <- [0..w - 1]]
| y <- [h - 1, h - 2 .. 0]]
writeFile ("out/" P.++ step' P.++ "-" P.++ name P.++ ".ppm") out