Silverlight Mandelbrot Zoomer
Written by Mike James   
Thursday, 12 August 2010
Article Index
Silverlight Mandelbrot Zoomer
Displaying the result
Using the byte array
Zooming
Final touches
Live Zoomer

Banner

Instead of working this way why not simply scan through the array in any order and work out the i,j value that the index corresponds to in the bitmap. Then work out the x,y value that the i,j value corresponds to in the Mandelbrot set. That is we can use the inverse storage mapping functions and just work our way through the byte array computing the pixel data at each location.

That is the location:

wbmap.Pixels[i] 

corresponds to the pixel at px,py in the bitmap as given by:

int py = i / s;
int px = i % s;

Notice that the division is integer division which truncates to the nearest lower integer. 

Now that we have the position in the bitmap we can convert this to the location in the Mandelbrot set. This is just a case of a simple scaling and translation to map the rectangle (0,0) (PixelWidth,PixelHeight) into the rectangle (-2.4, -1.5) (0.8, 1.5). A little algebra gives

x=px*(-1.5 + 2.4)/PixelWidth-2.4
y=0.8 - py*(1.5-0.8)/PixelHeight

If you are puzzled as to why the y expression seems to have an extra minus in it - the reason is that the bitmap y co-ordinate py increases down the screen but the usual y co-ordinate increases up the screen.

Of course these expressions are in terms of the particular rectangle that we are plotting. A more general expression can be written using the Rect area we defined earlier:

x=px*(area.Right - area.Left)/
PixelWidth-2.4
y=0.8 - py*area.Top - area.Bottom)/
PixelHeight

It also faster to remove anything that you can from inside the for loop that we can so first compute the scale factors:

double xscale = (area.Right - area.Left)/
 PixelWidth;
double yscale = (area.Top - area.Bottom)/
 PixelHeight;

Now we can start the for loop:

for (int i = 0; i < pixels.Length,i ++)
{

Notice that we are stepping through the array by the number of bytes needed to deal with one entire pixel each time through the loop. Although we could roll all of the co-ordinate transformation up into one big expression debugging is easier with them split into smaller logical units. First the pixel co-ordinates:

int py = i / s;
int px = i % s;

then the x,y co-ordinates from the pixel co-ordinates:

double x = area.Left + px * xscale;
double y = area.Top - py * yscale;

Now we have x,y it s possible to call the method that discovered if the point is in the Mandelbrot set or not:

Complex c = new Complex(x,y);
int count= mandelbrot(c);

The return value of count is going to use it to set the pixel's color.

How exactly to do this?

The four pixels starting at pixels[i] give the R, G, B, A components of the color respectively as byte values. Now we could use a method to map the count to a set of colors using a table say. However, given that we have been using so many storage mapping functions, we might as well use another one.

The idea is to think of the count, which varies from 1 to 1000, as 1000 different colors arranged in a line. Now consider the RGB color space as a cube of 10x10x10 different colors. The storage mapping function we need maps the 1000 linear colors to the rows, columns and layers of the RGB cube. More commonly the requirement is to map a 3D storage cube to a linear array but the inverse storage mapping functions map the cube to the array and this is what we need. A method to convert a count in the range 1 to 1000 to color values evenly spread across the RGB cube is:

private Color colorMap(int count)
{
 Color C = new Color();
 C.B =(byte) ( count/ 100 * 25);
 count = count % 100;
 C.G = (byte)(count / 10 * 25);
 C.R = (byte)(count % 10 * 25);
 C.A = 255;
 return C;
}

With this method we can complete the plotting of the Mandelbrot set by setting the pixel color values by packing each byte into the int32:

 Color C = colorMap(count);
wbmap.Pixels[i] = C.A << 24 |
C.R << 16 | C.G << 8 | C.B;
}

This also completes the for loop.

All that remains is to return the result:

 return wbmap;
}

The initial co-ordinates give a good view of the entire Mandelbrot set from which the user can pick an area to zoom in on. Initially the zoom area is set to the entire display, which means no zoom is applied. Finally notice that you do need a button called “Reset” on the form to activate this initialisation routine manually - and this is our next task.

zoom1

The initial view of the Mandelbrot set.

 

The reset button simply re-initialises the Rect and calls drawSet again:

private void button1_Click(object sender,
 RoutedEventArgs e)
{
 area = new Rect(new Point(-2.4, -1.5),
new Point(0.8, 1.5));
 image1.Source = drawSet(area);
}

If you now run the program you should now see the default view of the Mandelbrot set.

Banner

<ASIN:1430230606 >

<ASIN:1430225491 >

<ASIN:0672330628 >

<ASIN:1430219505 >

<ASIN:1430229888 >



Last Updated ( Monday, 30 October 2023 )