Welcome to McFunley.com Sign in | Join | Faq

Haskell Mandelbrot Set

posted on Tuesday, December 25, 2007 6:42 PM by mcfunley

Some people sing carols every XXX-mas, I get bored and write the Mandelbrot Set program in whatever my favorite language happens to be that year. I thought the brevity of the output this year (Haskell) was kinda neat.
import Graphics.UI.GLUT
import Control.Monad
import Data.Int
import Data.Complex

iterations = 400

x // y = fromIntegral x / fromIntegral y

-- Divides [a] into [[a], [a], ...] with each sublist of length n,
-- except the last sublist which has length <= n.
chunkify n [] = []
chunkify n xs = let (xs', rest) = splitAt n xs
                in xs' : chunkify n rest

-- Converts a coordinate in screen space to a vertex.
pix2vert (Size w h) (x, y) = Vertex2 ((3 // w * fromIntegral x) - 2.0)
                             ((2 // h * fromIntegral y) - 1.0)

-- List of all of the vertices that represent screen pixels.
vertices :: IO [Vertex2 GLfloat]
vertices = get windowSize >>= \(Size w h) ->
           return $ [pix2vert (Size w h) (x, y) | x <- [0..w-1], y <- [0..h-1]]

-- Gets the color for a number of iterations.
color3 r g b = Color3 r g b
getcolor :: Int -> Color3 Float
getcolor iter | iter == iterations = color3 0 0 0
              | otherwise          = color3 (amt*0.5) amt (amt*0.5)
              where amt = iter // iterations

-- Returns the number of iterations <= the maximum iterations of the
-- Mandelbrot set at the given vertex.
mandel (Vertex2 r i) = length . takeWhile (\z -> magnitude z <= 2) .
                       take iterations $ iterate (\z -> z^2 + (r :+ i)) 0

-- plots one point.
drawVert v = do color . getcolor $ mandel v
                vertex v

-- draws all the vertices in slices (to update the display while drawing).
display' chunks = do mapM_ (\vs -> do renderPrimitive Points $ mapM_ drawVert vs
                                      flush) chunks
                     displayCallback $= display

-- draws the whole fractal
display = do clear [ ColorBuffer ]
             displayCallback $= (vertices >>= display' . chunkify 256)
             get currentWindow >>= postRedisplay

main = do
   getArgsAndInitialize
   initialDisplayMode $= [ SingleBuffered, RGBMode ]
   initialWindowSize $= Size 1200 1024
   initialWindowPosition $= Position 100 100
   createWindow "Mandelbrot"
   clearColor $= Color4 0 0 0 0
   matrixMode $= Projection
   loadIdentity
   ortho (-2) 1 (-1) 1 (-1) 1
   displayCallback $= display
   mainLoop
Screenshot:



Dan McKinley does not claim to be a Haskell expert and definitely doesn't claim to be an expert about Haskell graphics rendering.

Comments

# re: Haskell Mandelbrot Set @ Saturday, January 05, 2008 9:47 PM

Hi there, I chanced on a previous blog entry of yours:

http://mcfunley.com/cs/blogs/dan/archive/2005/12/26/904.aspx

But the comments where closed, so I thought I'd leave it here:

Nice debugging skillz :-)

I recently had a similar problem with yours and the latest FF on Mac OS X, and I can verify that one of the biggest CPU hogs is JS_EvaluateUCScriptForPrincipals on my setup as well. I can also verify that I'm too lazy to look into the source and try to optimize it, and instead went for the Adblock plugin which took care of the JS nastyness that Wired includes on their pages. Now imagine 20+ Wired pages open at the same time. Every single one executing and recompiling the same block of JS code over and over. Who said that memory trampling and code sharing wasn't such a good idea?? :-) If only they'd take out each other, har har har.

Anyways, haven't tried Haskell yet, but your Mandelbrot code sure beats looking at the lame Wikipedia examples! Maybe I'll start with that one day!

Cheers

  Adonis

SKIN NAME : ImageHeader