User Tools

Site Tools


user:mgough:x-fractals

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

user:mgough:x-fractals [2010/05/08 21:35] – created mgoughuser:mgough:x-fractals [2010/05/20 21:41] (current) mgough
Line 1: Line 1:
 +====== 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.  
 +
 +<code c 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
 +</code>
 +
 +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).  
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +