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