Drawing A Mandelbrot Set With Zend PDF

As you may know from some of my previous posts, I’ve been looking at Zend Framework’s PDF support.

As a bit of fun on the train home from work, I wondered if it would be possible to draw out a Mandelbrot Set using the PDF drawing functions in Zend_Pdf.

For those who don’t know what the Mandelbrot Set is, it is a fractal shape, and one of those projects every developer plays with at some point. It looks like my time has come here.

There is a reasonably simple formula for generating the image, we just need to iterate between a maximum and minimum x and y co-ordinate set and draw a pixel at each point when the number of iterations of the formula has reached a pre-defined number. In my example, I’m using 20 iterations, though you can go higher for greater accuracy. I won’t go over the formula, as it is explained in far greater depth than I have time for elsewhere on the internet.

In this code example, I’m using variables similar to those used in Mandelbrots formula, so they do trigger warnings with Zend Framework’s coding standards, but I think they make sense.

// load the Zend Framework Autoloader.
ini_set('include_path', './library');
require_once 'Zend/Loader/Autoloader.php';
$loader = Zend_Loader_Autoloader::getInstance();
$maxIterations = 20;
$minX = -2;
$maxX = 1;
$minY = -1;
$maxY = 1;
try {
  // create a new PDF document.
  $pdf = new Zend_Pdf();
  // create a new A4 sized page.
  $page = new Zend_Pdf_Page(Zend_Pdf_Page::SIZE_A4);
  // calculate the width and height of the image to generate.
  $width = $page->getWidth();
  $height = $page->getHeight();
  if ($width > $height) {
    $width = $height;
  } else {
    $height = $width;
  }
  // prepare a black colour we can reuse when we draw the mandelbrot set.
  $black = new Zend_Pdf_Color_Html('#000000');
  // iterate over the mandelbrot set and draw it out.
  for ($x=0; $x<=$width; $x++) {
    for ($y=0; $y<=$height; $y++) {
      $c1 = $minX + ($maxX - $minX) / $width * $x;
      $c2 = $minY + ($maxY - $minY) / $height * $y;
      $z1 = 0;
      $z2 = 0;
      for ($i=0; $i<$maxIterations; $i++) {
        $newZ1 = $z1 * $z1 - $z2 * $z2 + $c1;
        $newZ2 = 2 * $z1 * $z2 + $c2;
        $z1 = $newZ1;
        $z2 = $newZ2;
        if ($z1 * $z1 + $z2 * $z2 >= 4) {
          break;
        }
      }
      // if we reach max iterations, draw a black pixel
      if ($i >= $maxIterations) {
        $page->setFillColor($black)
             ->setLineColor($black)
             ->drawRectangle(
               $x,
               $y,
               $x+1,
               $y+1,
               Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE
             );
      }
    }
  }
  // add the page to the pdf document.
  $pdf->pages[] = $page;
  // save the finished PDF to a file
  $pdf->save('fractal.pdf');
  // catch any errors
} catch (Zend_Pdf_Exception $e) {
  die('PDF error: ' . $e->getMessage());
} catch (Exception $e) {
  die ('Error: ' . $e->getMessage());
}

You will need to run this code on the command line, and it will take a few seconds to execute. When complete, you will have a PDF document with the following image.

Mandelbrot set rendered from a PDF

We can refine this slightly to get a prettier picture if we show the number of iterations used in a different colour, instead of just showing black if we go over our limit of maximum iterations.

To do this, we’d need to prepare an associative array with a colour for each iteration. The easiest option is to use grayscale and Zend_Pdf_Color_GrayScale. After we work out the max width and height, insert the following code.

// prepare colours
for ($i=0; $i >= $maxIterations; $i++) {
  $grayLevel = $i / $maxIterations;
  $colours[$i] = new Zend_Pdf_Color_GrayScale($grayLevel);
}

Now we have an associative array to look up a different shade of gray from 0 to $maxIterations.

Instead of the code that checks if $maxIterations has been reached and then draw a black pixel, we can replace it with the following

$page->setLineColor($colours[$i])
     ->setFillColor($colours[$i])
     ->drawRectangle($x, $y, $x+1, $y+1, Zend_Pdf_Page::SHAPE_DRAW_FILL_AND_STROKE);

Run this modified script on the command line, and you’ll generate a PDF with the following image.

Mandelbrot set rendered from a PDF in grayscale

That image looks great, but not very colourful. I’ll cover how to get some colour in there another time.