Creating Mandelbrot Fractals on X

Drawing fractals in an X windows environment in C.

Ooooh Fractals!!

This project was more or less to see if I could actually get the system to draw fractals and to learn about the code contained within. There are many interesting components to the code from creating a window in X and how to create a custom cursor.

A Mandelbrot fractal is a set of points on a plane that creates a complex, repeating pattern, the more you zoom in to more iterations of the same patterns you see. Our fractals were named after BenoƮt Mandelbrot (http://www.math.yale.edu/mandelbrot/). Our intention here is to use a program to create a window in X and draw the factals.

mandelbrot.c
 1 /*
  2    This program opens a window and draws the Mandelbrot fractal.
  3    The user can click and drag to zoom.  Requires Unix/X11.
  4
  5    Compile with:
  6       cc -o mandelbrot mandelbrot.c -lX11 -lgen
  7    on Irix (lgen is for libgen.h)
  8    If you have a linux box, try
  9       gcc -o mandelbrot mandelbrot.c -lX11 -L/usr/X11R6/lib/
 10 */
 11
 12 #include <stdio.h>
 13 #include <stdlib.h>   /* malloc(), free() */
 14 #include <unistd.h>   /* exit() */
 15 #include <libgen.h>   /* basename() */
 16 #include <stdarg.h>   /* needed for va_start(), va_arg(), va_end() */
 17
 18 #include <X11/Xlib.h>
 19 #include <X11/Xutil.h>
 20 /* #include <X11/Xos.h> */
 21 /* #include <X11/Xatom.h> */
 22 #include <X11/keysym.h>
 23
24 /* #define TOGGLEABLE_WINDOW_DECORATIONS */  /* requires Motif header files     */
 25
 26 #ifdef TOGGLEABLE_WINDOW_DECORATIONS
 27 #include <Xm/Xm.h>
 28 #include <Xm/MwmUtil.h>
 29 #endif
 30
 31 /* typedef enum { False = 0, True = 1 } Boolean; */
 32 #ifndef TOGGLEABLE_WINDOW_DECORATIONS
 33 typedef unsigned char Boolean;
 34 #endif
 35 typedef unsigned long Pixel;
 36
 37 #define ASSERT(condition) if (!(condition)) { \
 38    printf("Assertion failure, (%s), at line %d in file %s\n", \
 39       #condition, __LINE__, __FILE__ \
 40    ); \
 41    exit(1); \
 42 }
 43
 44 struct {
 45    char * commandLineName;
 46    /*
 47       Recall that in X terminology,
 48       one *display* can consist of
 49       many *screens*.
 50    */
 51    Display *display;
 52    int screen_num;
 53    Screen *screen_ptr;
 54    int screenWidth, screenHeight;       /* dimensions in pixels */
 55    int screenWidthMM, screenHeightMM;   /* dimensions in millimeters */
 56    unsigned int screenDepth;
 57    Window rootWindow;
 58    Window window;
 59    unsigned int windowWidth, windowHeight; /* dimensions in pixels */
 60    Cursor cursor;
 61    Pixel blackPixel, whitePixel;
 62    GC gc;                  /* Graphics context. */
 63 } XStuff;
 64
 65 struct {
 66    short **image;
 67    int imageWidth, imageHeight;
 68    int currentX, currentY;
 69    double minRe, minIm, maxRe, maxIm;
 70    int maxIterations;
 71    Boolean isImageComplete;
 72    Boolean isDragging;
 73    int dragX1, dragY1, dragX2, dragY2;
 74    Boolean isColorsAllocated;
 75    Pixel *colorArray;
 76    int numColors;
 77 } fractal = {
 78    NULL,
 79    0, 0,
 80    0, 0,
 81    -2.25, -1.575, 1.95, 1.575,
 82    64,
 83    False,
 84    False,
 85    0, 0, 0, 0,
86    False,
 87    NULL,
 88    0
 89 };
 90
 91 void MakeWindow(
 92    char * windowTitle,
 93    char * iconTitle,
 94    Pixmap iconPixmap,                      /* Pass (None) for none. */
 95    int minWidth, int minHeight,
 96    Boolean isFullScreenAndBorderless,      /* Doesn't work too well */
 97    Pixel backgroundPixel,
 98    int argc, char *argv[]                  /* Command line arguments. */
 99 ) {
100    XWMHints *wm_hints;                     /* Window manager hints. */
101    XClassHint *class_hints;                /* Application class hints. */
102    XSizeHints *size_hints;                 /* Preferred window geometry. */
103    XTextProperty windowName,iconName;      /* Special X "strings" */
104    int windowX, windowY;                   /* Upper left corner of window. *    /
105    unsigned int windowBorderWidth;
106
107    /*
108       Allocate hint structures.
109    */
110    if (NULL == (wm_hints = XAllocWMHints()) ||
111       NULL == (class_hints = XAllocClassHint()) ||
112       NULL == (size_hints = XAllocSizeHints())
113    ) {
114       fprintf(stderr,"%s: failure allocating memory.\n", XStuff.commandLineN    ame);
115       exit(1);
116    }
117
118    /*
119       Create text properties.
120    */
121    if (0 == XStringListToTextProperty(&windowTitle, 1, &windowName) ||
122       0 == XStringListToTextProperty(&iconTitle, 1, &iconName)
123    ) {
124       fprintf(stderr,"%s: structure allocation for text property failed.\n",
125          XStuff.commandLineName);
126       exit(1);
127    }
128
129    /*
130       Create the main window.
131    */
132    if (isFullScreenAndBorderless) {
133       windowX = 0;
134       windowY = 0;
135       XStuff.windowWidth = XStuff.screenWidth;
136       XStuff.windowHeight = XStuff.screenHeight;
137       windowBorderWidth = 0;
138    }
139    else {
140       windowX = XStuff.screenWidth/3;
141       windowY = XStuff.screenHeight/3;
142       XStuff.windowWidth = XStuff.screenWidth/3;
143       XStuff.windowHeight = XStuff.screenHeight/3;
144       windowBorderWidth = 0;
145    }
146    XStuff.window = XCreateSimpleWindow(XStuff.display, XStuff.rootWindow,
147       windowX, windowY, XStuff.windowWidth, XStuff.windowHeight,
148       windowBorderWidth,
149       XStuff.blackPixel, XStuff.whitePixel);
150
151    /*
152       Set window properties.
153    */
154    wm_hints->flags = StateHint | InputHint;
155    wm_hints->initial_state = NormalState;  /* Should window be normal or ico    nified when first mapped ? */
156    wm_hints->input = True;                 /* Does application need keyboard     input? */
157    if (None != iconPixmap) {
158       wm_hints->flags |= IconPixmapHint;
159       wm_hints->icon_pixmap = iconPixmap;
160    }
161
162    /* These are used by the window manager to get information */
163    /* about this application from the resource database. */
164    /*
165       For example, with 4Dwm, you could add lines
166       like these to your ~/.Xdefaults file :
167          4Dwm*foo.clientDecoration: none
168          4Dwm*goo.clientDecoration: border resizeh
169       If foo is the name or class of an app's window, then that window will
170       have no window decorations.  Similarly, in the case of goo, the window
171       will only have a resizable border.  (The clientDecoration resource wor    ks
172       the same way with Mwm.)
173    */
174    class_hints->res_name = XStuff.commandLineName;
175    class_hints->res_class = "MyClassOfApplications";
176
177    /* Use USPosition | USSize instead of PPosition | PSize to force hints. *    /
178    /* size_hints->flags = PPosition | PSize | PMinSize; */
179    size_hints->flags = PMinSize;
180    /* In R4 and later, the x, y, width, and height members of XSizeHints sho    uld not be set. */
181    size_hints->min_width = minWidth;
182    size_hints->min_height = minHeight;
183    if (isFullScreenAndBorderless)
184       size_hints->flags |= (USPosition | USSize);
185
186    XSetWMProperties(XStuff.display, XStuff.window, &windowName, &iconName,
187       argv, argc, size_hints, wm_hints,class_hints);
188
189    /*
190       Select event types wanted.
191
192       ConfigureNotify events inform the app that the window has been
193       resized.  Processing ConfigureNotify events is more efficient
194       than having to call XGetGeometry() (which requires a reply from
195       the server) on every Expose event.
196    */
197    XSelectInput(XStuff.display, XStuff.window,
198       ExposureMask |
199       KeyPressMask |
200       KeyReleaseMask |
201       ButtonPressMask |
202       ButtonReleaseMask |
203       PointerMotionMask |
204       /* PointerMotionHintMask | */ /* we don't process mouse motion events     */
205       StructureNotifyMask /* selects CirculateNotify, ConfigureNotify, Destr    oyNotify,
206                    GravityNotify, MapNotify, ReparentNotify, and UnmapNotify     */
207    );
208
209    /*
210       Display the window.
211    */
212    XMapWindow(XStuff.display, XStuff.window);
213
214    /*
215       Set the background color.
216    */
217    XSetWindowBackground(XStuff.display, XStuff.window, backgroundPixel);
218
219    /*
220       Get a graphics context.
221    */
222    XStuff.gc = XCreateGC(XStuff.display, XStuff.window, 0, NULL);
223    /* Specify black foreground since default window background */
224    /* is white and default foreground is undefined. */
225    XSetForeground(XStuff.display, XStuff.gc, XStuff.blackPixel);
226
227    /*
228       Get geometry information about window
229    */
230    if (False == XGetGeometry(XStuff.display, XStuff.window, &XStuff.rootWind    ow,
231       &windowX, &windowY, &XStuff.windowWidth, &XStuff.windowHeight,
232       &windowBorderWidth, &XStuff.screenDepth)
233    ) {
234       fprintf(stderr,"%s: can't get window geometry.\n", XStuff.commandLineN    ame);
235       exit(1);
236    }
237 }
238
239 /* ================================================================= */
240
241 /* The below bitmaps were created with the command "bitmap [filename]". */
242
243 #define cursor_width 25
244 #define cursor_height 25
245 #define cursor_x_hot 12
246 #define cursor_y_hot 12
247 static char cursor_bits[] = {
248    0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
249    0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
250    0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
251    0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00,
252    0x55, 0x55, 0x55, 0x01, 0x00, 0x28, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
253    0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
254    0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
255    0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
256    0x00, 0x10, 0x00, 0x00};
257 #define mask_width 25
258 #define mask_height 25
259 #define mask_x_hot 12
260 #define mask_y_hot 12
261 static char mask_bits[] = {
262    0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
263    0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
264    0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
265    0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00,
266    0xff, 0xff, 0xff, 0x01, 0x00, 0x38, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
267    0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
268    0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
269    0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
270    0x00, 0x10, 0x00, 0x00};
271
272 void CreateCursorForWindow() {
273
274    Pixmap source, mask;
275    XColor foreground, background;
276
277    foreground.pixel = 0;
278    foreground.red = 65535;
279    foreground.green = 65535;
280    foreground.blue = 65535;
281    foreground.flags = DoRed | DoGreen | DoBlue;
282    foreground.pad = 0;
283
284    background.pixel = 0;
285    background.red = 0;
286    background.green = 0;
287    background.blue = 0;
288    background.flags = DoRed | DoGreen | DoBlue;
289    background.pad = 0;
290
291    source = XCreateBitmapFromData(XStuff.display, XStuff.window,
292                    cursor_bits, cursor_width, cursor_height);
293    mask = XCreateBitmapFromData(XStuff.display, XStuff.window,
294                    mask_bits, mask_width, mask_height);
295    XStuff.cursor = XCreatePixmapCursor(XStuff.display, source, mask,
296                    &foreground, &background,
297                    cursor_x_hot, cursor_y_hot);
298
299    XDefineCursor(XStuff.display, XStuff.window, XStuff.cursor);
300
301    XFreePixmap(XStuff.display, source);
302    XFreePixmap(XStuff.display, mask);
303 }
304
305 #define MAX_INTERPOLATION_POINTS 20
306
307 Pixel *AllocReadOnlySpectrum(
308    int *size_return,
309    int numColorsBetweenPoints,
310    Boolean isWrapAround,
311    ...
312 ) {
313    va_list argPtr;
314    int intArg,
315       red   [MAX_INTERPOLATION_POINTS],
316       green [MAX_INTERPOLATION_POINTS],
317       blue  [MAX_INTERPOLATION_POINTS];
318    int numPoints, numColors, delta = 1 + numColorsBetweenPoints,
319       startPoint, endPoint, interpolationCounter, colorIndex, j;
320    float weight;
321    XColor xcolor;
322    Pixel *colorArray;
323
324    /* BEGIN: gather up paramters. */
325
326    va_start(argPtr,isWrapAround);
327    for (j = 0; j < MAX_INTERPOLATION_POINTS; j++) {
328       intArg = va_arg(argPtr, int);
329       if (intArg < 0) break;
330       red  [j] = intArg;
331       green[j] = va_arg(argPtr, int);
332       blue [j] = va_arg(argPtr, int);
333    }
334    numPoints = j;
335    va_end(argPtr);
336
337    /* END: gather up parameters. */
338
339    if (isWrapAround)
340       numColors = numPoints * delta;
341    else
342       numColors = numPoints * delta - numColorsBetweenPoints;
343
344    colorArray = (Pixel*)malloc(numColors * sizeof(Pixel));
345
346    colorIndex = 0;
347    startPoint = 0;
348    endPoint = 1;
349    while (endPoint < numPoints) {
350       for (interpolationCounter = 0; interpolationCounter <= numColorsBetwee    nPoints; interpolationCounter++) {
351          weight = interpolationCounter/(float)delta;
352          xcolor.pixel = 0;
353          xcolor.red   = red  [startPoint] + weight * (red  [endPoint] - red      [startPoint]);
354          xcolor.green = green[startPoint] + weight * (green[endPoint] - gree    n[startPoint]);
355          xcolor.blue  = blue [startPoint] + weight * (blue [endPoint] - blue     [startPoint]);
356          xcolor.flags = DoRed | DoGreen | DoBlue;
357          XAllocColor(XStuff.display, DefaultColormapOfScreen(XStuff.screen_p    tr), &xcolor);
358          colorArray[colorIndex] = xcolor.pixel;
359          /* printf("[%d](%d,%d,%d)\n", colorIndex, xcolor.red, xcolor.green,     xcolor.blue ); */
360          colorIndex++;
361       }
362
363       if (endPoint < startPoint)
364          /* We just finished the last wrap-around loop. */
365          break;
366
367       startPoint = endPoint;
368       endPoint++;
369
370       if (endPoint == numPoints && isWrapAround)
371          /* We now do the last loop necessary to complete wrap-around. */
372          endPoint = 0;
373    }
374    if (!isWrapAround) {
375       /* Fill in the last color. */
376       ASSERT(startPoint == numPoints - 1);
377       xcolor.pixel = 0;
378       xcolor.red   = red  [startPoint];
379       xcolor.green = green[startPoint];
380       xcolor.blue  = blue [startPoint];
381       xcolor.flags = DoRed | DoGreen | DoBlue;
382       XAllocColor(XStuff.display, DefaultColormapOfScreen(XStuff.screen_ptr)    , &xcolor);
383       colorArray[colorIndex] = xcolor.pixel;
384       /* printf("[%d](%d,%d,%d)\n", colorIndex, xcolor.red, xcolor.green, xc    olor.blue ); */
385       colorIndex++;
386    }
387    ASSERT(colorIndex == numColors);
388
389    *size_return = numColors;
390    return colorArray;
391 }
392
393 void FreePixelColors() {
394
395    int j;
396
397    for (j = 0; j < fractal.numColors; ++j)
398       XFreeColors(
399          XStuff.display,
400          DefaultColormapOfScreen(XStuff.screen_ptr),
401          & fractal.colorArray[j],
402          1,
403          0
404       );
405 }
406
407 void AllocateImage(int w,int h) {
408
409    int j,k;
410
411    fractal.imageWidth = w;
412    fractal.imageHeight = h;
413    ASSERT(NULL == fractal.image);
414    fractal.image = (short**)malloc((size_t)fractal.imageWidth * sizeof(short    *));
415    ASSERT(NULL != fractal.image);
416    for (j = 0; j < fractal.imageWidth; j++) {
417       fractal.image[j] = (short*)malloc((size_t)fractal.imageHeight * sizeof    (short));
418       ASSERT(NULL != fractal.image[j]);
419       for (k = 0; k < fractal.imageHeight; k++)
420          fractal.image[j][k] = fractal.maxIterations;
421    }
422    fractal.currentX = fractal.currentY = 0;
423    fractal.isImageComplete = False;
424 }
425
426 void ReleaseImage() {
427
428    int j;
429
430    if (fractal.image != NULL) {
431       for (j = 0; j < fractal.imageWidth; j++)
432          free(fractal.image[j]);
433       free(fractal.image);
434       fractal.image = NULL;
435    }
436    fractal.imageWidth = 0;
437    fractal.imageHeight = 0;
438    fractal.isImageComplete = False;
439 }
440
441 void StartComputations() {
442
443    double deltaRe, deltaIm;
444    int w,h;
445
446    deltaRe = fractal.maxRe - fractal.minRe;
447    deltaIm = fractal.maxIm - fractal.minIm;
448
449    if (deltaRe/deltaIm > (XStuff.windowWidth-1)/(double)(XStuff.windowHeight    -1)) {
450       /* The image is too wide to fit in the window. */
451       w = XStuff.windowWidth;
452       h = deltaIm/deltaRe*(w-1) + 1 + 0.5; /* the 0.5 is to round off */
453       ASSERT(h <= XStuff.windowHeight);
454    }
455    else {
456       /* The image is too tall, or perhaps it's just right ! */
457       h = XStuff.windowHeight;
458       w = deltaRe/deltaIm*(h-1) + 1 + 0.5; /* the 0.5 is to round off */
459       ASSERT(w <= XStuff.windowWidth);
460    }
461    AllocateImage(w,h);
462    fractal.currentX = fractal.currentY = 0;
463    fractal.isImageComplete = False;
464 }
465
466 void RestartComputations() {
467
468    ReleaseImage();
469    StartComputations();
470 }
471
472 void PlotAt(int x,int y) {
473
474    int deltaX, deltaY;
475    int iterations;
476
477    deltaX = (XStuff.windowWidth - fractal.imageWidth) / 2;
478    deltaY = (XStuff.windowHeight - fractal.imageHeight) / 2;
479    iterations = fractal.image[x][y];
480    if (iterations >= fractal.maxIterations)
481       XSetForeground(XStuff.display, XStuff.gc, XStuff.blackPixel);
482    else
483       XSetForeground(XStuff.display, XStuff.gc, fractal.colorArray[iteration    s % fractal.numColors]);
484
485    XDrawPoint(XStuff.display, XStuff.window, XStuff.gc, deltaX + x, deltaY +     y);
486 }
487
488 void DrawRect(int x1, int y1, int x2, int y2) {
489
490    int x, y;
491    int deltaX, deltaY;
492    Pixel color, newColor;
493    int iterations;
494
495    deltaX = (XStuff.windowWidth - fractal.imageWidth) / 2;
496    deltaY = (XStuff.windowHeight - fractal.imageHeight) / 2;
497
498    /* clip the boundries of the rectangle */
499    if (x1 < deltaX)
500       x1 = deltaX;
501    if (y1 < deltaY)
502       y1 = deltaY;
503    if (x2 > deltaX + fractal.imageWidth - 1)
504       x2 = deltaX + fractal.imageWidth - 1;
505    if (y2 > deltaY + fractal.imageHeight - 1)
506       y2 = deltaY + fractal.imageHeight - 1;
507
508    if (!fractal.isImageComplete && y2 > deltaY + fractal.currentY)
509       y2 = deltaY + fractal.currentY;
510
511    color = XStuff.blackPixel;
512    XSetForeground(XStuff.display, XStuff.gc, color);
513    for (y = y1; y <= y2; y++)
514       for (x = x1; x <= x2; x++) {
515          iterations = fractal.image[x - deltaX][y - deltaY];
516
517          if (iterations >= fractal.maxIterations)
518             newColor = XStuff.blackPixel;
519          else
519          else
520             newColor = fractal.colorArray[iterations  % fractal.numColors];
521
522          if (newColor != color)
523             XSetForeground(XStuff.display, XStuff.gc, color = newColor);
524
525          XDrawPoint(XStuff.display, XStuff.window, XStuff.gc, x, y);
526       }
527 }
528
529 void Zoom() {
530
531    double re1,im1,re2,im2,tmp;
532    int deltaX, deltaY, itmp;
533
534    itmp = fractal.dragX2 - fractal.dragX1;
535    if (itmp < 5 && itmp > -5)
536       return;
537    itmp = fractal.dragY2 - fractal.dragY1;
538    if (itmp < 5 && itmp > -5)
539       return;
540
541    deltaX = (XStuff.windowWidth - fractal.imageWidth) / 2;
542    deltaY = (XStuff.windowHeight - fractal.imageHeight) / 2;
543    fractal.dragX1 -= deltaX;
544    fractal.dragY1 -= deltaY;
545    fractal.dragX2 -= deltaX;
546    fractal.dragY2 -= deltaY;
547
548    re1 = fractal.dragX1/(double)(fractal.imageWidth-1) * (fractal.maxRe - fr    actal.minRe) + fractal.minRe;
549    re2 = fractal.dragX2/(double)(fractal.imageWidth-1) * (fractal.maxRe - fr    actal.minRe) + fractal.minRe;
550    im1 = fractal.maxIm - fractal.dragY1/(double)(fractal.imageHeight-1) * (f    ractal.maxIm - fractal.minIm);
551    im2 = fractal.maxIm - fractal.dragY2/(double)(fractal.imageHeight-1) * (f    ractal.maxIm - fractal.minIm);
552
553    if (re2 < re1) {
554       tmp = re1;
555       re1 = re2;
556       re2 = tmp;
557    }
558
559    if (im2 < im1) {
560       tmp = im1;
561       im1 = im2;
562       im2 = tmp;
563    }
564
565    if (re2 - re1 == 0 || im2 - im1 == 0)
566       return;
567
568    fractal.minRe = re1;
569    fractal.minIm = im1;
570    fractal.maxRe = re2;
571    fractal.maxIm = im2;
572    RestartComputations();
573 }
574
575 void ResizeImageToFillWindow() {
576
577    double re_center, im_center, width, height;
578
579    re_center = ( fractal.minRe + fractal.maxRe ) * 0.5;
580    im_center = ( fractal.minIm + fractal.maxIm ) * 0.5;
581    width = fractal.maxRe - fractal.minRe;
582    height = fractal.maxIm - fractal.minIm;
583
584    /* Here's the resize. */
585    if (width/height > (XStuff.windowWidth-1)/(double)(XStuff.windowHeight-1)    ) {
586       /* The image is too wide to fit in the window. */
587       height = width * (XStuff.windowHeight-1)/(double)(XStuff.windowWidth-1    );
588    }
589    else {
590       /* The image is too tall, or perhaps it's just right ! */
591       width = height * (XStuff.windowWidth-1)/(double)(XStuff.windowHeight-1    );
592    }
593
594    /* Slam the results back in. */
595    fractal.minRe = re_center - width * 0.5;
596    fractal.maxRe = re_center + width * 0.5;
597    fractal.minIm = im_center - height * 0.5;
598    fractal.maxIm = im_center + height * 0.5;
599
600    RestartComputations();
601 }
602
603 void ZoomInOnCenter( double magnificationFactor ) {
604
605    double re_center, im_center, width, height;
606
607    re_center = ( fractal.minRe + fractal.maxRe ) * 0.5;
608    im_center = ( fractal.minIm + fractal.maxIm ) * 0.5;
609    width = fractal.maxRe - fractal.minRe;
610    height = fractal.maxIm - fractal.minIm;
611
612    /* Here's the zoom-in. */
613    width /= magnificationFactor;
614    height /= magnificationFactor;
615
616    /* Slam the results back in. */
617    fractal.minRe = re_center - width * 0.5;
618    fractal.maxRe = re_center + width * 0.5;
619    fractal.minIm = im_center - height * 0.5;
620    fractal.maxIm = im_center + height * 0.5;
621
622    RestartComputations();
623 }
624
625 void ContinueComputations() {
626
627    double px, py, zx, zy, zx_new, zy_new;
628    int j;
629
630    if (NULL == fractal.image || fractal.isImageComplete)
631       return;
632
633    px = fractal.currentX/(double)(fractal.imageWidth-1) * (fractal.maxRe - f    ractal.minRe) + fractal.minRe;
634    py = fractal.maxIm - fractal.currentY/(double)(fractal.imageHeight-1) * (    fractal.maxIm - fractal.minIm);
635    zx = 0.0;
636    zy = 0.0;
637    for (j = 0; j < fractal.maxIterations; j++) {
638       zx_new = zx*zx - zy*zy + px;
639       zy_new = 2*zx*zy + py;
640       zx = zx_new;
641       zy = zy_new;
642       if (zx*zx + zy*zy >= 4)
643          break;
644    }
645
646    fractal.image[fractal.currentX][fractal.currentY] = j;
647
648    PlotAt(fractal.currentX, fractal.currentY);
649
650    fractal.currentX++;
651    if (fractal.imageWidth == fractal.currentX) {
652       fractal.currentX = 0;
653       fractal.currentY++;
654       if (fractal.imageHeight == fractal.currentY) {
655          fractal.currentY = 0;
656          fractal.isImageComplete = True;
657       }
658    }
659 }
660
661 main(int argc, char *argv[]) {
662
663    XEvent event;                   /* Structure for event information */
664    int keyCode;
665    int j;
666    /*
667       If display_name is not specified by the user, it should be set to
668       NULL, which causes XOpenDisplay() to connect to the server specified
669       in the UNIX environment DISPLAY variable.
670
671       setenv DISPLAY host:display.screen            (C shell)
672       setenv DISPLAY elvis:0.0
673       DISPLAY=host:display.screen; export DISPLAY   (Bourne shell)
674    */
675    char *display_name = NULL;       /* Server to connect to */
676    Boolean stillRunning = True;
677
678 #ifdef TOGGLEABLE_WINDOW_DECORATIONS
679    Boolean hasNoWindowDecorations = False;
680    PropMotifWmHints mwm_hints;
681    Atom mwm_hints_atom;
682 #endif
683
684    XStuff.commandLineName = basename(argv[0]);
685
686    /* Connect to the X server. */
687    if (NULL == (XStuff.display = XOpenDisplay(display_name))) {
688       fprintf(stderr,"%s: cannot connect to X server %s\n",
689          XStuff.commandLineName, XDisplayName(display_name));
690       exit(1);
691    }
692    XStuff.screen_num       = DefaultScreen         (XStuff.display);
693    XStuff.screen_ptr       = DefaultScreenOfDisplay(XStuff.display);
694    XStuff.screenWidth      = DisplayWidth          (XStuff.display, XStuff.s    creen_num);
695    XStuff.screenHeight     = DisplayHeight         (XStuff.display, XStuff.s    creen_num);
696    XStuff.screenWidthMM    = DisplayWidthMM        (XStuff.display, XStuff.s    creen_num);
697    XStuff.screenHeightMM   = DisplayHeightMM       (XStuff.display, XStuff.s    creen_num);
698    XStuff.rootWindow       = RootWindow            (XStuff.display, XStuff.s    creen_num);
699    XStuff.blackPixel       = BlackPixel            (XStuff.display, XStuff.s    creen_num);
700    XStuff.whitePixel       = WhitePixel            (XStuff.display, XStuff.s    creen_num);
701
702    printf(
703       "This program displays the Mandelbrot fractal.\n"
704       "To zoom in on a region, click and drag with the left mouse button.\n"
705       "Keys:\n"
706       "  keypad \"+\"/\"-\"     Zoom in/out, centered\n"
707       "  \"i\"                Modify the number of iterations\n"
708       "  space              Resize image to fill the window\n"
709 #ifdef TOGGLEABLE_WINDOW_DECORATIONS
710       "  F10                Toggle window decorations\n"
711       "                     (requires Motif-like window manager)\n"
712 #endif
713       "  escape             Quit\n"
714       "Press Enter to begin ... "
715    );
716    getchar();
717
718    MakeWindow("Mandelbrot Fractal","z -> z^2 + p",None,0,0,
719       False,
720       XStuff.blackPixel,argc,argv
721    );
722    CreateCursorForWindow();
723
724    fractal.colorArray = AllocReadOnlySpectrum(
725       & fractal.numColors, 15,
726 #if 1  /* a rainbow scheme */
727       True,
728       65535,     0,     0,
729       65535, 65535,     0,
730           0, 65535,     0,
731           0, 65535, 65535,
732           0,     0, 65535,
733       65535,     0, 65535,
734 #else  /* icy cold colours */
735       False,
736       65535,     0, 65535,
737           0, 65535,     0,
738           0, 65535, 65535,
739           0,     0, 65535,
740 #endif
741       -1
742    );
743    fractal.isColorsAllocated = True;
744
745    StartComputations();
746
747    while (stillRunning) {
748       if (!fractal.isImageComplete && NULL != fractal.image) {
749          while (0 == XEventsQueued(XStuff.display, QueuedAfterFlush)) {
750             for (j = 0; j < 100; j++)
751                ContinueComputations();
752          }
753       }
754       XNextEvent(XStuff.display, &event);
755       switch(event.type) {
756       case Expose:
757
758          #if 0 /* we draw each exposed region seperately; no skipping */
759          /* Unless this is the last contiguous expose, don't draw the window    . */
760          if (event.xexpose.count != 0)
761             break;
762          /* Only *contiguous* expose events can be skipped.  Searching throu    gh the event queue */
763          /* to find the last expose event would skip over intervening Config    ureNotify events. */
764          #endif
765
766          /* The exposed area is given by the x,y,width,height fields in even    t.xexpose */
767
768          ASSERT(fractal.isColorsAllocated && NULL != fractal.colorArray);
769          ASSERT(NULL != fractal.image);
770
771          DrawRect(event.xexpose.x, event.xexpose.y,
772             event.xexpose.x + event.xexpose.width - 1,
773             event.xexpose.y + event.xexpose.height - 1);
774          break;
775       case ConfigureNotify:
776          if (
777             XStuff.windowWidth != event.xconfigure.width ||
778             XStuff.windowHeight != event.xconfigure.height
779          ) {
780             XStuff.windowWidth = event.xconfigure.width;
781             XStuff.windowHeight = event.xconfigure.height;
782             RestartComputations();
783          }
784          break;
785       case ButtonPress:
786          /*
787          event.xbutton has members x, y, state, button
788          state == is a combination of Button1Mask | ... | Button5Mask
789             | ControlMask | ShiftMask | LockMask | Mod1Mask | ... | Mod5Mask
790          button == one of Button1, ..., Button5
791          */
792          fractal.isDragging = True;
793          fractal.dragX1 = event.xbutton.x;
794          fractal.dragY1 = event.xbutton.y;
795          break;
796       case ButtonRelease:
797          if (fractal.isDragging) {
798             fractal.dragX2 = event.xbutton.x;
799             fractal.dragY2 = event.xbutton.y;
800             fractal.isDragging = False;
801             Zoom();
802          }
803          break;
804       case KeyPress:
805          switch (keyCode = XLookupKeysym(&event.xkey, 0)) {
806          case XK_space:
807             ResizeImageToFillWindow();
808             break;
809          case XK_KP_Add:
810             ZoomInOnCenter( 3.0 );
811             break;
812          case XK_KP_Subtract:
813             ZoomInOnCenter( 1.0 / 3.0 );
814             break;
815          case XK_Up:
816             ;
817             break;
818          case XK_Down:
819             ;
820             break;
821          case XK_Right:
822             ;
823             break;
824          case XK_Left:
825             ;
826             break;
827          case XK_1:
828             ;
829             break;
830          case XK_i:
831          case XK_I:
832             printf("\nMax number of iterations is currently %d\n",fractal.ma    xIterations);
833             printf("Enter new max:\n");
834             scanf("%d",&j);
835             if (j >= 1 && j != fractal.maxIterations) {
836                fractal.maxIterations = j;
837                RestartComputations();
838             }
839             break;
840 #ifdef TOGGLEABLE_WINDOW_DECORATIONS
841          case XK_F10:
842             hasNoWindowDecorations = ! hasNoWindowDecorations;
843             mwm_hints_atom  = XInternAtom (
844                XStuff.display, _XA_MOTIF_WM_HINTS, False
845             );
846             mwm_hints.flags = (int) MWM_HINTS_DECORATIONS;
847             mwm_hints.decorations = hasNoWindowDecorations ? 0
848                : MWM_DECOR_ALL;
849                /*
850                : MWM_DECOR_BORDER | MWM_DECOR_TITLE | MWM_DECOR_RESIZEH
851                | MWM_DECOR_MINIMIZE | MWM_DECOR_MAXIMIZE | MWM_DECOR_MENU;
852                */
853             XChangeProperty(
854                XStuff.display, XStuff.window, mwm_hints_atom,
855                mwm_hints_atom,
856                32, PropModeReplace, (unsigned char *) & mwm_hints,
857                PROP_MWM_HINTS_ELEMENTS
858             );
859             break;
860 #endif
861          case XK_Escape:
862             stillRunning = False;
863             break;
864          }
865          break;
866       case KeyRelease:
867          ;
868          break;
869 #if 0
870       case MotionNotify:
871          /* get the latest motion event */
872          while(XCheckMaskEvent(XStuff.display,PointerMotionMask,&event))
873             ;
874          /* event.xmotion has members x, y, state, is_hint */
875          /* ... */
876          break;
877 #endif
878       default:
879          break;
880       } /* switch */
881    } /* while */
882
883    FreePixelColors();
884    ReleaseImage();
885    XFreeGC(XStuff.display, XStuff.gc);
886    XFreeCursor(XStuff.display, XStuff.cursor);
887    XCloseDisplay(XStuff.display);
888 }
889

The program must be compiled on a system running X or else it will fail (you need X windows to display a window!! GRR!).

All that is required after compilation is to run the software like this.

./mandelbrot.c

The program will create an X window with the fractal generated within. You can drag your cursor over a section and get a zoomed in view (hit space to maximize the view).