1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4 -*- */
2 /* mandelbrot --- animated mandelbrot sets */
3
4 #if 0
5 static const char sccsid[] = "@(#)mandelbrot.c 5.09 2003/06/30 xlockmore";
6
7 #endif
8
9 #define USE_LOG
10
11 /*-
12 * Copyright (c) 1997 Dan Stromberg <strombrg@nis.acs.uci.edu>
13 *
14 * Permission to use, copy, modify, and distribute this software and its
15 * documentation for any purpose and without fee is hereby granted,
16 * provided that the above copyright notice appear in all copies and that
17 * both that copyright notice and this permission notice appear in
18 * supporting documentation.
19 *
20 * This file is provided AS IS with no warranties of any kind. The author
21 * shall have no liability with respect to the infringement of copyrights,
22 * trade secrets or any patents by this file or any part thereof. In no
23 * event will the author be liable for any lost revenue or profits or
24 * other special, indirect and consequential damages.
25 *
26 * See A.K. Dewdney's "Computer Recreations", Scientific American
27 * Magazine" Aug 1985 for more info. Also A.K. Dewdney's "Computer
28 * Recreations", Scientific American Magazine" Jul 1989, has some neat
29 * extensions besides z^n + c (n small but >= 2) some of these are:
30 * z^z + z^n + c <-- pow
31 * sin(z) + z^n + c <-- sin
32 * sin(z) + e^z + c
33 * These were first explored by a colleague of Mandelbrot, Clifford A.
34 * Pickover. These would make nice additions to add.
35 *
36 * Revision History:
37 * 03-Jul-2003: Added pow and sin options
38 * 30-Jun-2003: Changed writeable mode to be more consistent with
39 * xscreensaver's starfish
40 * 01-Nov-2000: Allocation checks
41 * 24-Mar-1999: DEM and Binary decomp options added by Tim Auckland
42 * <tda10.geo AT yahoo.com>. Ideas from Peitgen & Saupe's
43 * "The Science of Fractal Images"
44 * 17-Nov-1998: Many Changes by Stromberg, including selection of
45 * interesting subregions, more extreme color ranges,
46 * reduction of possible powers to smaller/more interesting
47 * range, elimination of some unused code, slower color cycling,
48 * longer period of color cycling after the drawing is complete.
49 * Hopefully the longer color cycling period will make the mode
50 * reasonable even after CPUs are so fast that the drawing
51 * interval goes by quickly
52 * 20-Oct-1997: Written by Dan Stromberg <strombrg@nis.acs.uci.edu>
53 */
54
55
56 #ifdef STANDALONE
57 #define MODE_mandelbrot
58 #define DEFAULTS "*delay: 25000 \n" \
59 "*count: -8 \n" \
60 "*cycles: 20000 \n" \
61 "*ncolors: 200 \n" \
62
63 # define free_mandelbrot 0
64 # define reshape_mandelbrot 0
65 # define mandelbrot_handle_event 0
66 #define SMOOTH_COLORS
67 #define WRITABLE_COLORS
68 #include "xlockmore.h" /* from the xscreensaver distribution */
69 #else /* !STANDALONE */
70 #include "xlock.h" /* from the xlockmore distribution */
71 #include "color.h"
72 #endif /* !STANDALONE */
73
74 #ifdef MODE_mandelbrot
75
76 #define MINPOWER 2
77 #define DEF_INCREMENT "1.00"
78 #define DEF_BINARY "False"
79 #define DEF_DEM "False"
80 #define DEF_LYAP "False"
81 #define DEF_ALPHA "False"
82 #define DEF_INDEX "False"
83 #define DEF_POW "False"
84 #define DEF_SIN "False"
85 #define DEF_CYCLE "True"
86
87 /* 4.0 is best for seeing if a point is inside the set, 13 is better if
88 ** you want to get a pretty corona
89 */
90 #define ESCAPE 13.0
91 #define FLOATRAND(min,max) ((min)+((double) LRAND()/((double) MAXRAND))*((max)-(min)))
92
93 typedef enum {
94 NONE,
95 LYAPUNOV,
96 ALPHA,
97 INDEX,
98 interior_size
99 } interior_t;
100
101 /* incr also would be nice as a parameter. It controls how fast
102 the order is advanced. Non-integral values are not true orders,
103 but it's a somewhat interesting function anyway
104 */
105 static float increment;
106 static Bool binary_p;
107 static Bool dem_p;
108 static Bool lyap_p;
109 static Bool alpha_p;
110 static Bool index_p;
111 static Bool pow_p;
112 static Bool sin_p;
113 static Bool cycle_p;
114
115 static XrmOptionDescRec opts[] =
116 {
117 {(char *) "-increment", (char *) ".mandelbrot.increment", XrmoptionSepArg, (caddr_t) NULL},
118 {(char *) "-binary", (char *) ".mandelbrot.binary", XrmoptionNoArg, (caddr_t) "on"},
119 {(char *) "+binary", (char *) ".mandelbrot.binary", XrmoptionNoArg, (caddr_t) "off"},
120 {(char *) "-dem", (char *) ".mandelbrot.dem", XrmoptionNoArg, (caddr_t) "on"},
121 {(char *) "+dem", (char *) ".mandelbrot.dem", XrmoptionNoArg, (caddr_t) "off"},
122 {(char *) "-lyap", (char *) ".mandelbrot.lyap", XrmoptionNoArg, (caddr_t) "on"},
123 {(char *) "+lyap", (char *) ".mandelbrot.lyap", XrmoptionNoArg, (caddr_t) "off"},
124 {(char *) "-alpha", (char *) ".mandelbrot.alpha", XrmoptionNoArg, (caddr_t) "on"},
125 {(char *) "+alpha", (char *) ".mandelbrot.alpha", XrmoptionNoArg, (caddr_t) "off"},
126 {(char *) "-index", (char *) ".mandelbrot.index", XrmoptionNoArg, (caddr_t) "on"},
127 {(char *) "+index", (char *) ".mandelbrot.index", XrmoptionNoArg, (caddr_t) "off"},
128 {(char *) "-pow", (char *) ".mandelbrot.pow", XrmoptionNoArg, (caddr_t) "on"},
129 {(char *) "+pow", (char *) ".mandelbrot.pow", XrmoptionNoArg, (caddr_t) "off"},
130 {(char *) "-sin", (char *) ".mandelbrot.sin", XrmoptionNoArg, (caddr_t) "on"},
131 {(char *) "+sin", (char *) ".mandelbrot.sin", XrmoptionNoArg, (caddr_t) "off"},
132 {(char *) "-cycle", (char *) ".mandelbrot.cycle", XrmoptionNoArg, (caddr_t) "on"},
133 {(char *) "+cycle", (char *) ".mandelbrot.cycle", XrmoptionNoArg, (caddr_t) "off"}
134 };
135
136 static argtype vars[] =
137 {
138 {(void *) & increment, (char *) "increment", (char *) "Increment", (char *) DEF_INCREMENT, t_Float},
139 {(void *) & binary_p, (char *) "binary", (char *) "Binary", (char *) DEF_BINARY, t_Bool},
140 {(void *) & dem_p, (char *) "dem", (char *) "Dem", (char *) DEF_DEM, t_Bool},
141 {(void *) & lyap_p, (char *) "lyap", (char *) "Lyap", (char *) DEF_LYAP, t_Bool},
142 {(void *) & alpha_p, (char *) "alpha", (char *) "Alpha", (char *) DEF_ALPHA, t_Bool},
143 {(void *) & index_p, (char *) "index", (char *) "Index", (char *) DEF_INDEX, t_Bool},
144 {(void *) & pow_p, (char *) "pow", (char *) "Pow", (char *) DEF_POW, t_Bool},
145 {(void *) & sin_p, (char *) "sin", (char *) "Sin", (char *) DEF_SIN, t_Bool},
146 {(void *) & cycle_p, (char *) "cycle", (char *) "Cycle", (char *) DEF_CYCLE, t_Bool}
147 };
148 static OptionStruct desc[] =
149 {
150 {(char *) "-increment value", (char *) "increasing orders"},
151 {(char *) "-/+binary", (char *) "turn on/off Binary Decomposition colour modulation"},
152 {(char *) "-/+dem", (char *) "turn on/off Distance Estimator Method (instead of escape time)"},
153 {(char *) "-/+lyap", (char *) "render interior with Lyapunov measure"},
154 {(char *) "-/+alpha", (char *) "render interior with Alpha level sets"},
155 {(char *) "-/+index", (char *) "render interior with Alpha indexes"},
156 {(char *) "-/+pow", (char *) "turn on/off adding z^z"},
157 {(char *) "-/+sin", (char *) "turn on/off adding sin(z)"},
158 {(char *) "-/+cycle", (char *) "turn on/off colour cycling"}
159 };
160
161 ENTRYPOINT ModeSpecOpt mandelbrot_opts =
162 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
163
164 #ifdef USE_MODULES
165 ModStruct mandelbrot_description =
166 {"mandelbrot", "init_mandelbrot", "draw_mandelbrot", "release_mandelbrot",
167 (char *) NULL, "init_mandelbrot", (char *) NULL, &mandelbrot_opts,
168 25000, -8, 20000, 1, 64, 1.0, "",
169 "Shows mandelbrot sets", 0, NULL};
170
171 #endif
172
173 #define ROUND_FLOAT(x,a) ((float) ((int) ((x) / (a) + 0.5)) * (a))
174
175 typedef struct {
176 double real, imag;
177 } complex;
178
179 static void
add(complex * a,complex b)180 add(complex * a, complex b)
181 {
182 a->real = a->real + b.real;
183 a->imag = a->imag + b.imag;
184 }
185
186 static void
mult(complex * a,complex b)187 mult(complex * a, complex b)
188 {
189 double tr, ti;
190
191 /* a.real*b.real + i*a.real*b.imag + i*a.imag*b.real + i^2*a.imag*b.imag */
192 tr = a->real * b.real - a->imag * b.imag;
193 ti = a->real * b.imag + a->imag * b.real;
194 a->real = tr;
195 a->imag = ti;
196 }
197
198 static void
cln(complex * a)199 cln(complex * a)
200 {
201 /* ln(z) = ln ((x^2 + y^2)^(1/2)) + i * invtan(y/x) */
202 double tr, ti;
203
204 tr = sqrt(a->real * a->real + a->imag * a->imag);
205 ti = (a->real == 0.0 && a->imag == 0.0) ? 0.0 :
206 atan2(a->imag, a->real);
207 a->real = tr;
208 a->imag = ti;
209 }
210
211 static void
complex_exp(complex * a)212 complex_exp(complex * a)
213 {
214 /* e^z = e^x * (cos(y) + i * sin(y)) */
215 double tr;
216
217 tr = exp(a->real);
218 a->real = tr * cos(a->imag);
219 a->imag = tr * sin(a->imag);
220 }
221
222 static complex
complex_pow(complex a,complex b)223 complex_pow(complex a, complex b)
224 {
225 /* a^z = e^(z * ln(a)) */
226
227 complex c, d;
228
229 c = a;
230 d = b;
231 cln(&c);
232 mult(&d, c);
233 complex_exp(&d);
234 return d;
235 }
236
237 static complex
complex_sin(complex a)238 complex_sin(complex a)
239 {
240 /* sin(z) = -i * (e^(i * z) - e^(-i * z)) / 2 */
241 /* cos(z) = (e^(i * z) - e^(-i * z)) / 2 */
242 complex c, d, i;
243
244 c = a;
245 d = a;
246 i.real = 0; i.imag = 1;
247
248 /* inefficient but I just want to see the picture */
249 mult(&c, i);
250 i.imag = -i.imag;
251 mult(&d, i);
252 complex_exp(&c);
253 complex_exp(&d);
254 d.real = -d.real;
255 d.imag = -d.imag;
256 add(&c, d);
257 c.real = c.real / 2;
258 c.imag = c.imag / 2;
259 mult(&c, i);
260 return c;
261 }
262
263 /* this is a true power function. */
264 static void
ipow(complex * a,int n)265 ipow(complex * a, int n)
266 {
267
268
269 switch (n) {
270 case 1:
271 return;
272 case 2:
273 mult(a, *a);
274 return;
275 default:
276 {
277 complex a2;
278 int t2;
279
280 a2 = *a; /* Not very efficient to use: mult(a, ipow(&a2, n-1)); */
281 t2 = n / 2;
282 ipow(&a2, t2);
283 mult(&a2, a2);
284 if (t2 * 2 != n) /* if n is odd */
285 mult(&a2, *a);
286 *a = a2;
287 }
288 }
289 }
290
291 typedef struct {
292 int counter;
293 double power;
294 int column;
295 Bool backwards;
296 int ncolors;
297 unsigned int cur_color;
298 GC gc;
299 Colormap cmap;
300 unsigned long blackpixel, whitepixel, fg, bg;
301 int direction;
302 XColor *colors;
303 complex extreme_ul;
304 complex extreme_lr;
305 complex ul;
306 complex lr;
307 int screen_width;
308 int screen_height;
309 int reptop;
310 Bool dem, pow, sin, cycle_p, mono_p, no_colors;
311 Bool binary;
312 interior_t interior;
313 } mandelstruct;
314
315 static mandelstruct *mandels = (mandelstruct *) NULL;
316
317 /* do the iterations
318 * if binary is true, check halfplane of last iteration.
319 * if demrange is non zero, estimate lower bound of dist(c, M)
320 *
321 * DEM - Distance Estimator Method
322 * Based on Peitgen & Saupe's "The Science of Fractal Images"
323 *
324 * ALPHA - level sets of closest return.
325 * INDEX - index of ALPHA.
326 * Based on Peitgen & Richter's "The Beauty of Fractals" (Fig 33,34)
327 *
328 * LYAPUNOV - lyapunov exponent estimate
329 * Based on an idea by Jan Thor
330 *
331 */
332 static int
reps(complex c,double p,int r,Bool binary,interior_t interior,double demrange,Bool zpow,Bool zsin)333 reps(complex c, double p, int r, Bool binary, interior_t interior, double demrange, Bool zpow, Bool zsin)
334 {
335 int rep;
336 int escaped = 0;
337 complex t;
338 int escape = (int) ((demrange == 0) ? ESCAPE :
339 ESCAPE*ESCAPE*ESCAPE*ESCAPE); /* 2 more iterations */
340 complex t1;
341 complex dt;
342 double L = 0.0;
343 double l2;
344 double dl2 = 1.0;
345 double alpha2 = ESCAPE;
346 int index = 0;
347 #if defined(USE_LOG)
348 double log_top = log((double) r);
349 #endif
350
351 t = c;
352 dt.real = 1; dt.imag = 0;
353 for (rep = 0; rep < r; rep++) {
354 t1 = t;
355 ipow(&t, (int) p);
356 add(&t, c);
357 if (zpow)
358 add(&t, complex_pow(t1, t1));
359 if (zsin)
360 add(&t, complex_sin(t1));
361 l2 = t.real * t.real + t.imag * t.imag;
362 if (l2 <= alpha2) {
363 alpha2 = l2;
364 index = rep;
365 }
366 if (l2 >= escape) {
367 escaped = 1;
368 break;
369 } else if (interior == LYAPUNOV) {
370 /* Crude estimate of Lyapunov exponent. The stronger the
371 attractor, the more negative the exponent will be. */
372 /* n=N
373 L = lim 1/N * Sum log(abs(dx(n+1)/dx(n)))/ln(2)
374 N->inf n=1
375 */
376 L += log(sqrt(l2));
377 }
378 if (demrange){
379 /* compute dt/dc
380 * p-1
381 * dt = p * t * dt + 1
382 * k+1 k k
383 */
384 /* Note this is incorrect for zpow or zsin, but a correct
385 implementation is too slow to be useful. */
386 dt.real *= p; dt.imag *= p;
387 if(p > 2) ipow(&t1, (int) (p - 1));
388 mult(&dt, t1);
389 dt.real += 1;
390 dl2 = dt.real * dt.real + dt.imag * dt.imag;
391 if (dl2 >= 1e300) {
392 escaped = 2;
393 break;
394 }
395 }
396 }
397 if (escaped) {
398 if(demrange) {
399 double mt = sqrt(t1.real * t1.real + t1.imag * t1.imag);
400 /* distance estimate */
401 double dist = 0.5 * mt * log(mt) / sqrt(dl2);
402 /* scale for viewing. Allow black when showing interior. */
403 rep = (int) (((interior > NONE)?0:1) + 10*r*dist/demrange);
404 if(rep > r-1) rep = r-1; /* chop into color range */
405 }
406 if(binary && t.imag > 0)
407 rep = (r + rep / 2) % r; /* binary decomp */
408 #ifdef USE_LOG
409 if ( rep > 0 )
410 rep = (int) (r * log((double) rep)/log_top); /* Log Scale */
411 #endif
412 return rep;
413 } else if (interior == LYAPUNOV) {
414 return -(int)(L/M_LN2) % r;
415 } else if (interior == INDEX) {
416 return 1 + index;
417 } else if (interior == ALPHA) {
418 return (int) (r * sqrt(alpha2));
419 } else
420 return r;
421 }
422
423 static void
Select(complex * extreme_ul,complex * extreme_lr,int width,int height,int power,int top,Bool zpow,Bool zsin,complex * selected_ul,complex * selected_lr)424 Select(/* input variables first */
425 complex *extreme_ul, complex *extreme_lr,
426 int width, int height, int power, int top,
427 Bool zpow, Bool zsin,
428 /* output variables follow */
429 complex *selected_ul,complex *selected_lr)
430 {
431 double precision;
432 double s2;
433 int inside;
434 int uninteresting;
435 int found;
436 int tries;
437 found = 0;
438 while (!found) {
439 /* select a precision - be careful with this */
440 precision = pow(2.0,FLOATRAND(-9.0,-18.0));
441 /* (void) printf("precision is %f\n",precision); */
442 for (tries=0;tries<10000&&!found;tries++) {
443 /* it eventually turned out that this inner loop doesn't always
444 ** terminate - so we just try 10000 times, and if we don't get
445 ** anything interesting, we pick a new precision
446 */
447 complex temp;
448 int sample_step = 4;
449 int row,column;
450 inside = 0;
451 uninteresting = 0;
452 /* pick a random point in the allowable range */
453 temp.real = FLOATRAND(extreme_ul->real,extreme_lr->real);
454 temp.imag = FLOATRAND(extreme_ul->imag,extreme_lr->imag);
455 /* find upper left and lower right points */
456 selected_ul->real = temp.real - precision * width / 2;
457 selected_lr->real = temp.real + precision * width / 2;
458 selected_ul->imag = temp.imag - precision * height / 2;
459 selected_lr->imag = temp.imag + precision * height / 2;
460 /* sample the results we'd get from this choice, accept or reject
461 ** accordingly
462 */
463 for (row=0; row<sample_step; row++) {
464 for (column=0; column<sample_step; column++) {
465 int r;
466 temp.imag = selected_ul->imag +
467 (selected_ul->imag - selected_lr->imag) *
468 (((double)row)/sample_step);
469 temp.real = selected_ul->real +
470 (selected_ul->real - selected_lr->real) *
471 (((double)column)/sample_step);
472 r = reps(temp,(double) power,top,0,(interior_t) 0,0.0, zpow, zsin);
473 /* Here, we just want to see if the point is in the set,
474 ** not if we can make something pretty
475 */
476 if (r == top) {
477 inside++;
478 }
479 if (r < 2) {
480 uninteresting++;
481 }
482 }
483 }
484 s2 = sample_step*sample_step;
485 /* more than 10 percent, but less than 60 percent inside the set */
486 if (inside >= ceil(s2/10.0) && inside <= s2*6.0/10.0 &&
487 uninteresting <= s2/10.0) {
488 /* this one looks interesting */
489 found = 1;
490 }
491 /* else
492 *** this does not look like a real good combination, so back
493 *** up to the top of the loop to try another possibility
494 */
495 }
496 }
497 }
498
499
500 static void
free_mandelbrot_screen(ModeInfo * mi,mandelstruct * mp)501 free_mandelbrot_screen(ModeInfo *mi, mandelstruct *mp)
502 {
503 Display *display = MI_DISPLAY(mi);
504
505 if (mp == NULL) {
506 return;
507 }
508 if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) {
509 MI_WHITE_PIXEL(mi) = mp->whitepixel;
510 MI_BLACK_PIXEL(mi) = mp->blackpixel;
511 #ifndef STANDALONE
512 MI_FG_PIXEL(mi) = mp->fg;
513 MI_BG_PIXEL(mi) = mp->bg;
514 #endif
515 if (mp->colors != NULL) {
516 if (mp->ncolors && !mp->no_colors)
517 free_colors(
518 #ifdef STANDALONE
519 MI_SCREENPTR(mi),
520 #else
521 display,
522 #endif
523 mp->cmap, mp->colors, mp->ncolors);
524 free(mp->colors);
525 mp->colors = (XColor *) NULL;
526 }
527 if (mp->cmap != None) {
528 XFreeColormap(display, mp->cmap);
529 mp->cmap = None;
530 }
531 }
532 if (mp->gc != None) {
533 XFreeGC(display, mp->gc);
534 mp->gc = None;
535 }
536 mp = NULL;
537 }
538
539 #ifndef STANDALONE
540 extern char *background;
541 extern char *foreground;
542 #endif
543
544 ENTRYPOINT void
init_mandelbrot(ModeInfo * mi)545 init_mandelbrot(ModeInfo * mi)
546 {
547 Display *display = MI_DISPLAY(mi);
548 Window window = MI_WINDOW(mi);
549 mandelstruct *mp;
550
551 MI_INIT(mi, mandels);
552 mp = &mandels[MI_SCREEN(mi)];
553
554 mp->screen_width = MI_WIDTH(mi);
555 mp->screen_height = MI_HEIGHT(mi);
556 mp->backwards = (Bool) (LRAND() & 1);
557 if (mp->backwards)
558 mp->column = mp->screen_width - 1;
559 else
560 mp->column = 0;
561 mp->power = NRAND(3) + MINPOWER;
562 mp->counter = 0;
563
564 MI_CLEARWINDOW(mi);
565
566 if (MI_IS_FULLRANDOM(mi)) {
567 mp->binary = (Bool) (LRAND() & 1);
568 mp->dem = (Bool) (LRAND() & 1);
569 mp->interior = (interior_t) NRAND(interior_size);
570 #if 0
571 /* too slow */
572 mp->pow = (NRAND(5) == 0);
573 mp->sin = (NRAND(5) == 0);
574 #endif
575 } else {
576 mp->binary = binary_p;
577 mp->dem = dem_p;
578 if (index_p) {
579 mp->interior = INDEX;
580 } else if(alpha_p) {
581 mp->interior = ALPHA;
582 } else if(lyap_p) {
583 mp->interior = LYAPUNOV;
584 } else {
585 mp->interior = NONE;
586 }
587 mp->pow = pow_p;
588 mp->sin = sin_p;
589 }
590
591 mp->reptop = 300;
592
593 /* these could be tuned a little closer, but the selection
594 ** process throws out the chaf anyway, it just takes slightly
595 ** longer
596 */
597 mp->extreme_ul.real = -3.0;
598 mp->extreme_ul.imag = -3.0;
599 mp->extreme_lr.real = 3.0;
600 mp->extreme_lr.imag = 3.0;
601
602 if (!mp->gc) {
603 if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) {
604 XColor color;
605
606 #ifndef STANDALONE
607 mp->fg = MI_FG_PIXEL(mi);
608 mp->bg = MI_BG_PIXEL(mi);
609 #endif
610 mp->blackpixel = MI_BLACK_PIXEL(mi);
611 mp->whitepixel = MI_WHITE_PIXEL(mi);
612 if ((mp->cmap = XCreateColormap(display, window,
613 MI_VISUAL(mi), AllocNone)) == None) {
614 free_mandelbrot_screen(mi, mp);
615 return;
616 }
617 XSetWindowColormap(display, window, mp->cmap);
618 (void) XParseColor(display, mp->cmap, "black", &color);
619 (void) XAllocColor(display, mp->cmap, &color);
620 MI_BLACK_PIXEL(mi) = color.pixel;
621 (void) XParseColor(display, mp->cmap, "white", &color);
622 (void) XAllocColor(display, mp->cmap, &color);
623 MI_WHITE_PIXEL(mi) = color.pixel;
624 #ifndef STANDALONE
625 (void) XParseColor(display, mp->cmap, background, &color);
626 (void) XAllocColor(display, mp->cmap, &color);
627 MI_BG_PIXEL(mi) = color.pixel;
628 (void) XParseColor(display, mp->cmap, foreground, &color);
629 (void) XAllocColor(display, mp->cmap, &color);
630 MI_FG_PIXEL(mi) = color.pixel;
631 #endif
632 mp->colors = (XColor *) NULL;
633 mp->ncolors = 0;
634 }
635 if ((mp->gc = XCreateGC(display, MI_WINDOW(mi),
636 (unsigned long) 0, (XGCValues *) NULL)) == None) {
637 free_mandelbrot_screen(mi, mp);
638 return;
639 }
640 }
641 MI_CLEARWINDOW(mi);
642
643 /* Set up colour map */
644 mp->direction = (LRAND() & 1) ? 1 : -1;
645 if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) {
646 #ifdef STANDALONE
647 Screen *screen = MI_SCREENPTR(mi);
648 #endif
649 if (mp->colors != NULL) {
650 if (mp->ncolors && !mp->no_colors)
651 free_colors(
652 #ifdef STANDALONE
653 screen,
654 #else
655 display,
656 #endif
657 mp->cmap, mp->colors, mp->ncolors);
658 free(mp->colors);
659 mp->colors = (XColor *) NULL;
660 }
661 mp->ncolors = MI_NCOLORS(mi);
662 if (mp->ncolors < 2)
663 mp->ncolors = 2;
664 if (mp->ncolors <= 2)
665 mp->mono_p = True;
666 else
667 mp->mono_p = False;
668
669 if (mp->mono_p)
670 mp->colors = (XColor *) NULL;
671 else
672 if ((mp->colors = (XColor *) malloc(sizeof (*mp->colors) *
673 (mp->ncolors + 1))) == NULL) {
674 free_mandelbrot_screen(mi, mp);
675 return;
676 }
677 mp->cycle_p = has_writable_cells(
678 #ifdef STANDALONE
679 MI_SCREENPTR(mi), MI_VISUAL(mi)
680 #else
681 mi
682 #endif
683 );
684 if (mp->cycle_p) {
685 if (MI_IS_FULLRANDOM(mi)) {
686 if (!NRAND(8))
687 mp->cycle_p = False;
688 else
689 mp->cycle_p = True;
690 } else {
691 mp->cycle_p = cycle_p;
692 }
693 }
694 if (!mp->mono_p) {
695 #ifdef STANDALONE
696 Screen *screen = MI_SCREENPTR(mi);
697 #endif
698 if (!(LRAND() % 10))
699 make_random_colormap(
700 #ifdef STANDALONE
701 screen, MI_VISUAL(mi),
702 mp->cmap, mp->colors, &mp->ncolors,
703 True, True, &mp->cycle_p, True
704 #else
705 mi,
706 mp->cmap, mp->colors, &mp->ncolors,
707 True, True, &mp->cycle_p
708 #endif
709 );
710 else if (!(LRAND() % 2))
711 make_uniform_colormap(
712 #ifdef STANDALONE
713 screen, MI_VISUAL(mi),
714 mp->cmap, mp->colors, &mp->ncolors,
715 True, &mp->cycle_p, True
716 #else
717 mi,
718 mp->cmap, mp->colors, &mp->ncolors,
719 True, &mp->cycle_p
720 #endif
721 );
722 else
723 make_smooth_colormap(
724 #ifdef STANDALONE
725 screen, MI_VISUAL(mi),
726 mp->cmap, mp->colors, &mp->ncolors,
727 True, &mp->cycle_p, True
728 #else
729 mi,
730 mp->cmap, mp->colors, &mp->ncolors,
731 True, &mp->cycle_p
732 #endif
733 );
734 }
735 XInstallColormap(display, mp->cmap);
736 if (mp->ncolors < 2) {
737 mp->ncolors = 2;
738 mp->no_colors = True;
739 } else
740 mp->no_colors = False;
741 if (mp->ncolors <= 2)
742 mp->mono_p = True;
743
744 if (mp->mono_p)
745 mp->cycle_p = False;
746
747 }
748 if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) {
749 if (mp->mono_p) {
750 mp->cur_color = MI_BLACK_PIXEL(mi);
751 }
752 }
753 Select(&mp->extreme_ul,&mp->extreme_lr,
754 mp->screen_width,mp->screen_height,
755 (int) mp->power,mp->reptop, mp->pow, mp->sin,
756 &mp->ul,&mp->lr);
757 }
758
759 ENTRYPOINT void
draw_mandelbrot(ModeInfo * mi)760 draw_mandelbrot(ModeInfo * mi)
761 {
762 Display *display = MI_DISPLAY(mi);
763 Window window = MI_WINDOW(mi);
764 int h;
765 complex c;
766 double demrange;
767 mandelstruct *mp;
768
769 if (mandels == NULL)
770 return;
771 mp = &mandels[MI_SCREEN(mi)];
772
773 MI_IS_DRAWN(mi) = True;
774 if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) {
775 if (mp->mono_p) {
776 XSetForeground(display, mp->gc, mp->cur_color);
777 } else {
778 mp->cur_color = (mp->cur_color + 1) % mp->ncolors;
779 XSetForeground(display, mp->gc, mp->colors[mp->cur_color].pixel);
780 }
781 } else {
782 if (MI_NPIXELS(mi) > 2)
783 XSetForeground(display, mp->gc, MI_PIXEL(mi, mp->cur_color));
784 else if (mp->cur_color)
785 XSetForeground(display, mp->gc, MI_BLACK_PIXEL(mi));
786 else
787 XSetForeground(display, mp->gc, MI_WHITE_PIXEL(mi));
788 if (++mp->cur_color >= (unsigned int) MI_NPIXELS(mi))
789 mp->cur_color = 0;
790 }
791
792 /* Rotate colours */
793 if (mp->cycle_p) {
794 rotate_colors(
795 #ifdef STANDALONE
796 MI_SCREENPTR(mi),
797 #else
798 display,
799 #endif
800 mp->cmap, mp->colors, mp->ncolors, mp->direction);
801 if (!(LRAND() % 1000))
802 mp->direction = -mp->direction;
803 }
804 /* so we iterate columns beyond the width of the physical screen, so that
805 ** we just wait around and show what we've done
806 */
807 if ((!mp->backwards && (mp->column >= 3 * mp->screen_width)) ||
808 (mp->backwards && (mp->column < -2 * mp->screen_width))) {
809 /* reset to left edge of screen, bump power */
810 mp->backwards = (Bool) (LRAND() & 1);
811 if (mp->backwards)
812 mp->column = mp->screen_width - 1;
813 else
814 mp->column = 0;
815 mp->power = NRAND(3) + MINPOWER;
816 /* select a new region! */
817 Select(&mp->extreme_ul,&mp->extreme_lr,
818 mp->screen_width,mp->screen_height,
819 (int) mp->power,mp->reptop, mp->pow, mp->sin,
820 &mp->ul,&mp->lr);
821 } else if (mp->column >= mp->screen_width || mp->column < 0) {
822 /* delay a while */
823 if (mp->backwards)
824 mp->column--;
825 else
826 mp->column++;
827 mp->counter++;
828 return;
829 }
830 /* demrange is used to give some idea of scale */
831 demrange = mp->dem ? fabs(mp->ul.real - mp->lr.real) / 2 : 0;
832 for (h = 0; h < mp->screen_height; h++) {
833 unsigned int color;
834 int result;
835
836 /* c.real = 1.3 - (double) mp->column / mp->screen_width * 3.4; */
837 /* c.imag = -1.6 + (double) h / mp->screen_height * 3.2; */
838 c.real = mp->ul.real +
839 (mp->ul.real-mp->lr.real)*(((double)(mp->column))/mp->screen_width);
840 c.imag = mp->ul.imag +
841 (mp->ul.imag - mp->lr.imag)*(((double) h) / mp->screen_height);
842 result = reps(c, mp->power, mp->reptop, mp->binary, mp->interior, demrange, mp->pow, mp->sin);
843 if (result < 0 || result >= mp->reptop)
844 XSetForeground(display, mp->gc, MI_BLACK_PIXEL(mi));
845 else {
846 color=(unsigned int) ((MI_NPIXELS(mi) * (float)result) / mp->reptop);
847 XSetForeground(display, mp->gc, MI_PIXEL(mi, color));
848 }
849 /* we no longer have vertical symmetry - so we compute all points
850 ** and don't draw with redundancy
851 */
852 XDrawPoint(display, window, mp->gc, mp->column, h);
853 }
854 if (mp->backwards)
855 mp->column--;
856 else
857 mp->column++;
858
859 mp->counter++;
860 if (mp->counter > MI_CYCLES(mi)) {
861 init_mandelbrot(mi);
862 }
863 }
864
865 ENTRYPOINT void
release_mandelbrot(ModeInfo * mi)866 release_mandelbrot(ModeInfo * mi)
867 {
868 if (mandels != NULL) {
869 int screen;
870
871 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
872 free_mandelbrot_screen(mi, &mandels[screen]);
873 free(mandels);
874 mandels = (mandelstruct *) NULL;
875 }
876 }
877
878 XSCREENSAVER_MODULE ("Mandelbrot", mandelbrot)
879
880 #endif /* MODE_mandelbrot */
881