packages feed

gloss-examples-1.12.0.0: raster/Mandel/Solver.hs

{-# LANGUAGE BangPatterns        #-}
{-# LANGUAGE PatternGuards       #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Solver
        ( mandelPicture
        , mandelArray
        , f2d
        , d2f)
where
import Graphics.Gloss.Raster.Array
import Graphics.Gloss.Data.Bitmap
import Graphics.Gloss.Data.Picture
import Data.Word
import System.IO.Unsafe
import Unsafe.Coerce
import Data.Bits
import GHC.Float
import Data.Array.Repa                          as R
import Data.Array.Repa.Repr.ForeignPtr          as R
import Data.Array.Repa.Repr.HintInterleave      as R
import Data.Array.Repa.Algorithms.ColorRamp     as R
import Prelude                                  as P


mandelPicture
        :: Int          -- Window Size X
        -> Int          -- Window Size Y
        -> Int          -- Pixels X
        -> Int          -- Pixels Y
        -> Double       -- Offset X
        -> Double       -- Offset Y
        -> Double       -- zoom
        -> Double       -- radius
        -> Int          -- iterations
        -> Picture
mandelPicture winX winY pixelsX pixelsY offX offY zoom radius iters
 = let  scaleX  :: Double = 1
        scaleY  :: Double = fromIntegral winY / fromIntegral winX
   in   makePicture
                winX winY
                pixelsX pixelsY
                (mandelPixel scaleX scaleY offX offY zoom iters radius)
{-# NOINLINE mandelPicture #-}


mandelArray
        :: Int          -- Window Size X
        -> Int          -- Window Size Y
        -> Double       -- Offset X
        -> Double       -- Offset Y
        -> Double       -- zoom
        -> Double       -- radius
        -> Int          -- iterations
        -> IO (Array U DIM2 (Word8, Word8, Word8))

mandelArray winX winY offX offY zoom radius iters
 = let  scaleX  :: Double = 1
        scaleY  :: Double = fromIntegral winY / fromIntegral winX

        arr     = makeFrame winX winY 1 1
                $ mandelPixel scaleX scaleY offX offY zoom iters radius

   in   R.computeP arr
{-# NOINLINE mandelArray #-}


-- Picture --------------------------------------------------------------------
makePicture
        :: Int                  -- Window Size X
        -> Int                  -- Window Size Y
        -> Int                  -- Pixels X
        -> Int                  -- Pixels Y
        -> (Double -> Double -> Color)
        -> Picture
makePicture !winSizeX !winSizeY !zoomX !zoomY !makePixel
 = let  -- Size of the raw image to render.
        sizeX = winSizeX `div` zoomX
        sizeY = winSizeY `div` zoomY

        {-# INLINE conv #-}
        conv (r, g, b)
         = let  r'      = fromIntegral r
                g'      = fromIntegral g
                b'      = fromIntegral b
                a       = 255

                !w      =   unsafeShiftL r' 24
                        .|. unsafeShiftL g' 16
                        .|. unsafeShiftL b' 8
                        .|. a
           in   w

   in unsafePerformIO $ do

        -- Define the image, and extract out just the RGB color components.
        -- We don't need the alpha because we're only drawing one image.
        (arrRGB :: Array F DIM2 Word32)
                <- R.computeP
                $ R.map conv
                $ makeFrame winSizeX winSizeY zoomX zoomY makePixel

        -- Wrap the ForeignPtr from the Array as a gloss picture.
        let picture
                = Scale (fromIntegral zoomX) (fromIntegral zoomY)
                $ bitmapOfForeignPtr
                        sizeX sizeY     -- raw image size
                        (BitmapFormat BottomToTop PxABGR)
                        (R.toForeignPtr $ unsafeCoerce arrRGB)
                                        -- the image data.
                        False           -- don't cache this in texture memory.

        return picture
{-# INLINE makePicture #-}


-- Frame ----------------------------------------------------------------------
makeFrame
        :: Int                  -- Window Size X
        -> Int                  -- Window Size Y
        -> Int                  -- Pixels X
        -> Int                  -- Pixels Y
        -> (Double -> Double -> Color)
        -> Array (I D) DIM2 (Word8, Word8, Word8)

makeFrame !winSizeX !winSizeY !zoomX !zoomY !makePixel
 = let  -- Size of the raw image to render.
        sizeX = winSizeX `div` zoomX
        sizeY = winSizeY `div` zoomY

        fsizeX, fsizeY  :: Double
        !fsizeX          = fromIntegral sizeX
        !fsizeY          = fromIntegral sizeY

        fsizeX2, fsizeY2 :: Double
        !fsizeX2        = fsizeX / 2
        !fsizeY2        = fsizeY / 2

        -- Midpoint of image.
        midX, midY :: Int
        !midX           = sizeX `div` 2
        !midY           = sizeY `div` 2

        {-# INLINE pixelOfIndex #-}
        pixelOfIndex (Z :. y :. x)
         = let  x'      = fromIntegral (x - midX) / fsizeX2
                y'      = fromIntegral (y - midY) / fsizeY2
           in   makePixel x' y'

   in   R.hintInterleave
         $ R.map unpackColor
         $ R.fromFunction (Z :. sizeY  :. sizeX)
         $ pixelOfIndex
{-# INLINE makeFrame #-}


-- Mandel ---------------------------------------------------------------------
mandelPixel
        :: Double               -- Scale X
        -> Double               -- Scale Y
        -> Double               -- Offset X
        -> Double               -- Offset Y
        -> Double               -- Zoom
        -> Int                  -- iterations
        -> Double               -- max radius
        -> Double               -- X (Real)
        -> Double               -- Y (Imaginary)
        -> Color
mandelPixel scaleX scaleY x0 y0 zoom cMax rMax x y
 = let
        !x'     = x0 + x * zoom * scaleX
        !y'     = y0 + y * zoom * scaleY

        !count  = mandelRun (fromIntegral cMax) rMax x' y'
        !v      = fromIntegral count / fromIntegral cMax

        color'
         | v > 0.99     = rgb' 0 0 0
         | (r, g, b)    <- rampColorHotToCold 0 1 v
         = rgb' r g b
   in   color'
{-# INLINE mandelPixel #-}


mandelRun :: Int -> Double -> Double -> Double -> Int
mandelRun countMax rMax cr ci
 = go cr ci 0
 where
  go :: Double -> Double -> Int -> Int
  go !zr !zi  !count
   | count >= countMax                 = count
   | sqrt (zr * zr + zi * zi) > rMax   = count

   | otherwise
   = let !z2r     = zr*zr - zi*zi
         !z2i     = 2 * zr * zi
         !yr      = z2r + cr
         !yi      = z2i + ci
     in  go yr yi (count + 1)
{-# INLINE mandelRun #-}


-- Conversion -----------------------------------------------------------------
f2d :: Float -> Double
f2d = float2Double
{-# INLINE f2d #-}


d2f :: Double -> Float
d2f = double2Float
{-# INLINE d2f #-}


-- | Float to Word8 conversion because the one in the GHC libraries
--   doesn't have enout specialisations and goes via Integer.
word8OfFloat :: Float -> Word8
word8OfFloat f
        = fromIntegral (truncate f :: Int)
{-# INLINE word8OfFloat #-}


unpackColor :: Color -> (Word8, Word8, Word8)
unpackColor c
        | (r, g, b, _) <- rgbaOfColor c
        = ( word8OfFloat (r * 255)
          , word8OfFloat (g * 255)
          , word8OfFloat (b * 255))
{-# INLINE unpackColor #-}