Animating Mandelbrot fractals with Silverlight 3 WriteableBitmap

Use the new WriteableBitmap class in Silverlight 3 Beta to render an animated zooming Mandelbrot Set fractal image.

I've been a student of fractal geometry for many years. Benoit Mandelbrot is considered the "father" of modern fractals. When his book "The Fractal geometry of Nature" was published in 1982, I bought a copy. This is a coffee-table style book with a huge amount of information about the history of fractals, mathematical formulae, and is loaded with stunning color pictures of computer -generated fractals of all types. To this day, it remains the seminal work on fractal geometry.

Fractals are realistic and useful models of many phenomena in the real world. Natural fractals include the shapes of mountains, coastlines and river basins; the structures of plants, blood vessels and lungs; the clustering of galaxies; and Brownian motion. Fractals are found in human pursuits, such as music, painting, architecture, and stock market prices.

In Silverlight 3, we have the addition of the WriteableBitmap class, which enables the manipulation of the pixels in an image, as well as to create transforms that can be used to create reflections and similar effects. Even with some complex number math and many iterations involved, this class will render a bitmap of say 480 X 360 pixels of the computed Mandelbrot Set in around 120 milliseconds, making it a candidate for animation of small bitmaps in Silverlight.

To test this out, I took some C# Mandelbrot Set code and put it into an animation timeline generated through the use of the DispatcherTimer. Each time the timer Tick event fires, I increment the viewport dimensions by a small amount and regenerate the corresponding view of the Mandelbrot Set. This results in an animation that appears to the user that you are slowly zooming away from the original subset view until after a minute or so you can see the entire Mandelbrot Set mathematical domain.

This is Silverlight 3 Beta code, so I can't provide a live sample. But if you're getting into Silverlight 3, you can download the working solution below and play around with it.

Here is the class in its entirety:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Media.Imaging;
using System.Windows.Threading;

namespace WriteableBitmap
{
    public partial class MainPage : UserControl
    {
         private System.Windows.Threading.DispatcherTimer tmr = new DispatcherTimer();
      
         private int _max = 30;
        private int _escape = 20;

        double rmin = -.75;
        double rmax = -.46;
        double imin = -.65;
        double imax = -.50;
        public MainPage()
        {
            InitializeComponent();
            tmr.Interval = TimeSpan.FromTicks(0);
            tmr.Tick += new EventHandler(tmr_Tick);
             tmr.Start();
         }

        void tmr_Tick(object sender, EventArgs e)
        {
            rmin -= .002;
            rmax += .002;
            imin -= .002;
            imax += .002;

             DrawMandelbrotSet(this.image1, rmin, rmax, imin, imax, 480,360);
         }

        private void DrawMandelbrotSet(Image image, double rmin, double rmax,
                                      double imin, double imax, int width, int height)
        {
         // uncomment next line and 3 lines at bottom to see rendering time in output window
          //  DateTime start = DateTime.Now;
            // Silverlight 2 - you can use Joe Stegman's WriteableBitmap class separately
            System.Windows.Media.Imaging.WriteableBitmap bitmap =
                 new System.Windows.Media.Imaging.WriteableBitmap(width, height, PixelFormats.Bgr32);
            bitmap.Lock();
            
            double dr = (rmax - rmin) / (width - 1);
             double di = (imax - imin) / (height - 1);

             for (int x = 0; x < width; x++)
             {
                 double cr = rmin + (x * dr);
                 for (int y = 0; y < height; y++)
                 {
                      double ci = imin + (y * di);
                     double zr = cr;
                     double zi = ci;
                     int count = 0;

                     while (count < _max)
                     {
                          double zr2 = zr * zr;
                          double zi2 = zi * zi;

                          if (zr2 + zi2 > _escape)
                        {
                            bitmap[(y*width) + x] = (int) Math.Pow(count + 1, 5) % int.MaxValue;
                           break;
                        }
                        zi = ci + (2.0 * zr * zi);
                        zr = cr + zr2 - zi2;
                          count++;
                     }

                     if (count == _max)
                        bitmap[(y * width) + x] = 0; // Black
                }
            }
            
           bitmap.Invalidate();
            bitmap.Unlock();
            image1.Source = bitmap;
         //   DateTime end = DateTime.Now;
          //  TimeSpan elapsed = end - start;
           // System.Diagnostics.Debug.WriteLine( elapsed.TotalMilliseconds.ToString() );
        }

     }
}

Here's a small screen cap of the view when it first starts animating:



And here is the same image after it has been zooming out for 30 seconds or so:



If you want to play around with the speed and resolution, try changing the _max and _escape values.  By the way, this is a real memory hog, it may be leaky: don't be surprised if you get out of memory errors from "playing too hard"!

You can download the Visual Studio 2008 Silverlight 3 Beta Solution here.

By Peter Bromberg   Popularity  (3531 Views)