Abhijit Joshi

ENGINEER. PROGRAMMER. ARTIST.

OpenGL Tutorial 009: Exploring the Mandelbrot Set


// GLFW

#include <GLFW/glfw3.h>

// the usual gang of C++ headers

#include <iostream>
#include <complex>
#include <cmath>
#include <cstdlib>

// this function checks if a point (x,y) is a member of the Mandelbrot set
// it returns the number of iterations it takes for this point to escape from the set
// if (x,y) is inside the set, it will not escape even after the maximum number of iterations
// and this function will take a long time to compute this and return the maximum iterations

int Mandelbrot_Member(double x, double y, const int MAX_ITER)
{
   typedef std::complex  dcmplx;   // define a new data type, the double-precision complex number

   dcmplx c(x,y);
   dcmplx z(0.0,0.0);

   int iter = 0;

   while(iter 2.0) break;   // (x,y) is outside the set, quick exit from this loop
   }
   return iter;
}

// calculate pixel colors for the current graphics window, defined by the
// minimum and maximum X and Y coordinates

void showMandelbrot(int WIDTH, int HEIGHT, double xmin, double xmax, double ymin, double ymax)
{
    //--------------------------------
    //  OpenGL initialization stuff 
    //--------------------------------

    // select background color to be white
    // R = 1, G = 1, B = 1, alpha = 0
    glClearColor (1.0, 1.0, 1.0, 0.0);

    // initialize viewing values
    glMatrixMode(GL_PROJECTION);

    // replace current matrix with the identity matrix
    glLoadIdentity();

    // set clipping planes in the X-Y-Z coordinate system
    glOrtho(xmin,xmax,ymin,ymax, -1.0, 1.0);

    // clear all pixels
    glClear (GL_COLOR_BUFFER_BIT);

    // problem parameters
    const int MAX_ITER = 200;

    // 2D array size is identical to the window size in pixels
    const int NX = WIDTH;
    const int NY = HEIGHT;

    int *iters = new int[WIDTH*HEIGHT];

    double dx = (xmax - xmin)/NX; // grid spacing along X
    double dy = (ymax - ymin)/NY; // grid spacing along Y

    // fill the 2D array with the "iter" parameter, which 
    // represents the number of iterations it takes for the
    // point to escape from the set

    for(int i = 0; i < NX; i++) {
      for(int j = 0; j < NY; j++) {

        double x = xmin + i*dx;   // actual x coordinate
        double y = ymin + j*dy;   // actual y coordinate

        // calculate iterations to escape
        iters[i*WIDTH + j] = Mandelbrot_Member(x,y,MAX_ITER);
      }
    }

    // assign color based on the number of iterations - Red Green Blue (RGB) 

    for(int i = 0; i < NX; i++) {
        for(int j = 0; j < NY; j++) {

            double x = xmin + i*dx;   // actual x coordinate
            double y = ymin + j*dy;   // actual y coordinate

            int VAL = iters[i*WIDTH + j];

            if(VAL==MAX_ITER)
            {
                glColor3f(0,0,0);   // black
            }
            else
            {
                // ratio of iterations required to escape
                // the higher this value, the closer the point is to the set
                float frac = (float) VAL / MAX_ITER;

                if(frac<=0.5)
                {
                    // yellow to blue transition
                    glColor3f(2*frac,2*frac,1-2*frac);
                }
                else
                {
                    // red to yellow transition
                    glColor3f(1,2-2*frac,0);
                }
            }
            glRectf (x, y,x+dx,y+dy);
      }
    }

    // draw the horizontal and vertical centerlines for the window
    glColor3f(1,1,1);
    glRectf(xmin+(xmax-xmin)/2-1.95*dx,ymin,xmin+(xmax-xmin)/2+1.95*dx,ymax);
    glRectf(xmin,ymin+(ymax-ymin)/2-1.95*dy,xmax,ymin+(ymax-ymin)/2+1.95*dy);
}

int main(int argc, char* argv[])
{
    //--------------------------------
    //   Create a WINDOW using GLFW
    //--------------------------------

    GLFWwindow *window;

    // initialize the library
    if(!glfwInit())
        return -1;

    // window size for displaying graphics
    int WIDTH  = 800;
    int HEIGHT = 800;

    // set the window's display mode
    window = glfwCreateWindow(WIDTH, HEIGHT, "Mandelbrot Set", NULL, NULL);
    if(!window)
    {
        glfwTerminate();
    return -1;
    }

    // make the windows context current
    glfwMakeContextCurrent(window);

    //---------------------------------------
    // Loop until the user closes the window
    //---------------------------------------

    // user selection of appropriate quadrant for zooming in
    int choice;

    std::cout << " +------+------+ " << std::endl;
    std::cout << " |      |      | " << std::endl;
    std::cout << " |  1   |   2  | " << std::endl;
    std::cout << " |      |      | " << std::endl;
    std::cout << " +------+------+ " << std::endl;
    std::cout << " |      |      | " << std::endl;
    std::cout << " |  3   |   4  | " << std::endl;
    std::cout << " |      |      | " << std::endl;
    std::cout << " +------+------+ " << std::endl;

    // specify initial window size in the X-Y plane
    double xmin = -2, xmax = 1, ymin = -1.5, ymax = 1.5;

    while(!glfwWindowShouldClose(window))
    {
        // display the Mandelbrot set in (xmin,ymin)-(xmax,ymax)
        showMandelbrot(WIDTH, HEIGHT, xmin, xmax, ymin, ymax);

        // swap front and back buffers
        glfwSwapBuffers(window);

        // poll for and processs events
        glfwPollEvents();

        // ask user for selecting a region for further zoom-in
        std::cout << "Zoom in to <1, 2, 3, 4> [0 to quit]:";
        std::cin >> choice;

        // update display limits based on user choice
        switch (choice) {
            case 0:
                glfwSetWindowShouldClose(window, GL_TRUE);
                break;
            case 1:
                xmax = xmin + (xmax - xmin)/2;
                ymin = ymin + (ymax - ymin)/2;
                break;
            case 2:
                xmin = xmin + (xmax - xmin)/2;
                ymin = ymin + (ymax - ymin)/2;
                break;
            case 3:
                xmax = xmin + (xmax - xmin)/2;
                ymax = ymin + (ymax - ymin)/2;
                break;
            case 4:
                xmin = xmin + (xmax - xmin)/2;
                ymax = ymin + (ymax - ymin)/2;
                break;
        }
    }

    glfwDestroyWindow(window);
    glfwTerminate();
    return 0;
}

To compile this code, use the following Makefile:

all:
    g++ -I /usr/local/include/ -L /usr/local/lib -o cpu.x -framework OpenGL -framework Cocoa -framework IOKit -framework CoreVideo CPU_Mandelbrot.cpp -lglfw
 
clean:
    rm *.x