CPSC170A Assignment 6
Mandelbrot Set

Due March 22nd, 5:00 PM

The Mandelbrot set is a set of complex numbers that remains bounded (does not increase to infinity) when iterated with a quadratic polynomial function. When the set is graphed on the complex plane, the set boundary has a complex pattern that does not simplify at any magnification and is therefore sometimes defined as a fractal.

Details

You will be using the ppm module to draw. Remember you can find the documentation here .

The first part of this assignment is the creation of a class that represents complex numbers. Complex numbers are of the form (a+bi), where i is the mathematical imaginary number. For Mandelbrot, you will be required to have three operations: addition, multiplication, and magnitude (euclidean distance from the origin for the complex number). We will discuss in class you you can add, multiply, and compute the magnitudes.

Create a program that draws an image of the Mandelbrot set for complex numbers in the range -2 > a, b < 2. To draw the set using PPM, you will need to convert image pixel x, y coordinates to complex a, b coordinates. For example, if the image is 200 by 200 pixels, the pixel (0, 0) would map to the complex coordinate (-2, -2) and the pixel (100, 100) to the complex coordinate (0, 0). For each pixel in the image set the color to a shade of gray depending on how many iterations of the quadratic polynomial function were required for the complex number's absolute value to exceed 2. The quadratic polynomial function can be defined recursively as:

cn = cn-12 + c0

Where c0 is an initial complex number, that corresponds to a pixel in the image, and cn is the nth complex number computed with the quadratic polynomial function. Note, since 255 is the largest number that can be represented with a gray-scale color, 255 is the maximum number of times that you need to iterate the quadratic polynomial function. For example, if the quadratic polynomial function is iterated 100 times before the absolute value becomes larger than 2, then set the pixel to be the color with the RGB values (100, 100, 100).

The real beauty of the Mandelbrot set is not the full image, but the inherent recursive structure that becomes apparent as you zoom in on pieces of the set. For this, you will want to write your functions that compute the set in such a way that the user can specify what locations to "zoom" in on within the image. Your program will read in the range in which to render the Mandelbrot set. For example:

python3 mandelbrot.py 0 0 4
python3 mandelbrot.py -1 -1 1
python3 mandelbrot.py center_x center_y size

The first line above will render the entire Mandelbrot set. The second command, however, will zoom in on the lower left portion of the set. it specifies that the center of the image is the complex coordinate (-1, -1), and the size of the plane is 1 unit wide. Notice that this is specification of the portion of the complex plane that is being rendered. Each generated image will be the same size.

In order to implement this, the program should store the range of complex numbers that are currently being rendered in instance variables. The program should also have a method that converts pixel coordinates to complex coordinates using the current complex range instance variables. The following equations will convert between pixels coordinates and complex complex coordinates in a specified range:

ca = px / W * (maxa - mina) + mina
cb = py / H * (maxb - minb) + minb

Where c is the complex coordinate that corresponds to the pixel coordinate, p, W and H are the width and height of the window, and min and max are the current complex range of values.

Make sure you play around with different generations of the Mandelbrot set. Include a copy of your favorite image with your code submission. Make it a good one, because the best image in the class (as selected by an on-line poll) will be hung on my office door, and displayed on the course website. For the purposes of the competition, you should generate large images, on the order of 1600 x 1600 pixels.

Feel free to look online for interesting coordinates to start looking at. However, any image submitted for this competition must be created with your program. Be prepared to demonstrate your program generating your image. That means you need to keep track of your locations you are generating images from.

Here are some starter locations, to help guide your searches, and test your zoom capabilities:

Submission: Submit your code as a zip file with your name as the zip file name on the course Inquire site.
Due Monday, March 22nd by 5:00 PM

Class Structure and Test Cases

Write down your class hierarchy in .py files. You should define what classes you need, what the attributes of those classes will be, and what methods each class will have. For each attribute, define the datatype being stored within, and the purpose of the attribute. For each method, define their Pre and Post conditions.

You are only required to provide test cases for the complex number class you are also required to write. For each method of the complex class, provide at least 1 test case that can be used to verify that your code meets the requirements. You can write all three test cases in a file called test_complex.py. Be sure to provide comments in this file, with the expected result of each test case.

Submission: Submit your files as a zip file with named First_NameLast_Name_Tests.zip, replacing First_NameLast_Name with your name.
Due Monday, March 18th by 10:00 pm

Extra

Color Map: It is possible to create more visually pleasing images by using a color map to specify what color maps to which escape value. It is possible, for example, to have a color map between colors with RGB values (100, 255, 150) and (200, 50, 0) by interpolating between the two colors based on the escape value. It is possible to interpolate by using the equation from lab, but use it with each of the color channels. For example, if the escape value is 128, then the interpolated color of the above two colors would be r = (100 * (1 - i)) + (200 * i), g = (255 * (1 - i)) + (50 * i), b = (150 * (1 - i)) + (0 * i), where i = 128 / 255.

It is also possible to produce a color map with more than two colors by first determining which pair of colors an escape value is between, and then interpolating the colors. For example, if a color map consists of three colors and the escape value is 42, then because 42 / 255 is less than 0.5 the interpolated color would be produced by interpolating between the first and second colors with i = 42 / 127.

Smooth Gradient: The colors of the fractal correspond to the distance that a particular point is from the boundary of the set. The colors produce bands of distinct regions because the distance is calculated using integers. In order to find distances that can be fractional, and therefore produce images with smooth gradient transitions, use the following equation to determine the distance, and therefore the color, of a complex number:

c = n + 1 - ln(ln(|cn|) / ln(2)) / ln(2)

where n is the number of iterations of the quadratic polynomial function that were computed, cn is the complex number that was computed from the nth iteration, and ln is the natural log. The natural log is loge, which is what the math.log function computes if no base is specified. Note that the value that this equation computes is no longer in the range of 0 to 255. In order to use it to generate a color for the image you will have to find its minimum and maximum values and scale it back into the color range.

Click, Click, Zoom: As you will no doubt be able to see, trying to find interesting locations in the Mandelbrot set with the above zoom capabilities is going to be difficult. You will have to manually convert from image pixels to complex numbers, and determine the zoom factor the same way.

However, there is an easier way. But it involves turtle. With turtle's click listener, you can get the location the user wishes to zoom in. You can also assume that each click will zoom in by a factor of 2. For the first click, the size of the complex plane becomes 2, ranging from (-1, 1) in both directions.

This process will be faster than manually determining where to zoom, but is still is not going to be quick. Be prepared to wait several seconds (or minutes) between each click to see the new zoomed in location. The code for this, however, is pretty easy to implement.