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 17:35]
mgough created
user:mgough:x-fractals [2010/05/20 17: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).  ​
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
 +
  
user/mgough/x-fractals.txt · Last modified: 2010/05/20 17:41 by mgough