gloss-examples-1.7.3.1: raster/Fluid/Model.hs
{-# LANGUAGE ScopedTypeVariables, BangPatterns #-}
module Model
( Delta
, Rate
, Field
, DensityField
, VelocityField
, Source (..)
, CurrentButton (..)
, Model (..)
, initModel
, pictureOfModel
, pixel32OfDensity
, pixel8OfDensity)
where
import Graphics.Gloss
import Data.Array.Repa as R
import Data.Array.Repa.Repr.ForeignPtr as R
import Data.Bits
import Data.Word
import Unsafe.Coerce
-- | 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 Source a
= Source DIM2 a
-- Model ----------------------------------------------------------------------
-- | The world model.
data Model
= Model
{ densityField :: DensityField
, densitySource :: Maybe (Source Float)
, velocityField :: VelocityField
, velocitySource :: Maybe (Source (Float, Float))
, clickLoc :: Maybe (Int, Int)
, stepsPassed :: Int
, currButton :: CurrentButton
}
-- | 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 }
{-# INLINE initModel #-}
-- Picture --------------------------------------------------------------------
-- | Function to convert the Model into a Bitmap for displaying in Gloss
pictureOfModel :: Monad m => (Float, Float) -> Model -> m Picture
pictureOfModel (scaleX, scaleY) m
= let (Z :. width' :. height') = R.extent $ densityField m
width = fromIntegral width'
height = fromIntegral height'
in do
(arrDensity :: Array F DIM2 Word32)
<- computeP $ R.map pixel32OfDensity $ densityField m
return $ Scale scaleX scaleY
$ bitmapOfForeignPtr width height
(R.toForeignPtr $ unsafeCoerce arrDensity)
False
{-# 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 #-}