title logo
title right side
30.12.2024 19:17:17 Lokal:
Serverstatus: (1.70/1.62/1.6054.88/1242.3 GByte  
Aussentemperatur : -1.4 °C  Luftdruck : 1030.97 hPas/mbar
Impressions: 43592 
Visits heute: 7753 

  Aktuelle Seite: Mandelbrot set explained

Home  

Mandelbrot Set Zoom Animation in C Language

Introduction

Try the mandelbrot set in your browser

The Mandelbrot set is a fascinating mathematical object that exhibits intricate and beautiful patterns. It is a set of complex numbers that, when iterated through a specific formula, either remain bounded or escape to infinity. The set is named after the mathematician Benoit Mandelbrot, who studied it in the 1970s.

In this article, we will explore how to generate a zoom animation of the Mandelbrot set using the C programming language. We will provide a detailed explanation of the code and its key concepts.

Key Concepts

Before diving into the code, let`s understand some key concepts related to the Mandelbrot set:

  1. Complex Numbers: The Mandelbrot set is defined in the complex plane, which consists of complex numbers. A complex number is a number of the form a + bi, where a and b are real numbers, and i is the imaginary unit.

  2. Iteration: To determine whether a complex number is part of the Mandelbrot set, we iterate through a specific formula for a fixed number of times. If the magnitude of the resulting number remains bounded, the complex number is considered part of the set. Otherwise, it escapes to infinity.

  3. Escape Radius: The escape radius is a threshold value that determines when a complex number is considered to have escaped to infinity. If the magnitude of the number exceeds the escape radius, it is considered to have escaped.

  4. Coloring: To visualize the Mandelbrot set, we assign colors to the points based on the number of iterations it takes for them to escape. This allows us to create visually appealing images of the set.

Code Structure

The code provided is written in the C programming language and consists of several functions and variables. Here is a breakdown of the code structure:

  1. Header Files: The code includes several standard C header files, such as stdio.h, stdlib.h, string.h, and math.h. These header files provide necessary functions and definitions used in the code.

  2. Global Variables: The code defines several global variables, such as iXmax, iYmax, CxMin, CxMax, CyMin, CyMax, and EscapeRadius. These variables define the dimensions of the image, the range of the complex plane, and the escape radius.

  3. Structures: The code defines two structures: ppm_p and pix_triple. These structures are used to store information about the PPM image and individual pixels.

  4. Function Prototypes: The code declares prototypes for various functions used in the program, such as iter, imin, imax, ppm_size, ppm_setsize, ppm_init, ppm_uninit, ppm_write, ppm_putpixel, and ppm_line.

  5. Main Function: The main function is the entry point of the program. It initializes the necessary variables, allocates memory for the PPM image, generates the Mandelbrot set, and saves the image to a file.

  6. Helper Functions: The code includes several helper functions, such as iter, ppm_size, ppm_setsize, ppm_init, ppm_uninit, ppm_write, ppm_putpixel, and ppm_line. These functions perform various tasks, such as calculating the number of iterations for a given complex number, manipulating the PPM image, and drawing lines.

Code Examples

To better understand the code, lets take a look at some code examples:

  • Calculating the Number of Iterations:
int iter(double cx, double cy)
{
  Zx = 0.0;
  Zy = 0.0;
  Zx2 = 0.0;
  Zy2 = 0.0;

  for (Iteration = 0; Iteration < IterationMax && ((Zx2 + Zy2) < ER2); Iteration++)
  {
    Zy = 2 * Zx * Zy + cy;
    Zx = Zx2 - Zy2 + cx;
    Zx2 = Zx * Zx;
    Zy2 = Zy * Zy;
  }

  if (Iteration == IterationMax)
  {
    return 0; // Interior of Mandelbrot set (black)
  } else {
    return Iteration % MaxColorComponentValue; // Exterior of Mandelbrot set (white)
  }
}
  • Writing a Pixel to the PPM Image:
void ppm_putpixel( struct ppm_p *a, int x, int y, unsigned char r, unsigned char g, unsigned char b)
{
  long long img_offset;
  img_offset = ((a->resx) * y + x) * 3;

  if (((x >= 0) & (y >= 0)) & ((x < a->resx) & (y < a->resy)))
  {
    *(a->rgbdata + img_offset + 0) = r;
    *(a->rgbdata + img_offset + 1) = g;
    *(a->rgbdata + img_offset + 2) = b;
  }
}
  • Drawing a Line on the PPM Image:
void ppm_line( struct ppm_p *a, int sx, int sy, int dx, int dy, unsigned char r, unsigned char g, unsigned char b)
{
  long t;
  double d, dsx, dsy, stx, sty, x1, y1;

  dsx = (double)dx - (double)sx;
  dsy = (double)dy - (double)sy;
  d = sqrt(dsx * dsx + dsy * dsy) + 1;

  stx = dsx / d;
  sty = dsy / d;

  x1 = sx;
  y1 = sy;

  for (t = 0; t < (long)d; t++)
  {
    ppm_putpixel(a, x1, y1, r, g, b);
    x1 = x1 + stx;
    y1 = y1 + sty;
  }
}

Limits

Accuracy of the data type double.

Conclusion

In this article, we explored how to generate a zoom animation of the Mandelbrot set using the C programming language. We discussed the key concepts related to the Mandelbrot set and provided a detailed explanation of the code structure and its functions.

By understanding the code and its underlying principles, you can further customize and enhance the Mandelbrot set visualization. You can experiment with different zoom levels, color schemes, and image sizes to create stunning visual representations of this fascinating mathematical object.

Complete program code:

// compile like:
//    gcc -o mandelbrotAA-rahmen_ppm -lm mandelbrotAA-rahmen_ppm.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <math.h>

const int dbg = 5;

         /* screen ( integer) coordinate */
        int iX,iY;
        const int iXmax = 1920*12; 
        const int iYmax = 1080*12;
        /* world ( double) coordinate = parameter plane*/
        double Cx,Cy;
        const double CxMin=-0.62;
        const double CxMax=-0.52;
        const double CyMin=-0.65;
        const double CyMax=-0.6;

        static unsigned char color[3];
        /* Z=Zx+Zy*i  ;   Z0 = 0 */
        double Zx, Zy;
        double Zx2, Zy2; /* Zx2=Zx*Zx;  Zy2=Zy*Zy  */
        /*  */
        int Iteration;
        const int IterationMax=4*256;
        /* bail-out value , radius of circle ;  */
        const double EscapeRadius = 2;
//        const long tripple_size = (long) iXmax * (long) iYmax;
        double ER2 = 4;// EscapeRadius * EscapeRadius;
        const int MaxColorComponentValue=255; 

struct ppm_p {
    int resx,resy;
    char *rgbdata;
    short mem_init;
}ppm_picture;

struct pix_triple {
    unsigned char r,g,b;
}pixel_rgb;

// Prototypen
int iter( double  cx, double  cy);
int imin( int a, int b);
int imax( int a, int b);
int ppm_size( struct ppm_p *a ); // Return Memsize as int
void ppm_setsize( struct ppm_p *a, int x, int y );
int ppm_init( struct ppm_p *a, int x, int y );
void ppm_uninit( struct ppm_p *a);
void ppm_write( struct ppm_p *a );
void ppm_putpixel( struct ppm_p *a, int x, int y, unsigned char r, unsigned char g, unsigned char b );
void ppm_line( struct ppm_p *a, int sx, int sy, int dx, int dy, unsigned char r, unsigned char g, unsigned char b );


// Hauptprogramm
int main()
{
        int x,y,t,c;
        /* */
        double PixelWidth=(CxMax-CxMin)/iXmax;
        double PixelHeight=(CyMax-CyMin)/iYmax;
        double pw=(CxMax-CxMin)/iXmax/3;
        double ph=(CyMax-CyMin)/iYmax/3;
        /* color component ( R or G or B) is coded from 0 to 255 */
        /* it is 24 bit color RGB file */

        ppm_picture.mem_init = -1;
        x = iXmax;
        y = iYmax;

        ppm_setsize(&ppm_picture,x,y);
        t = ppm_size(&ppm_picture);

        printf("Bei Auflösung von %d x %d beträgt der Speicherbedarf %d Bytes.\n",x,y,t);

        t = ppm_init(&ppm_picture,x,y);
        if (ppm_picture.mem_init == 0) {
            printf("ppm-Allozierung liefert positives Ergebnis [%d]\n",ppm_picture.mem_init);
        } else {
            printf("ppm-Allozierung liefert negatives Ergebnis [%d]\n",ppm_picture.mem_init);
            exit(-1);
        }
        printf(" {%p}\n",ppm_picture.rgbdata);
/*      ppm_putpixel(&ppm_picture, 0, 0, 255, 0, 0);
        ppm_putpixel(&ppm_picture, 1, 0, 0, 255, 0);
        ppm_putpixel(&ppm_picture, 0, 1, 0, 0, 255);
        ppm_putpixel(&ppm_picture, x-1, y-1, 255, 0, 0);
        ppm_putpixel(&ppm_picture, x-2, y-1, 0, 255, 0);
        ppm_putpixel(&ppm_picture, x-1, y-2, 0, 0, 255);
*/

        for(iY=0;iY<iYmax;iY++)
        {
            Cy=CyMin + iY*PixelHeight;
            if (fabs(Cy)< PixelHeight/2) Cy=0.0; /* Main antenna */
            for(iX=0;iX<iXmax;iX++)
            {         
                Cx=CxMin + iX*PixelWidth;
                /* initial value of orbit = critical point Z= 0 */
                c = (iter(Cx-pw,Cy-ph)+iter(Cx,Cy-ph)+iter(Cx+pw,Cy-ph)+
                    iter(Cx-pw,Cy)+iter(Cx,Cy)*2+iter(Cx+pw,Cy)+
                    iter(Cx-pw,Cy+ph)+iter(Cx,Cy+ph)+iter(Cx+pw,Cy+ph) ) / 10;
                /*write color to the file*/
//               fwrite(color,1,3,fp);
//                ppm_putpixel(&ppm_picture, iX, iY, color[0], color[1], color[2]);
                ppm_putpixel(&ppm_picture, iX, iY, c, c, c);
            }
            printf("\r%.1f %% finished ...          ",((double) iY / (double) iYmax * 100));
        }
    printf("\n");

        ppm_line(&ppm_picture,0,0,x-1,0,255,255,255);
        ppm_line(&ppm_picture,x-1,0,x-1,y-1,255,255,255);
        ppm_line(&ppm_picture,x-1,y-1,0,y-1,255,255,255);
        ppm_line(&ppm_picture,0,y-1,0,0,255,255,255);
        printf("Schreibe Bild nach ./test.ppm\n");
        ppm_write(&ppm_picture);

        printf("Räume auf ...");

        ppm_uninit(&ppm_picture);

        printf(" fertig.\n\n");

        return 0;
}


// Funktionen
int iter(double cx,double cy)
{
  Zx=0.0;
  Zy=0.0;
  /*Zx2=Zx*Zx;
  Zy2=Zy*Zy;*/
  Zx2=0.0;
  Zy2=0.0;
  /* */
  for (Iteration=0;Iteration<IterationMax && ((Zx2+Zy2)<ER2);Iteration++)
  {
    Zy=2*Zx*Zy + cy;
    Zx=Zx2-Zy2 + cx;
    Zx2=Zx*Zx;
    Zy2=Zy*Zy;
  };
  /* compute  pixel color (24 bit = 3 bytes) */
  if (Iteration==IterationMax)
  { /*  interior of Mandelbrot set = black */
//    color[0]=0;
//    color[1]=0;
//    color[2]=0;
    return 0;
  } else { /* exterior of Mandelbrot set = white */
//    color[0]=Iteration % MaxColorComponentValue; /* Red*/
//    color[1]=Iteration % MaxColorComponentValue;  /* Green */ 
//    color[2]=Iteration % MaxColorComponentValue;/* Blue */
    return Iteration % MaxColorComponentValue;
  }
}


int ppm_pdbg( struct ppm_p *a )
{
    printf("ppm_p\n  mem_init = %d\n  resx = %d\n  resy = %d\n  rgbdata = %p\n",a->mem_init,a->resx,a->resy,a->rgbdata);
}

int ppm_size( struct ppm_p *a )
{
    return (a->resx * a->resy * 3);
}

void ppm_setsize( struct ppm_p *a, int x, int y )
{
    a->resx = x;
    a->resy = y;
}

int ppm_init( struct ppm_p *a, int x, int y )
{
    long s;

    s = x*y*3;
    a->mem_init = 1;
    a->resx = x;
    a->resy = y;
    if (( a->rgbdata = (char *) malloc( s ) ))
    {
        a->mem_init = 0;
    }
    printf(" %d * %d * 3 = %d  (%d)\n",x,y,s,errno);
    ppm_pdbg(a);
    return a->mem_init;
}

void ppm_uninit( struct ppm_p *a )
{
    if (a->mem_init == 0)
    {
        free(a->rgbdata);
        a->mem_init = -1;
    }
}

void ppm_write( struct ppm_p *a )
{
    FILE * fp;
    if (a->mem_init == 0)
    {
        if (fp = fopen ("test_mr.ppm", "w"))
        {
            fprintf( fp, "P6\n%d %d\n255\n", a->resx, a->resy);
            fwrite( a->rgbdata, 1, a->resx*a->resy*3, fp);
            fclose(fp);
        }
    }
}

void ppm_putpixel( struct ppm_p *a, int x, int y, unsigned char r, unsigned char g, unsigned char b )
{
    long long img_offset;
    long rgbd;

    img_offset = ( (a->resx)  * y + x ) * 3;
    //printf("Offset berechnet aus 
    //rgbd = a->rgbdata + img_offset;
//  printf("Setze Pixel an Adressen %p,%p und %p {%dx%d,%d} [%p]\n",a->rgbdata+img_offset,a->rgbdata+img_offset + 1,a->rgbdata+img_offset + 2,x,y,img_offset,a->rgbdata);
    if (((x >= 0) & (y >= 0)) & ((x < a->resx) & (y < a->resy))) {
        *(a->rgbdata+img_offset+0) = r;
        *(a->rgbdata+img_offset+1) = g;
        *(a->rgbdata+img_offset+2) = b;
    }
}

int imin( int a, int b)
{
    if ( a < b ) {
        return a;
    } else {
        return b;
    }
}

int imax( int a, int b)
{
    if ( a < b ) {
        return b;
    } else {
        return a;
    }
}

void ppm_line( struct ppm_p *a, int sx, int sy, int dx, int dy, unsigned char r, unsigned char g, unsigned char b )
{
    long t;
    double d,dsx,dsy,stx,sty,x1,y1;

    // Berechnung der Distanz d, als Zähler fürs Pixel setzen
    dsx = (double) dx - (double) sx;
    dsy = (double) dy - (double) sy;
    d = sqrt(dsx*dsx+dsy*dsy)+1;

    // Stepweiten stx und sty
    stx = dsx/d;
    sty = dsy/d;

    x1=sx;
    y1=sy;

    // Linie zeichnen
    for(t=0;t<(long) d;t++) {
        ppm_putpixel( a, x1, y1, r, g, b);
        x1=x1+stx;
        y1=y1+sty;
    }
}