1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* lyapunov --- animated lyapunov space */
3 
4 #if 0
5 static const char sccsid[] = "@(#)lyapunov.c	5.09 2003/06/30 xlockmore";
6 
7 #endif
8 
9 /*-
10  * Permission to use, copy, modify, and distribute this software and its
11  * documentation for any purpose and without fee is hereby granted,
12  * provided that the above copyright notice appear in all copies and that
13  * both that copyright notice and this permission notice appear in
14  * supporting documentation.
15  *
16  * This file is provided AS IS with no warranties of any kind.  The author
17  * shall have no liability with respect to the infringement of copyrights,
18  * trade secrets or any patents by this file or any part thereof.  In no
19  * event will the author be liable for any lost revenue or profits or
20  * other special, indirect and consequential damages.
21  *
22  * Pictures discovered by Mario Markus and Benno Hess while trying to
23  * simulate how enzymes break down carbohydrates.  By adjusting a pair of
24  * parameter's they found they could make the simulated enzymes behave in
25  * either a orderly or chaotic manner.  Images are based on a formula
26  * named after the Russian mathematician Aleksandr M. Lyapunov.
27  * See  A.K. Dewdney's "Computer Recreations", Scientific American
28  * Magazine" Sep 1991 for more info.
29  *
30  * Revision History:
31  * 30-Jun-2003: Changed writeable mode to be more consistent with
32  *              xscreensaver's starfish
33  * 01-Nov-2000: Allocation checks
34  * 17-Dec-1998: Used mandelbrot.c as basis
35  */
36 
37 
38 #ifdef STANDALONE
39 #define MODE_lyapunov
40 #define DEFAULTS "*delay: 25000 \n" \
41 	"*count: 600 \n" \
42 	"*cycles: 20000 \n" \
43 	"*ncolors: 200 \n" \
44 
45 # define free_lyapunov 0
46 # define reshape_lyapunov 0
47 # define lyapunov_handle_event 0
48 #define SMOOTH_COLORS
49 #define WRITABLE_COLORS
50 #include "xlockmore.h"		/* from the xscreensaver distribution */
51 #else /* !STANDALONE */
52 #include "xlock.h"		/* from the xlockmore distribution */
53 #include "color.h"
54 #endif /* !STANDALONE */
55 
56 #ifdef MODE_lyapunov
57 
58 #define MINPOWER 2
59 #define DEF_INCREMENT  "1.00"
60 /* 4.0 is best for seeing if a point is inside the set, 13 is better if
61  * you want to get a pretty corona
62  */
63 
64 #define FLOATRAND(min,max) ((min)+((double) LRAND()/((double) MAXRAND))*((max)-(min)))
65 
66 #define DEF_CYCLE "True"
67 
68 static Bool cycle_p;
69 
70 static XrmOptionDescRec opts[] =
71 {
72   {(char *) "-cycle", (char *) ".lyapunov.cycle", XrmoptionNoArg, (caddr_t) "on"},
73   {(char *) "+cycle", (char *) ".lyapunov.cycle", XrmoptionNoArg, (caddr_t) "off"}
74 };
75 
76 static argtype vars[] =
77 {
78   {(void *) & cycle_p, (char *) "cycle", (char *) "Cycle", (char *) DEF_CYCLE
79 , t_Bool}
80 };
81 static OptionStruct desc[] =
82 {
83   {(char *) "-/+cycle", (char *) "turn on/off colour cycling"}
84 };
85 
86 ENTRYPOINT ModeSpecOpt lyapunov_opts =
87 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
88 
89 #ifdef USE_MODULES
90 ModStruct   lyapunov_description =
91 {"lyapunov", "init_lyapunov", "draw_lyapunov", "release_lyapunov",
92  (char *) NULL, "init_lyapunov", (char *) NULL, &lyapunov_opts,
93  25000, 600, 20000, 1, 64, 1.0, "",
94  "Shows lyapunov space", 0, NULL};
95 
96 #endif
97 
98 #define ROUND_FLOAT(x,a) ((float) ((int) ((x) / (a) + 0.5)) * (a))
99 #define MAXPOWER 14 /* (32 - 2) for 32 bit unsigned long */
100 
101 #ifdef DEBUG
102 static int debug;
103 #endif
104 
105 typedef struct {
106 	int         column;
107 	Bool        backwards, cycle_p, mono_p, no_colors;
108 	int         ncolors;
109 	unsigned int cur_color;
110 	GC          gc;
111 	Colormap    cmap;
112 	unsigned long blackpixel, whitepixel, fg, bg;
113 	int direction;
114 	XColor     *colors;
115 	int         screen_width;
116 	int         screen_height;
117 	float       reptop;
118 	unsigned long periodic_forcing;
119 	int firstone;
120 	unsigned long seq;
121 	int          na, nb, hbit;
122 	int          iter_local;
123 	double       norm;
124 } lyapstruct;
125 
126 static lyapstruct *lyaps = (lyapstruct *) NULL;
127 #define OFFSET 3.0  /* Slight changes here may have unexpected results! */
128 
129 static double
reps(lyapstruct * lp,unsigned long periodic_forcing,int firstone,int iter,double a,double b)130 reps(lyapstruct * lp, unsigned long periodic_forcing, int firstone, int iter, double a, double b)
131 {
132 	#define ABS_LESS(v, limit) ((-(limit) < (v)) && ((v) < (limit)))
133 	#define ABS_MORE(v, limit) (((v) < -(limit)) || ((limit) < (v)))
134 
135 	#define ta_max 512
136 	double  ta[ta_max];
137 	int     ta_idx = 0;
138 	int     i, j = firstone;
139 	double  f = 0.5, t = 1.0, r;
140 	double  log_b;
141 
142 	a += OFFSET;
143 	b += OFFSET; log_b = log(b);
144 
145 	/* update cached values */
146 	if (lp->seq != periodic_forcing || lp->hbit != firstone)
147 	{
148 		lp->seq = periodic_forcing;
149 		lp->hbit = firstone;
150 
151 		for (i=lp->hbit, lp->nb=0; i>=0; i--)
152       			if (lp->seq & (1 << i))
153 				lp->nb++;
154 		lp->na = lp->hbit - lp->nb;
155 	}
156 
157   	if (iter != lp->iter_local)
158 	{
159 		lp->iter_local = iter;
160 		lp->norm = 1.0 / (log(2.0) * iter);
161 	}
162 
163 	for (i = 0; i < iter; i++) {
164 		r = (lp->seq & (1 << j)) ? b : a;
165 		if (! j--)
166 			j = lp->hbit;
167 
168 		f *= r*(1-f);
169 		t *= 1-2*f;
170 
171 		if (ta_idx < ta_max)
172 		{
173 			if (ABS_LESS(t, 1e-50)) { ta[ta_idx++] = t; t = 1.0; }
174 			if (ABS_MORE(t, 1e+50)) { ta[ta_idx++] = t; t = 1.0; }
175 		}
176 	}
177 
178 	/* post process */
179 	r = log(ABS(t)) + lp->na*log(ABS(a)) + lp->nb*log_b;
180 	for (i=0; i<ta_idx; i++)
181         	r += log(ABS(ta[i]));
182 
183       	return r < 0 ? (r * lp->norm) : r;
184 }
185 
186 static void
free_lyapunov_screen(ModeInfo * mi,lyapstruct * lp)187 free_lyapunov_screen(ModeInfo *mi, lyapstruct *lp)
188 {
189 	Display *display = MI_DISPLAY(mi);
190 
191 	if (lp == NULL) {
192 		return;
193 	}
194 	if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) {
195 		MI_WHITE_PIXEL(mi) = lp->whitepixel;
196 		MI_BLACK_PIXEL(mi) = lp->blackpixel;
197 #ifndef STANDALONE
198 		MI_FG_PIXEL(mi) = lp->fg;
199 		MI_BG_PIXEL(mi) = lp->bg;
200 #endif
201 		if (lp->colors != NULL) {
202 #ifdef STANDALONE
203 			Screen *screen = MI_SCREENPTR(mi);
204 #endif
205 			if (lp->ncolors && !lp->no_colors)
206 				free_colors(
207 #ifdef STANDALONE
208 					screen,
209 #else
210 					display,
211 #endif
212 					lp->cmap, lp->colors, lp->ncolors);
213 			free(lp->colors);
214 			lp->colors = (XColor *) NULL;
215 		}
216 		if (lp->cmap != None) {
217 			XFreeColormap(display, lp->cmap);
218 			lp->cmap = None;
219 		}
220 	}
221 	if (lp->gc != None) {
222 		XFreeGC(display, lp->gc);
223 		lp->gc = None;
224 	}
225 	lp = NULL;
226 }
227 
228 #ifndef STANDALONE
229 extern char *background;
230 extern char *foreground;
231 #endif
232 
233 ENTRYPOINT void
init_lyapunov(ModeInfo * mi)234 init_lyapunov(ModeInfo * mi)
235 {
236 	Window      window = MI_WINDOW(mi);
237 	Display    *display = MI_DISPLAY(mi);
238 	int i = NRAND(MAXPOWER);
239 	unsigned long power2;
240 	lyapstruct *lp;
241 
242 	MI_INIT(mi, lyaps);
243 	lp = &lyaps[MI_SCREEN(mi)];
244 
245 #ifdef DEBUG
246 	debug = MI_IS_DEBUG(mi);
247 #endif
248 	lp->screen_width = MI_WIDTH(mi);
249 	lp->screen_height = MI_HEIGHT(mi);
250 	lp->backwards = (Bool) (LRAND() & 1);
251 	if (lp->backwards)
252 		lp->column = lp->screen_width - 1;
253 	else
254 		lp->column = 0;
255 
256 	MI_CLEARWINDOW(mi);
257 
258 	lp->reptop = 0.6;
259 
260 	/* To force a number, say <i = 2;> has  i + 2 (or 4) binary digits */
261   power2 = 1 << (i + 1);
262   /* Do not want numbers which in binary are all 1's. */
263   lp->periodic_forcing = NRAND(power2 - 1) + power2;
264 	lp->firstone = 1;
265 	for (i = 32 - 2; i >= 1; i--)
266 		if (lp->periodic_forcing & (1 << i)) {
267 			lp->firstone = i;
268 			break;
269 		}
270 
271   if (MI_IS_VERBOSE(mi)) {
272 		(void) printf("0x%lx forcing number", lp->periodic_forcing);
273 		switch (lp->periodic_forcing) {
274 			case 0x2:
275 				(void) printf(", swallow\n");
276 				break;
277       case 0x34:
278 				(void) printf(", jellyfish\n");
279 				break;
280 			case 0xFC0: /* Yeah, I think it should be "city" too, but its not. */
281 				(void) printf(", zircon zity\n");
282 				break;
283 			default:
284 				(void) printf("\n");
285 				break;
286 		}
287 	}
288 
289 	if (!lp->gc) {
290 		if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) {
291 			XColor      color;
292 
293 #ifndef STANDALONE
294 			lp->fg = MI_FG_PIXEL(mi);
295 			lp->bg = MI_BG_PIXEL(mi);
296 #endif
297 			lp->blackpixel = MI_BLACK_PIXEL(mi);
298 			lp->whitepixel = MI_WHITE_PIXEL(mi);
299 			if ((lp->cmap = XCreateColormap(display, window,
300 					MI_VISUAL(mi), AllocNone)) == None) {
301 				free_lyapunov_screen(mi, lp);
302 				return;
303 			}
304 			XSetWindowColormap(display, window, lp->cmap);
305 			(void) XParseColor(display, lp->cmap, "black", &color);
306 			(void) XAllocColor(display, lp->cmap, &color);
307 			MI_BLACK_PIXEL(mi) = color.pixel;
308 			(void) XParseColor(display, lp->cmap, "white", &color);
309 			(void) XAllocColor(display, lp->cmap, &color);
310 			MI_WHITE_PIXEL(mi) = color.pixel;
311 #ifndef STANDALONE
312 			(void) XParseColor(display, lp->cmap, background, &color);
313 			(void) XAllocColor(display, lp->cmap, &color);
314 			MI_BG_PIXEL(mi) = color.pixel;
315 			(void) XParseColor(display, lp->cmap, foreground, &color);
316 			(void) XAllocColor(display, lp->cmap, &color);
317 			MI_FG_PIXEL(mi) = color.pixel;
318 #endif
319 			lp->colors = (XColor *) NULL;
320 			lp->ncolors = 0;
321 		}
322 		if ((lp->gc = XCreateGC(display, MI_WINDOW(mi),
323 			     (unsigned long) 0, (XGCValues *) NULL)) == None) {
324 			free_lyapunov_screen(mi, lp);
325 			return;
326 		}
327 	}
328 	MI_CLEARWINDOW(mi);
329 
330   /* Set up colour map */
331   lp->direction = (LRAND() & 1) ? 1 : -1;
332   if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) {
333     if (lp->colors != NULL) {
334 #ifdef STANDALONE
335       Screen *screen = MI_SCREENPTR(mi);
336 #endif
337       if (lp->ncolors && !lp->no_colors)
338 	free_colors(
339 #ifdef STANDALONE
340 		screen,
341 #else
342 		display,
343 #endif
344 		lp->cmap, lp->colors, lp->ncolors);
345       free(lp->colors);
346       lp->colors = (XColor *) NULL;
347     }
348     lp->ncolors = MI_NCOLORS(mi);
349     if (lp->ncolors < 2)
350       lp->ncolors = 2;
351     if (lp->ncolors <= 2)
352       lp->mono_p = True;
353     else
354       lp->mono_p = False;
355 
356     if (lp->mono_p)
357       lp->colors = (XColor *) NULL;
358     else
359       if ((lp->colors = (XColor *) malloc(sizeof (*lp->colors) *
360           (lp->ncolors + 1))) == NULL) {
361         free_lyapunov_screen(mi, lp);
362         return;
363       }
364 #ifdef STANDALONE
365     lp->cycle_p = has_writable_cells(MI_SCREENPTR(mi), MI_VISUAL(mi));
366 #else
367     lp->cycle_p = has_writable_cells(mi);
368 #endif
369     if (lp->cycle_p) {
370       if (MI_IS_FULLRANDOM(mi)) {
371         if (!NRAND(8))
372           lp->cycle_p = False;
373         else
374           lp->cycle_p = True;
375       } else {
376         lp->cycle_p = cycle_p;
377       }
378     }
379     if (!lp->mono_p) {
380 #ifdef STANDALONE
381       Screen *screen = MI_SCREENPTR(mi);
382 #endif
383 
384       if (!(LRAND() % 10))
385         make_random_colormap(
386 #ifdef STANDALONE
387 		screen, MI_VISUAL(mi),
388 		lp->cmap, lp->colors, &lp->ncolors,
389 		True, True, &lp->cycle_p, True
390 #else
391 		mi,
392 		lp->cmap, lp->colors, &lp->ncolors,
393 		True, True, &lp->cycle_p
394 #endif
395 		);
396       else if (!(LRAND() % 2))
397         make_uniform_colormap(
398 #ifdef STANDALONE
399 		screen, MI_VISUAL(mi),
400 		lp->cmap, lp->colors, &lp->ncolors,
401 		True, &lp->cycle_p, True
402 #else
403 		mi,
404 		lp->cmap, lp->colors, &lp->ncolors,
405 		True, &lp->cycle_p
406 #endif
407 		);
408       else
409         make_smooth_colormap(
410 #ifdef STANDALONE
411 		screen, MI_VISUAL(mi),
412 		lp->cmap, lp->colors, &lp->ncolors,
413 		True, &lp->cycle_p, True
414 #else
415 		mi,
416 		lp->cmap, lp->colors, &lp->ncolors,
417 		True, &lp->cycle_p
418 #endif
419 		);
420     }
421     XInstallColormap(display, lp->cmap);
422     if (lp->ncolors < 2) {
423       lp->ncolors = 2;
424       lp->no_colors = True;
425     } else
426       lp->no_colors = False;
427     if (lp->ncolors <= 2)
428       lp->mono_p = True;
429 
430     if (lp->mono_p)
431       lp->cycle_p = False;
432 
433   }
434   if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) {
435                 if (lp->mono_p) {
436 			lp->cur_color = MI_BLACK_PIXEL(mi);
437 		}
438   }
439 	lp->seq = (unsigned long) -1;
440 	lp->hbit = -1;
441 	lp->iter_local = -1;
442 }
443 
444 ENTRYPOINT void
draw_lyapunov(ModeInfo * mi)445 draw_lyapunov(ModeInfo * mi)
446 {
447 	Display    *display = MI_DISPLAY(mi);
448 	Window      window = MI_WINDOW(mi);
449 	GC          gc = MI_GC(mi);
450 	int         h;
451 	lyapstruct *lp;
452 
453 	if (lyaps == NULL)
454 		return;
455 	lp = &lyaps[MI_SCREEN(mi)];
456 
457 	MI_IS_DRAWN(mi) = True;
458 
459         if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) {
460                 if (lp->mono_p) {
461                         XSetForeground(display, lp->gc, lp->cur_color);
462                 } else {
463                         lp->cur_color = (lp->cur_color + 1) % lp->ncolors;
464                         XSetForeground(display, lp->gc, lp->colors[lp->cur_color].pixel);
465                 }
466         } else {
467                 if (MI_NPIXELS(mi) > 2)
468                         XSetForeground(display, lp->gc, MI_PIXEL(mi, lp->cur_color));
469                 else if (lp->cur_color)
470                         XSetForeground(display, lp->gc, MI_BLACK_PIXEL(mi));
471                 else
472                         XSetForeground(display, lp->gc, MI_WHITE_PIXEL(mi));
473                 if (++lp->cur_color >= (unsigned int) MI_NPIXELS(mi))
474                         lp->cur_color = 0;
475         }
476 
477   /* Rotate colours */
478   if (lp->cycle_p) {
479     rotate_colors(
480 #ifdef STANDALONE
481 	MI_SCREENPTR(mi),
482 #else
483 	display,
484 #endif
485 	lp->cmap, lp->colors, lp->ncolors,
486 	lp->direction);
487     if (!(LRAND() % 1000))
488       lp->direction = -lp->direction;
489   }
490 	/* so we iterate columns beyond the width of the physical screen,
491         ** so that we just wait around and show what we've done
492 	*/
493 	if ((!lp->backwards && (lp->column >= 4 * lp->screen_width)) ||
494 	    (lp->backwards && (lp->column < -3 * lp->screen_width))) {
495 		/* reset to left edge of screen */
496 		init_lyapunov(mi);
497 		return;
498 		/* select a new region! */
499 	} else if (lp->column >= lp->screen_width || lp->column < 0) {
500 		/* delay a while */
501 		if (lp->backwards)
502 			lp->column--;
503 		else
504 			lp->column++;
505 		return;
506 	}
507 	for (h = 0; h < lp->screen_height; h++) {
508 		unsigned int color;
509 		double  result;
510 
511 #define MULT 1.0
512 		result = reps(lp, lp->periodic_forcing, lp->firstone, MI_COUNT(mi),
513 			((double) lp->column) / (MULT * lp->screen_width),
514 			((double) h) / (MULT * lp->screen_height));
515 #ifdef DEBUG
516 		if (debug)
517 		(void) printf("a %g, b %g, result %g\n",
518 			 ((double) lp->column) / lp->screen_width,
519 			 ((double) h) / lp->screen_height, result);
520 #endif
521 		if (result > 0.0)
522 			XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
523 		else {
524 			color=(unsigned int) ((MI_NPIXELS(mi) * (float)-result) / lp->reptop);
525 			color = color % MI_NPIXELS(mi);
526 			XSetForeground(display, gc, MI_PIXEL(mi, color));
527 		}
528 		/* we no longer have vertical symmetry - so we compute all points
529 		** and do not draw with redundancy
530 		*/
531 		XDrawPoint(display, window, gc, lp->column, h);
532 	}
533 	if (lp->backwards)
534 		lp->column--;
535 	else
536 		lp->column++;
537 }
538 
539 ENTRYPOINT void
release_lyapunov(ModeInfo * mi)540 release_lyapunov(ModeInfo * mi)
541 {
542 	if (lyaps != NULL) {
543 		int         screen;
544 
545 		for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
546 			free_lyapunov_screen(mi, &lyaps[screen]);
547 		free(lyaps);
548 		lyaps = (lyapstruct *) NULL;
549 	}
550 }
551 
552 XSCREENSAVER_MODULE ("Lyapunov", lyapunov)
553 
554 #endif /* MODE_lyapunov */
555