views:

152

answers:

2

So I just wrote a little snippet to generate the Mandelbrot fractal and imagine my surprise when it came out all ugly and skewed (as you can see at the bottom). I'd appreciate a point in the direction of why this would even happen. It's a learning experience and I'm not looking for anyone to do it for me, but I'm kinda at a dead end debugging it. The offending generation code is:

module Mandelbrot where
import Complex
import Image

main = writeFile "mb.ppm" $ imageMB 1000

mandelbrotPixel x y = mb (x:+y) (0:+0) 0

mb c x iter | magnitude x > 2 = iter
            | iter >= 255     = 255
            | otherwise       = mb c (c+q^2) (iter+1)
    where q = x -- Mandelbrot
          -- q = (abs.realPart $ x) :+ (abs.imagPart $ x) --Burning Ship

argandPlane x0 x1 y0 y1 width height = [ (x,y) | 
        y <- [y1, y1 - dy .. y0], --traverse from
        x <- [x0, x0 + dx .. x1] ] --top-left to bottom-right
    where dx = (x1 - x0) / width
          dy = (y1 - y0) / height

drawPicture :: (a -> b -> c) -> (c -> Colour) -> [(a, b)] -> Image
drawPicture function colourFunction = map (colourFunction . uncurry function)

imageMB s = createPPM s s
        $ drawPicture mandelbrotPixel (replicate 3)
        $ argandPlane (-1.8) (-1.7) (0.02) 0.055 s' s'
    where s' = fromIntegral s

And the image code (which I'm fairly confident in) is:

module Image where

type Colour = [Int]
type Image = [Colour]

createPPM :: Int -> Int -> Image -> String
createPPM w h i = concat ["P3 ", show w, " ", show h, " 255\n",
    unlines.map (unwords.map show) $ i]

Ugly Mandelskew thing

+14  A: 

Well, the image is skewed because the dimensions are wrong, but that's obvious. You're specifying the image size and then spitting out a list of pixels, but with an incorrect number of pixels per line somewhere.

More specifically, note that the image wraps around almost exactly once: In other words, skew per line * height of the image = width of the image. Since the image is square, that means you're generating an extra pixel per line--a good old off-by-one error.

The obvious place for this to happen is when you're generating the coordinates to iterate on. Let's try a small set and see what it gives us:

> length $ argandPlane (-2.5) (-2) 1.5 2 10 10
121
> 10 ^ 2
100
> 11 ^ 2
121

And so. I suspect the error is because you're calculating the increment as real distance divided by pixel size, which generates the correct number of intervals, but an extra point. Consider the interval from 0.0 to 1.0. Using your calculation with a width of 4, we get:

> let x0 = 0.0
> let x1 = 1.0
> let width = 4.0
> let dx = (x1 - x0) / width
> dx
0.25
> let xs = [x0, x0 + dx .. x1]
> xs
[0.0, 0.25, 0.5, 0.75, 1.0]
> length xs
5

So, to get the correct number of points, just reduce the size by 1 when generating the the coordinates.

camccann
Couldn't ask for a better answer. Thank you very much sir.
Sean D
@Sean: If that is the case, then I think marking the answer as accepted is in order. :)
Onion-Knight
Of course using (width-1) makes much more sense, initially after reading your answer I just used tail
Sean D
Oh, and if anyone is interested, there's even a specific term for this mistake: a [fencepost error](http://en.wikipedia.org/wiki/Off-by-one_error#Fencepost_error).
camccann
+3  A: 

It's a learning experience and I'm not looking for anyone to do it for me, but I'm kinda at a dead end debugging it

I know camccann already solved your problem, but he kind of "gave you the fish" while "teaching you how to fish" could be more useful.

So I'll share what I believe could be a useful way of reaching the solution.

So your mandelbrot image is skewed. Some likely possible causes:

  • You have a bug in your mandelbrot formula
  • You have a bug in presenting/saving your picture

You could do an experiment to learn further if any of the above explanations are relevant or not. Such an experiment could be for example drawing trivial images of, say, horizontal and vertical lines.

After doing that experiement you will see that your vertical lines are not so vertical. Going back to the likely possible causes, it's clear that you have a bug in presenting/saving your image, and that explains everything. You may still have a bug in your mandelbrot formula but you probably don't, and that's not relevant to the issue at hand now.

Now you should ponder what kind of image saving bug will cause vertical lines to be diagonal. If no idea pops up you can make your simple example smaller and smaller until the PPM result becomes small enough that you could examine it by hand. Then you'll surely catch the bug.

yairchu
I tried to at least talk through the solution in roughly the sequence I followed to get to it myself, but was in a hurry and didn't spell it out in as much detail as you've done--thanks for doing so. In particular, noting the "one extra pixel per line" was how I narrowed it to coordinate generation, and the GHCi snippet was an experiment to test the hypothesis.
camccann
Thanks very much, I'll bear those things in mind next time I'm stuck. Between your answer and camccann's I think I got everything I could have wanted.
Sean D