Drawing fractals in an X windows environment in C.
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.
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).