This shows you the differences between two versions of the page.
user:mgough:x-fractals [2010/05/08 21:35] – created mgough | user: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. | ||
+ | |||
+ | 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:// | ||
+ | |||
+ | <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/ | ||
+ | 10 */ | ||
+ | 11 | ||
+ | 12 #include < | ||
+ | 13 #include < | ||
+ | 14 #include < | ||
+ | 15 #include < | ||
+ | 16 #include < | ||
+ | 17 | ||
+ | 18 #include < | ||
+ | 19 #include < | ||
+ | 20 /* #include < | ||
+ | 21 /* #include < | ||
+ | 22 #include < | ||
+ | 23 | ||
+ | 24 /* #define TOGGLEABLE_WINDOW_DECORATIONS */ /* requires Motif header files */ | ||
+ | 25 | ||
+ | 26 #ifdef TOGGLEABLE_WINDOW_DECORATIONS | ||
+ | 27 #include < | ||
+ | 28 #include < | ||
+ | 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)) { \ | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | 42 } | ||
+ | 43 | ||
+ | 44 struct { | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | 63 } XStuff; | ||
+ | 64 | ||
+ | 65 struct { | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | 77 } fractal = { | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | 86 False, | ||
+ | | ||
+ | | ||
+ | 89 }; | ||
+ | 90 | ||
+ | 91 void MakeWindow( | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | 99 ) { | ||
+ | 100 XWMHints *wm_hints; | ||
+ | 101 XClassHint *class_hints; | ||
+ | 102 XSizeHints *size_hints; | ||
+ | 103 XTextProperty windowName, | ||
+ | 104 int windowX, windowY; | ||
+ | 105 unsigned int windowBorderWidth; | ||
+ | 106 | ||
+ | 107 /* | ||
+ | 108 | ||
+ | 109 */ | ||
+ | 110 if (NULL == (wm_hints = XAllocWMHints()) || | ||
+ | 111 NULL == (class_hints = XAllocClassHint()) || | ||
+ | 112 NULL == (size_hints = XAllocSizeHints()) | ||
+ | 113 ) { | ||
+ | 114 | ||
+ | 115 | ||
+ | 116 } | ||
+ | 117 | ||
+ | 118 /* | ||
+ | 119 | ||
+ | 120 */ | ||
+ | 121 if (0 == XStringListToTextProperty(& | ||
+ | 122 0 == XStringListToTextProperty(& | ||
+ | 123 ) { | ||
+ | 124 | ||
+ | 125 XStuff.commandLineName); | ||
+ | 126 | ||
+ | 127 } | ||
+ | 128 | ||
+ | 129 /* | ||
+ | 130 | ||
+ | 131 */ | ||
+ | 132 if (isFullScreenAndBorderless) { | ||
+ | 133 | ||
+ | 134 | ||
+ | 135 | ||
+ | 136 | ||
+ | 137 | ||
+ | 138 } | ||
+ | 139 else { | ||
+ | 140 | ||
+ | 141 | ||
+ | 142 | ||
+ | 143 | ||
+ | 144 | ||
+ | 145 } | ||
+ | 146 XStuff.window = XCreateSimpleWindow(XStuff.display, | ||
+ | 147 | ||
+ | 148 | ||
+ | 149 | ||
+ | 150 | ||
+ | 151 /* | ||
+ | 152 Set window properties. | ||
+ | 153 */ | ||
+ | 154 wm_hints-> | ||
+ | 155 wm_hints-> | ||
+ | 156 wm_hints-> | ||
+ | 157 if (None != iconPixmap) { | ||
+ | 158 | ||
+ | 159 | ||
+ | 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 ~/ | ||
+ | 167 4Dwm*foo.clientDecoration: | ||
+ | 168 4Dwm*goo.clientDecoration: | ||
+ | 169 If foo is the name or class of an app's window, then that window will | ||
+ | 170 have no window decorations. | ||
+ | 171 will only have a resizable border. | ||
+ | 172 the same way with Mwm.) | ||
+ | 173 */ | ||
+ | 174 class_hints-> | ||
+ | 175 class_hints-> | ||
+ | 176 | ||
+ | 177 /* Use USPosition | USSize instead of PPosition | PSize to force hints. * / | ||
+ | 178 /* size_hints-> | ||
+ | 179 size_hints-> | ||
+ | 180 /* In R4 and later, the x, y, width, and height members of XSizeHints sho uld not be set. */ | ||
+ | 181 size_hints-> | ||
+ | 182 size_hints-> | ||
+ | 183 if (isFullScreenAndBorderless) | ||
+ | 184 | ||
+ | 185 | ||
+ | 186 XSetWMProperties(XStuff.display, | ||
+ | 187 argv, argc, size_hints, wm_hints, | ||
+ | 188 | ||
+ | 189 /* | ||
+ | 190 | ||
+ | 191 | ||
+ | 192 | ||
+ | 193 | ||
+ | 194 than having to call XGetGeometry() (which requires a reply from | ||
+ | 195 the server) on every Expose event. | ||
+ | 196 */ | ||
+ | 197 XSelectInput(XStuff.display, | ||
+ | 198 | ||
+ | 199 | ||
+ | 200 | ||
+ | 201 | ||
+ | 202 | ||
+ | 203 | ||
+ | 204 /* PointerMotionHintMask | */ /* we don't process mouse motion events | ||
+ | 205 | ||
+ | 206 GravityNotify, | ||
+ | 207 ); | ||
+ | 208 | ||
+ | 209 /* | ||
+ | 210 | ||
+ | 211 */ | ||
+ | 212 XMapWindow(XStuff.display, | ||
+ | 213 | ||
+ | 214 /* | ||
+ | 215 Set the background color. | ||
+ | 216 */ | ||
+ | 217 XSetWindowBackground(XStuff.display, | ||
+ | 218 | ||
+ | 219 /* | ||
+ | 220 Get a graphics context. | ||
+ | 221 */ | ||
+ | 222 XStuff.gc = XCreateGC(XStuff.display, | ||
+ | 223 /* Specify black foreground since default window background */ | ||
+ | 224 /* is white and default foreground is undefined. */ | ||
+ | 225 XSetForeground(XStuff.display, | ||
+ | 226 | ||
+ | 227 /* | ||
+ | 228 Get geometry information about window | ||
+ | 229 */ | ||
+ | 230 if (False == XGetGeometry(XStuff.display, | ||
+ | 231 & | ||
+ | 232 & | ||
+ | 233 ) { | ||
+ | 234 | ||
+ | 235 | ||
+ | 236 } | ||
+ | 237 } | ||
+ | 238 | ||
+ | 239 /* ================================================================= */ | ||
+ | 240 | ||
+ | 241 /* The below bitmaps were created with the command " | ||
+ | 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, | ||
+ | 292 cursor_bits, | ||
+ | 293 mask = XCreateBitmapFromData(XStuff.display, | ||
+ | 294 mask_bits, mask_width, mask_height); | ||
+ | 295 XStuff.cursor = XCreatePixmapCursor(XStuff.display, | ||
+ | 296 & | ||
+ | 297 cursor_x_hot, | ||
+ | 298 | ||
+ | 299 XDefineCursor(XStuff.display, | ||
+ | 300 | ||
+ | 301 XFreePixmap(XStuff.display, | ||
+ | 302 XFreePixmap(XStuff.display, | ||
+ | 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 | ||
+ | 316 green [MAX_INTERPOLATION_POINTS], | ||
+ | 317 | ||
+ | 318 int numPoints, numColors, delta = 1 + numColorsBetweenPoints, | ||
+ | 319 | ||
+ | 320 float weight; | ||
+ | 321 XColor xcolor; | ||
+ | 322 Pixel *colorArray; | ||
+ | 323 | ||
+ | 324 /* BEGIN: gather up paramters. */ | ||
+ | 325 | ||
+ | 326 va_start(argPtr, | ||
+ | 327 for (j = 0; j < MAX_INTERPOLATION_POINTS; | ||
+ | 328 | ||
+ | 329 if (intArg < 0) break; | ||
+ | 330 | ||
+ | 331 | ||
+ | 332 blue [j] = va_arg(argPtr, | ||
+ | 333 } | ||
+ | 334 numPoints = j; | ||
+ | 335 va_end(argPtr); | ||
+ | 336 | ||
+ | 337 /* END: gather up parameters. */ | ||
+ | 338 | ||
+ | 339 if (isWrapAround) | ||
+ | 340 | ||
+ | 341 else | ||
+ | 342 | ||
+ | 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 | ||
+ | 351 weight = interpolationCounter/ | ||
+ | 352 xcolor.pixel = 0; | ||
+ | 353 xcolor.red | ||
+ | 354 xcolor.green = green[startPoint] + weight * (green[endPoint] - gree n[startPoint]); | ||
+ | 355 xcolor.blue | ||
+ | 356 xcolor.flags = DoRed | DoGreen | DoBlue; | ||
+ | 357 XAllocColor(XStuff.display, | ||
+ | 358 colorArray[colorIndex] = xcolor.pixel; | ||
+ | 359 /* printf(" | ||
+ | 360 colorIndex++; | ||
+ | 361 } | ||
+ | 362 | ||
+ | 363 if (endPoint < startPoint) | ||
+ | 364 /* We just finished the last wrap-around loop. */ | ||
+ | 365 break; | ||
+ | 366 | ||
+ | 367 | ||
+ | 368 | ||
+ | 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 | ||
+ | 377 | ||
+ | 378 | ||
+ | 379 | ||
+ | 380 | ||
+ | 381 | ||
+ | 382 | ||
+ | 383 | ||
+ | 384 /* printf(" | ||
+ | 385 | ||
+ | 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; | ||
+ | 398 | ||
+ | 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; | ||
+ | 417 | ||
+ | 418 | ||
+ | 419 for (k = 0; k < fractal.imageHeight; | ||
+ | 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; | ||
+ | 432 free(fractal.image[j]); | ||
+ | 433 | ||
+ | 434 | ||
+ | 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/ | ||
+ | 450 /* The image is too wide to fit in the window. */ | ||
+ | 451 w = XStuff.windowWidth; | ||
+ | 452 h = deltaIm/ | ||
+ | 453 | ||
+ | 454 } | ||
+ | 455 else { | ||
+ | 456 /* The image is too tall, or perhaps it's just right ! */ | ||
+ | 457 h = XStuff.windowHeight; | ||
+ | 458 w = deltaRe/ | ||
+ | 459 | ||
+ | 460 } | ||
+ | 461 AllocateImage(w, | ||
+ | 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 | ||
+ | 482 else | ||
+ | 483 | ||
+ | 484 | ||
+ | 485 XDrawPoint(XStuff.display, | ||
+ | 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, | ||
+ | 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 | ||
+ | 519 else | ||
+ | 519 else | ||
+ | 520 | ||
+ | 521 | ||
+ | 522 if (newColor != color) | ||
+ | 523 | ||
+ | 524 | ||
+ | 525 XDrawPoint(XStuff.display, | ||
+ | 526 } | ||
+ | 527 } | ||
+ | 528 | ||
+ | 529 void Zoom() { | ||
+ | 530 | ||
+ | 531 double re1, | ||
+ | 532 int deltaX, deltaY, itmp; | ||
+ | 533 | ||
+ | 534 itmp = fractal.dragX2 - fractal.dragX1; | ||
+ | 535 if (itmp < 5 && itmp > -5) | ||
+ | 536 | ||
+ | 537 itmp = fractal.dragY2 - fractal.dragY1; | ||
+ | 538 if (itmp < 5 && itmp > -5) | ||
+ | 539 | ||
+ | 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/ | ||
+ | 549 re2 = fractal.dragX2/ | ||
+ | 550 im1 = fractal.maxIm - fractal.dragY1/ | ||
+ | 551 im2 = fractal.maxIm - fractal.dragY2/ | ||
+ | 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 | ||
+ | 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/ | ||
+ | 586 /* The image is too wide to fit in the window. */ | ||
+ | 587 | ||
+ | 588 } | ||
+ | 589 else { | ||
+ | 590 /* The image is too tall, or perhaps it's just right ! */ | ||
+ | 591 width = height * (XStuff.windowWidth-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 | ||
+ | 632 | ||
+ | 633 px = fractal.currentX/ | ||
+ | 634 py = fractal.maxIm - fractal.currentY/ | ||
+ | 635 zx = 0.0; | ||
+ | 636 zy = 0.0; | ||
+ | 637 for (j = 0; j < fractal.maxIterations; | ||
+ | 638 | ||
+ | 639 | ||
+ | 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, | ||
+ | 649 | ||
+ | 650 fractal.currentX++; | ||
+ | 651 if (fractal.imageWidth == fractal.currentX) { | ||
+ | 652 | ||
+ | 653 | ||
+ | 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; | ||
+ | 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 | ||
+ | 672 | ||
+ | 673 | ||
+ | 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 | ||
+ | 689 XStuff.commandLineName, | ||
+ | 690 | ||
+ | 691 } | ||
+ | 692 XStuff.screen_num | ||
+ | 693 XStuff.screen_ptr | ||
+ | 694 XStuff.screenWidth | ||
+ | 695 XStuff.screenHeight | ||
+ | 696 XStuff.screenWidthMM | ||
+ | 697 XStuff.screenHeightMM | ||
+ | 698 XStuff.rootWindow | ||
+ | 699 XStuff.blackPixel | ||
+ | 700 XStuff.whitePixel | ||
+ | 701 | ||
+ | 702 printf( | ||
+ | 703 " | ||
+ | 704 " | ||
+ | 705 " | ||
+ | 706 " | ||
+ | 707 " | ||
+ | 708 " | ||
+ | 709 #ifdef TOGGLEABLE_WINDOW_DECORATIONS | ||
+ | 710 " | ||
+ | 711 " | ||
+ | 712 #endif | ||
+ | 713 " | ||
+ | 714 " | ||
+ | 715 ); | ||
+ | 716 getchar(); | ||
+ | 717 | ||
+ | 718 MakeWindow(" | ||
+ | 719 | ||
+ | 720 | ||
+ | 721 ); | ||
+ | 722 CreateCursorForWindow(); | ||
+ | 723 | ||
+ | 724 fractal.colorArray = AllocReadOnlySpectrum( | ||
+ | 725 & fractal.numColors, | ||
+ | 726 #if 1 /* a rainbow scheme */ | ||
+ | 727 True, | ||
+ | 728 | ||
+ | 729 | ||
+ | 730 0, 65535, | ||
+ | 731 0, 65535, 65535, | ||
+ | 732 | ||
+ | 733 | ||
+ | 734 #else /* icy cold colours */ | ||
+ | 735 | ||
+ | 736 | ||
+ | 737 0, 65535, | ||
+ | 738 0, 65535, 65535, | ||
+ | 739 | ||
+ | 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, | ||
+ | 750 for (j = 0; j < 100; j++) | ||
+ | 751 ContinueComputations(); | ||
+ | 752 } | ||
+ | 753 } | ||
+ | 754 | ||
+ | 755 | ||
+ | 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 | ||
+ | 762 /* Only *contiguous* expose events can be skipped. | ||
+ | 763 /* to find the last expose event would skip over intervening Config | ||
+ | 764 #endif | ||
+ | 765 | ||
+ | 766 /* The exposed area is given by the x, | ||
+ | 767 | ||
+ | 768 ASSERT(fractal.isColorsAllocated && NULL != fractal.colorArray); | ||
+ | 769 ASSERT(NULL != fractal.image); | ||
+ | 770 | ||
+ | 771 DrawRect(event.xexpose.x, | ||
+ | 772 | ||
+ | 773 | ||
+ | 774 break; | ||
+ | 775 case ConfigureNotify: | ||
+ | 776 if ( | ||
+ | 777 | ||
+ | 778 | ||
+ | 779 ) { | ||
+ | 780 | ||
+ | 781 | ||
+ | 782 | ||
+ | 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 | ||
+ | 799 | ||
+ | 800 | ||
+ | 801 | ||
+ | 802 } | ||
+ | 803 break; | ||
+ | 804 case KeyPress: | ||
+ | 805 switch (keyCode = XLookupKeysym(& | ||
+ | 806 case XK_space: | ||
+ | 807 | ||
+ | 808 | ||
+ | 809 case XK_KP_Add: | ||
+ | 810 | ||
+ | 811 | ||
+ | 812 case XK_KP_Subtract: | ||
+ | 813 | ||
+ | 814 | ||
+ | 815 case XK_Up: | ||
+ | 816 ; | ||
+ | 817 | ||
+ | 818 case XK_Down: | ||
+ | 819 ; | ||
+ | 820 | ||
+ | 821 case XK_Right: | ||
+ | 822 ; | ||
+ | 823 | ||
+ | 824 case XK_Left: | ||
+ | 825 ; | ||
+ | 826 | ||
+ | 827 case XK_1: | ||
+ | 828 ; | ||
+ | 829 | ||
+ | 830 case XK_i: | ||
+ | 831 case XK_I: | ||
+ | 832 | ||
+ | 833 | ||
+ | 834 | ||
+ | 835 if (j >= 1 && j != fractal.maxIterations) { | ||
+ | 836 fractal.maxIterations = j; | ||
+ | 837 RestartComputations(); | ||
+ | 838 } | ||
+ | 839 | ||
+ | 840 #ifdef TOGGLEABLE_WINDOW_DECORATIONS | ||
+ | 841 case XK_F10: | ||
+ | 842 | ||
+ | 843 | ||
+ | 844 XStuff.display, | ||
+ | 845 ); | ||
+ | 846 | ||
+ | 847 | ||
+ | 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 | ||
+ | 854 XStuff.display, | ||
+ | 855 mwm_hints_atom, | ||
+ | 856 32, PropModeReplace, | ||
+ | 857 PROP_MWM_HINTS_ELEMENTS | ||
+ | 858 ); | ||
+ | 859 | ||
+ | 860 #endif | ||
+ | 861 case XK_Escape: | ||
+ | 862 | ||
+ | 863 | ||
+ | 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, | ||
+ | 873 ; | ||
+ | 874 /* event.xmotion has members x, y, state, is_hint */ | ||
+ | 875 /* ... */ | ||
+ | 876 break; | ||
+ | 877 #endif | ||
+ | 878 | ||
+ | 879 break; | ||
+ | 880 } /* switch */ | ||
+ | 881 } /* while */ | ||
+ | 882 | ||
+ | 883 FreePixelColors(); | ||
+ | 884 ReleaseImage(); | ||
+ | 885 XFreeGC(XStuff.display, | ||
+ | 886 XFreeCursor(XStuff.display, | ||
+ | 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. | ||
+ | ./ | ||
+ | | ||
+ | The program will create an X window with the fractal generated within. | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||