/* -*- Mode: C; tab-width: 4 -*- */ /* lyapunov --- animated lyapunov space */ #if !defined( lint ) && !defined( SABER ) static const char sccsid[] = "@(#)lyapunov.c 5.09 2003/06/30 xlockmore"; #endif /*- * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation. * * This file is provided AS IS with no warranties of any kind. The author * shall have no liability with respect to the infringement of copyrights, * trade secrets or any patents by this file or any part thereof. In no * event will the author be liable for any lost revenue or profits or * other special, indirect and consequential damages. * * Pictures discovered by Mario Markus and Benno Hess while trying to * simulate how enzymes break down carbohydrates. By adjusting a pair of * parameter's they found they could make the simulated enzymes behave in * either a orderly or chaotic manner. Images are based on a formula * named after the Russian mathematician Aleksandr M. Lyapunov. * See A.K. Dewdney's "Computer Recreations", Scientific American * Magazine" Sep 1991 for more info. * * Revision History: * 30-Jun-2003: Changed writeable mode to be more consistent with * xscreensaver's starfish * 01-Nov-2000: Allocation checks * 17-Dec-1998: Used mandelbrot.c as basis */ #ifdef STANDALONE #define MODE_lyapunov #define PROGCLASS "Lyapunov" #define HACK_INIT init_lyapunov #define HACK_DRAW draw_lyapunov #define lyapunov_opts xlockmore_opts #define DEFAULTS "*delay: 25000 \n" \ "*count: 600 \n" \ "*cycles: 20000 \n" \ "*ncolors: 200 \n" #define SMOOTH_COLORS #define WRITABLE_COLORS #include "xlockmore.h" /* from the xscreensaver distribution */ #else /* !STANDALONE */ #include "xlock.h" /* from the xlockmore distribution */ #include "color.h" #endif /* !STANDALONE */ #ifdef MODE_lyapunov #define MINPOWER 2 #define DEF_INCREMENT "1.00" /* 4.0 is best for seeing if a point is inside the set, 13 is better if * you want to get a pretty corona */ #define FLOATRAND(min,max) ((min)+((double) LRAND()/((double) MAXRAND))*((max)-(min))) #define DEF_CYCLE "True" static Bool cycle_p; static XrmOptionDescRec opts[] = { {(char *) "-cycle", (char *) ".lyapunov.cycle", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+cycle", (char *) ".lyapunov.cycle", XrmoptionNoArg, (caddr_t) "off"} }; static argtype vars[] = { {(void *) & cycle_p, (char *) "cycle", (char *) "Cycle", (char *) DEF_CYCLE , t_Bool} }; static OptionStruct desc[] = { {(char *) "-/+cycle", (char *) "turn on/off colour cycling"} }; ModeSpecOpt lyapunov_opts = {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc}; #ifdef USE_MODULES ModStruct lyapunov_description = {"lyapunov", "init_lyapunov", "draw_lyapunov", "release_lyapunov", (char *) NULL, "init_lyapunov", (char *) NULL, &lyapunov_opts, 25000, 600, 20000, 1, 64, 1.0, "", "Shows lyapunov space", 0, NULL}; #endif #define ROUND_FLOAT(x,a) ((float) ((int) ((x) / (a) + 0.5)) * (a)) #define MAXPOWER 14 /* (32 - 2) for 32 bit unsigned long */ #ifdef DEBUG static int debug; #endif typedef struct { int column; Bool backwards, cycle_p, mono_p, no_colors; int ncolors; unsigned int cur_color; GC gc; Colormap cmap; unsigned long blackpixel, whitepixel, fg, bg; int direction; XColor *colors; int screen_width; int screen_height; float reptop; unsigned long periodic_forcing; int firstone; ModeInfo *mi; unsigned long seq; int na, nb, hbit; int iter_local; double norm; } lyapstruct; static lyapstruct *lyaps = (lyapstruct *) NULL; #define OFFSET 3.0 /* Slight changes here may have unexpected results! */ static double reps(lyapstruct * lp, unsigned long periodic_forcing, int firstone, int iter, double a, double b) { #define ABS_LESS(v, limit) ((-(limit) < (v)) && ((v) < (limit))) #define ABS_MORE(v, limit) (((v) < -(limit)) || ((limit) < (v))) #define ta_max 512 double ta[ta_max]; int ta_idx = 0; int i, j = firstone; double f = 0.5, t = 1.0, r; double log_b; a += OFFSET; b += OFFSET; log_b = log(b); /* update cached values */ if (lp->seq != periodic_forcing || lp->hbit != firstone) { lp->seq = periodic_forcing; lp->hbit = firstone; for (i=lp->hbit, lp->nb=0; i>=0; i--) if (lp->seq & (1 << i)) lp->nb++; lp->na = lp->hbit - lp->nb; } if (iter != lp->iter_local) { lp->iter_local = iter; lp->norm = 1.0 / (log(2.0) * iter); } for (i = 0; i < iter; i++) { r = (lp->seq & (1 << j)) ? b : a; if (! j--) j = lp->hbit; f *= r*(1-f); t *= 1-2*f; if (ta_idx < ta_max) { if (ABS_LESS(t, 1e-50)) { ta[ta_idx++] = t; t = 1.0; } if (ABS_MORE(t, 1e+50)) { ta[ta_idx++] = t; t = 1.0; } } } /* post process */ r = log(ABS(t)) + lp->na*log(ABS(a)) + lp->nb*log_b; for (i=0; inorm) : r; } static void free_lyapunov(Display *display, lyapstruct *lp) { ModeInfo *mi = lp->mi; if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) { MI_WHITE_PIXEL(mi) = lp->whitepixel; MI_BLACK_PIXEL(mi) = lp->blackpixel; #ifndef STANDALONE MI_FG_PIXEL(mi) = lp->fg; MI_BG_PIXEL(mi) = lp->bg; #endif if (lp->colors != NULL) { if (lp->ncolors && !lp->no_colors) free_colors(display, lp->cmap, lp->colors, lp->ncolors); free(lp->colors); lp->colors = (XColor *) NULL; } if (lp->cmap != None) { XFreeColormap(display, lp->cmap); lp->cmap = None; } } if (lp->gc != None) { XFreeGC(display, lp->gc); lp->gc = None; } } #ifndef STANDALONE extern char *background; extern char *foreground; #endif void init_lyapunov(ModeInfo * mi) { Window window = MI_WINDOW(mi); Display *display = MI_DISPLAY(mi); int i = NRAND(MAXPOWER); unsigned long power2; lyapstruct *lp; if (lyaps == NULL) { if ((lyaps = (lyapstruct *) calloc(MI_NUM_SCREENS(mi), sizeof (lyapstruct))) == NULL) return; } lp = &lyaps[MI_SCREEN(mi)]; lp->mi = mi; #ifdef DEBUG debug = MI_IS_DEBUG(mi); #endif lp->screen_width = MI_WIDTH(mi); lp->screen_height = MI_HEIGHT(mi); lp->backwards = (Bool) (LRAND() & 1); if (lp->backwards) lp->column = lp->screen_width - 1; else lp->column = 0; MI_CLEARWINDOW(mi); lp->reptop = 0.6; /* To force a number, say has i + 2 (or 4) binary digits */ power2 = 1 << (i + 1); /* Do not want numbers which in binary are all 1's. */ lp->periodic_forcing = NRAND(power2 - 1) + power2; lp->firstone = 1; for (i = 32 - 2; i >= 1; i--) if (lp->periodic_forcing & (1 << i)) { lp->firstone = i; break; } if (MI_IS_VERBOSE(mi)) { (void) printf("0x%lx forcing number", lp->periodic_forcing); switch (lp->periodic_forcing) { case 0x2: (void) printf(", swallow\n"); break; case 0x34: (void) printf(", jellyfish\n"); break; case 0xFC0: /* Yeah, I think it should be "city" too, but its not. */ (void) printf(", zircon zity\n"); break; default: (void) printf("\n"); break; } } if (!lp->gc) { if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) { XColor color; #ifndef STANDALONE lp->fg = MI_FG_PIXEL(mi); lp->bg = MI_BG_PIXEL(mi); #endif lp->blackpixel = MI_BLACK_PIXEL(mi); lp->whitepixel = MI_WHITE_PIXEL(mi); if ((lp->cmap = XCreateColormap(display, window, MI_VISUAL(mi), AllocNone)) == None) { free_lyapunov(display, lp); return; } XSetWindowColormap(display, window, lp->cmap); (void) XParseColor(display, lp->cmap, "black", &color); (void) XAllocColor(display, lp->cmap, &color); MI_BLACK_PIXEL(mi) = color.pixel; (void) XParseColor(display, lp->cmap, "white", &color); (void) XAllocColor(display, lp->cmap, &color); MI_WHITE_PIXEL(mi) = color.pixel; #ifndef STANDALONE (void) XParseColor(display, lp->cmap, background, &color); (void) XAllocColor(display, lp->cmap, &color); MI_BG_PIXEL(mi) = color.pixel; (void) XParseColor(display, lp->cmap, foreground, &color); (void) XAllocColor(display, lp->cmap, &color); MI_FG_PIXEL(mi) = color.pixel; #endif lp->colors = (XColor *) NULL; lp->ncolors = 0; } if ((lp->gc = XCreateGC(display, MI_WINDOW(mi), (unsigned long) 0, (XGCValues *) NULL)) == None) { free_lyapunov(display, lp); return; } } MI_CLEARWINDOW(mi); /* Set up colour map */ lp->direction = (LRAND() & 1) ? 1 : -1; if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) { if (lp->colors != NULL) { if (lp->ncolors && !lp->no_colors) free_colors(display, lp->cmap, lp->colors, lp->ncolors); free(lp->colors); lp->colors = (XColor *) NULL; } lp->ncolors = MI_NCOLORS(mi); if (lp->ncolors < 2) lp->ncolors = 2; if (lp->ncolors <= 2) lp->mono_p = True; else lp->mono_p = False; if (lp->mono_p) lp->colors = (XColor *) NULL; else if ((lp->colors = (XColor *) malloc(sizeof (*lp->colors) * (lp->ncolors + 1))) == NULL) { free_lyapunov(display, lp); return; } lp->cycle_p = has_writable_cells(mi); if (lp->cycle_p) { if (MI_IS_FULLRANDOM(mi)) { if (!NRAND(8)) lp->cycle_p = False; else lp->cycle_p = True; } else { lp->cycle_p = cycle_p; } } if (!lp->mono_p) { if (!(LRAND() % 10)) make_random_colormap( #if STANDALONE display, MI_WINDOW(mi), #else mi, #endif lp->cmap, lp->colors, &lp->ncolors, True, True, &lp->cycle_p); else if (!(LRAND() % 2)) make_uniform_colormap( #if STANDALONE display, MI_WINDOW(mi), #else mi, #endif lp->cmap, lp->colors, &lp->ncolors, True, &lp->cycle_p); else make_smooth_colormap( #if STANDALONE display, MI_WINDOW(mi), #else mi, #endif lp->cmap, lp->colors, &lp->ncolors, True, &lp->cycle_p); } XInstallColormap(display, lp->cmap); if (lp->ncolors < 2) { lp->ncolors = 2; lp->no_colors = True; } else lp->no_colors = False; if (lp->ncolors <= 2) lp->mono_p = True; if (lp->mono_p) lp->cycle_p = False; } if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) { if (lp->mono_p) { lp->cur_color = MI_BLACK_PIXEL(mi); } } lp->seq = (unsigned long) -1; lp->hbit = -1; lp->iter_local = -1; } void draw_lyapunov(ModeInfo * mi) { Display *display = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); GC gc = MI_GC(mi); int h; lyapstruct *lp; if (lyaps == NULL) return; lp = &lyaps[MI_SCREEN(mi)]; MI_IS_DRAWN(mi) = True; if (MI_IS_INSTALL(mi) && MI_NPIXELS(mi) > 2) { if (lp->mono_p) { XSetForeground(display, lp->gc, lp->cur_color); } else { lp->cur_color = (lp->cur_color + 1) % lp->ncolors; XSetForeground(display, lp->gc, lp->colors[lp->cur_color].pixel); } } else { if (MI_NPIXELS(mi) > 2) XSetForeground(display, lp->gc, MI_PIXEL(mi, lp->cur_color)); else if (lp->cur_color) XSetForeground(display, lp->gc, MI_BLACK_PIXEL(mi)); else XSetForeground(display, lp->gc, MI_WHITE_PIXEL(mi)); if (++lp->cur_color >= (unsigned int) MI_NPIXELS(mi)) lp->cur_color = 0; } /* Rotate colours */ if (lp->cycle_p) { rotate_colors(display, lp->cmap, lp->colors, lp->ncolors, lp->direction); if (!(LRAND() % 1000)) lp->direction = -lp->direction; } /* so we iterate columns beyond the width of the physical screen, ** so that we just wait around and show what we've done */ if ((!lp->backwards && (lp->column >= 4 * lp->screen_width)) || (lp->backwards && (lp->column < -3 * lp->screen_width))) { /* reset to left edge of screen */ init_lyapunov(mi); return; /* select a new region! */ } else if (lp->column >= lp->screen_width || lp->column < 0) { /* delay a while */ if (lp->backwards) lp->column--; else lp->column++; return; } for (h = 0; h < lp->screen_height; h++) { unsigned int color; double result; #define MULT 1.0 result = reps(lp, lp->periodic_forcing, lp->firstone, MI_COUNT(mi), ((double) lp->column) / (MULT * lp->screen_width), ((double) h) / (MULT * lp->screen_height)); #ifdef DEBUG if (debug) (void) printf("a %g, b %g, result %g\n", ((double) lp->column) / lp->screen_width, ((double) h) / lp->screen_height, result); #endif if (result > 0.0) XSetForeground(display, gc, MI_BLACK_PIXEL(mi)); else { color=(unsigned int) ((MI_NPIXELS(mi) * (float)-result) / lp->reptop); color = color % MI_NPIXELS(mi); XSetForeground(display, gc, MI_PIXEL(mi, color)); } /* we no longer have vertical symmetry - so we compute all points ** and do not draw with redundancy */ XDrawPoint(display, window, gc, lp->column, h); } if (lp->backwards) lp->column--; else lp->column++; } void release_lyapunov(ModeInfo * mi) { if (lyaps != NULL) { int screen; for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) free_lyapunov(MI_DISPLAY(mi), &lyaps[screen]); free(lyaps); lyaps = (lyapstruct *) NULL; } } #endif /* MODE_lyapunov */