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