Exploring the Mandelbrot Image

Read time: 25 minutes (6462 words)

Basically, the Mandelbrot images are generated by calculating an integer number for every point in a region that is approximately within a circle of radius two in the complex plane. The number produced by the calculation is used to generate some color for the graphics display. Sounds simple, but we have some work to do to generate the image.

Image Size

We start off by defining the size of the image we wish to draw, and the complex number we wish to see in the center of that display.

const int WINDOW_WIDTH = 600;
const int WINDOW_HEIGHT = 400;

double cx = -0.5;
double cy = 0.0;

We will call the point in the middle of the image the view point.

Initially want this view point to line up with some chosen spot in the complex space we are examining. If we initially want to see the entire world Mandelbrot discovered, that means a circle with a radius of 2, drawn in Mandelbrot space, needs to be visible in this window.

Let’s look at a bit of simple geometry math, because this kind of thing pops up all the time when you are working with graphics output. More and more, folks need to generate a bunch of data, then visualize those data values somehow, and your job will involve figuring all of the geometry out!

We will limit our work here to 2-dimensional displays, although the step to 3-dimensions is not that hard.

Coordinate Transformations

Basically, we have two different coordinate systems to deal with:

  • The graphics world - where our image will be generated
  • The complex world - where the raw data values live

Let’s use subscripts to indicate which world we are talking about. We will use g to indicate the graphics world, and c to indicate the complex world.

We will specify points in each world using conventional :math`(X,Y)` number pairs. (X moves along the horizontal axis, increasing to the right, and :math`Y` moves along the vertical axis, increasing upward.)

Here is the situation:

Coordinate Systems

Our graphics window is like a window through which we view the complex world. Coordinates in our graphics world are normally positive, and we put the origin of our graphics coordinate system at the lower left corner of our graphics window.

The origin of the complex world coordinate system is placed at the complex number 0+0i.

Figuring out where we “see” a point in the complex world in our graphics world coordinates is what we need to do!

We can slide that graphics windows back and forth to see different places in the complex world, and move it in and out to see larger or smaller portions of that complex world. Sliding is called translating, and moving in and out is called scaling or zooming.

Translating

Translating is pretty easy. All we are doing is adding or subtracting some number to convert one coordinate value into another one. Let’s use a :math`t` subscript to indicate the translation value.

Here are the formulas we need:

X_c = X_g - X_t \\
Y_c = Y_g - Y_t

You might need some help in visualizing these formulas. Let’s take that first one and explain it a bit:

The complex world will be drawn on a piece of paper we tape down on a table. The grphics world is a piece of glass we place on top of that paper. We will move the glass around over that paper, “translating it by some amount in each direction. If the value of X_t is positive, you moved the glass to the right by that amount. If the value of Y_t is positive, you moved the glass up.

Pick a point in the complex world, and mark that spot on the glass. If we slide the glass to the right by X_t, the original spot on the paper will seem to move to the left in our glass window. That means the coordinate we use on the glass will be smaller (more negative). Think it through until you have this down!

That was not so hard, was it?

Zooming

Zooming means we make the portion of the world we are looking at bigger or smaller. We will use s as our subscript for that transformation:

X_c = X_g / X_s \\
Y_c = Y_g / Y_s

Here, we are using “pixels” as our measurement in the graphics world, and the scale factor is also in pixels. We want to divide graphics world numbers by the scale factor to get to our complex world values.

Note

If we use different scale factors for X_s and Y_s we distort the view we see. Normally we will use the same value for both so the image looks “right”! We will use SF for that one value.

Combining these two sets of formulas gives us a general transformation scheme we can use to look into Mandelbrot’s world”

X_c = (X_g - X_t) / SF \\
Y_c = (Y_g - Y_t) / SF

It will be convenient to transform the other way: pick a point in Mandelbrot’s complex world, and figure out where in our graphics world` that point sits:

X_g = X_c * SF + X_t\\
Y_g = Y_c * SF + Y_t

Note

We will need to test these formulas to make sure things are right!

So, our view of Mandelbrot’s world is controlled by three user defined values:

  • X_t - how far we slide the viewpoint to the left or right (in pixels)
  • Y_t - how far we slide the viewpoint up or down (in pixels)
  • SF - how much we “zoom” in and out (in pixels)

Corner Coordinates

It will be handy to work out the formulas for the complex numbers at the corners of our graphics world, using these three numbers and the size of the graphics window.

Given:

  • (X_c,Y_c) - the point in the complex world we want to be in the middle of the graphics window
  • SF - the scaling we want to use (see below)
  • (X_w, Y_w) - the window width and height (always positive)

Find:

  • (X_c^{ul}, Y_x^{ul}) - the upper left point in the complex world
  • (X_c^{ur}, Y_c^{ur}) - the upper right point in the complex world
  • (X_c^{ll}, Y_c^{ll}) - the lower left point in the complex world
  • (X_c^{lr}, Y_c^{lr}) - the lower right point in the complex world

If we want to see the entire world Mandelbrot explored, we need to be able to see a circle of radius 2 displayed completely in the graphics window. Since the window height (Y_w) is the smallest of the two dimensions, that means we want an initial scale factor of Y_w / 2.

Additionally, Mandelbrot discovered that most of the interesting images were centered on a complex number of -0.5+0i, so we want our initial translation to be set so that spot is in the middle of our graphics window, at (X_g, Y_g) = (X_w/2, Y_w/2).

Here are the formulas we need. (I will leave it for you to check the math!)

X_g^{ul} = 0\\
Y_g^{ul} = Y_w\\
X_g^{ur} = X_w\\
Y_G^{ur} = Y_w\\
X_g^{ll} = 0\\
Y_g^{ll} = 0\\
X_g^{lr} = X_w\\
Y_g^{lr} = 0

These points map to the following complex coordinates:

X_t = X_w / 2
Y_t = Y_w / w
SF = Yw / 2

X_{ul}^c =  - (X_w / (2 * SF))

Y_{ul}^c = - (Y_w / (2 * SF))

User Interface

Now we are in a position to think about what we want to see when we view Mandelbrot’s world.

Initially, we will look at the entire world. Mandelbrot discovered an interesting view if he centered his viewpoint at (-0.5, 0.0):

../_images/first-image.png

Ideally, choosing all of these values is something the user of our program will be able to control!

double ul_x = -(WINDOW_WIDTH / (2 * scale)); double x2 = x1; double x3 = x1 + WINDOW_WIDTH / scale; double x4 = x3; double y1 = WINDOW_HEIGHT / (2 * scale)); double y2 = y1 - WINDOW_HEIGHT / scale); double y3 = y1 double y4 = y2