Time for action – implementing the algorithm

Let's create a MandelbrotGenerator object in a new file named mandelbrotGenerator.js. This object will implement the algorithm that generates the Mandelbrot. The constructor takes the canvas width and height, and the bounds of the Mandelbrot:

function MandelbrotGenerator(canvasWidth, canvasHeight, left, top,right, bottom)
    {

Next we define the variables that the algorithm uses:

    var scalarX = (right - left) / canvasWidth,
        scalarY = (bottom - top) / canvasHeight,
        maxIterations = 1000,
        abort = false,
        inSetColor = { r: 0x00, g: 0x00, b: 0x00 },
        colors = [ /* array of color objects */ ];

The scalarX and scalarY variables are used to convert the Mandelbrot coordinates to canvas coordinates. They are computed by dividing the width or height of the Mandelbrot by the width or height of the canvas. For example, while the canvas may be set to 640 by 480 pixels, the bounds of the Mandelbrot may be something like (-2, -2) for top left and (2, 2) for bottom right. In this case the Mandelbrot height and width are both 4:

Time for action – implementing the algorithm

Next we set the maximum number of iterations for the algorithm to 1000. If you set it higher you will get better results but it will take longer to compute. Using 1000 provides a good middle ground between processing time and acceptable results. The abort variable is used to stop the algorithm. The inSetColor variable controls what color pixels that are in the Mandelbrot set get colored. We set it to black. Finally there is an array of colors that will get used to color pixels that aren't in the set.

Let's write those methods to convert canvas coordinates to Mandelbrot coordinates first. They simply multiply the position by the scalar and add the top or left offset:

function getMandelbrotX(x)
{
    return scalarX * x + left;
}
function getMandelbrotY(y)
{
    return scalarY * y + top;
}

Now let's define the main loop of the algorithm in a public method named draw(). It takes the image data from a canvas to draw on as a parameter:

this.draw = function(imageData)
{
    abort = false;
    
    for (var y = 0; y < canvasHeight; y++)
    {
        var my = getMandelbrotY(y);
        for (var x = 0; x < canvasWidth; x++)
        {
            if (abort) return;
            var mx = getMandelbrotX(x);
            var iteration = getIteration(mx, my);
            var color = getColor(iteration);
            setPixel(imageData, x, y, color);
        }
    }
};

In the outer loop we iterate over all of the rows of pixels in the canvas. Inside this loop we call getMandelbrotY(), passing in the canvas y-position and get back the corresponding y-position in the Mandelbrot.

Next we iterate over all of the pixels in the row. For each pixel we:

  1. Call getMandelbrotX(), passing in the canvas x-position and get back the corresponding x-position in the Mandelbrot.
  2. Call getIterations(), passing in the Mandelbrot x and y positions. This method is where it will find the number of iterations it takes to reach the escape condition.
  3. Call getColor(), passing in the number of iterations. This method gets the color for the number of iterations.
  4. Finally we call setPixel(), passing in the image data, x and y positions, and the color.

Let's implement the getIterations() method next. This is where we determine if the pixel is within the Mandelbrot set or not. It takes the Mandelbrot x and y positions as parameters:

function getIterations(x0, y0)
{
    var x = 0,
        y = 0,
        iteration = 0;
    do
    {
        iteration++;
        if (iteration >= maxIterations) return -1;
        var xtemp = x * x - y * y + x0;
        y = 2 * x * y + y0;
        x = xtemp;
    }
    while (x * x + y * y < 4);

    return iteration;
}

First we initialize working x and y positions to zero and the iteration counter to zero. Next we start a do-while loop. Inside the loop we increment the iteration counter and if it is more than maxIterations we return -1. This signals that the escape condition was not met and the point is inside the Mandelbrot set.

Next we compute the x and y variables for checking the escape condition. Then we check the condition to determine whether to continue with the loop. Once the escape condition has been met we return the number of iterations it took to find it.

Now we will write the getColor() method. It takes the iteration count as a parameter:

function getColor(iteration)
{
    if (iteration < 0) return inSetColor;
    return colors[iteration % colors.length];
}

If the iteration parameter is less than zero it means it's in the Mandelbrot set and we return the inSetColor object. Otherwise we look up the color object in the colors array by using the modulus operator to constrain the number of iterations to the length of the array.

Finally we will write the setPixel() method. It takes the image data, canvas x and y positions, and the color:

function setPixel(imageData, x, y, color)
{
    var d = imageData.data;
    var index = 4 * (canvasWidth * y + x);
    d[index] = color.r;
    d[index + 1] = color.g;
    d[index + 2] = color.b;
    d[index + 3] = 255; // opacity
}

This should look very familiar from Chapter 5, Not So Blank Canvas, where we learned how to manipulate image data. First we find the index of the pixel in the image data array. Then we set each of the color channels from the color object and set the opacity to the maximum value of 255.

What just happened?

We implemented the algorithm to draw a Mandelbrot to a canvas's image data. Each pixel is set to either black if it's in the Mandelbrot set or some color depending on how many iterations it took to find the escape condition.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.134.90.44