xenocara/app/xlockmore/modes/lyapunov.c
2006-11-26 11:07:42 +00:00

515 lines
14 KiB
C

/* -*- 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; i<ta_idx; i++)
r += log(ABS(ta[i]));
return r < 0 ? (r * lp->norm) : 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 <i = 2;> 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 */