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

674 lines
15 KiB
C

/* -*- Mode: C; tab-width: 4 -*- */
/* drift --- drifting recursive fractal cosmic flames */
#if !defined( lint ) && !defined( SABER )
static const char sccsid[] = "@(#)drift.c 5.00 2000/11/01 xlockmore";
#endif
/*-
* Copyright (c) 1991 by Patrick J. Naughton.
*
* 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.
*
* Revision History:
* 01-Nov-2000: Allocation checks
* 10-May-1997: Jamie Zawinski <jwz@jwz.org> compatible with xscreensaver
* 01-Jan-1997: Moved new flame to drift. Compile time options now run time.
* 01-Jun-1995: Updated by Scott Draves.
* 27-Jun-1991: vary number of functions used.
* 24-Jun-1991: fixed portability problem with integer mod (%).
* 06-Jun-1991: Written, received from Scott Draves <spot@cs.cmu.edu>
*/
#ifdef STANDALONE
#define MODE_drift
#define PROGCLASS "Drift"
#define HACK_INIT init_drift
#define HACK_DRAW draw_drift
#define drift_opts xlockmore_opts
#define DEFAULTS "*delay: 10000 \n" \
"*count: 30 \n" \
"*ncolors: 200 \n" \
"*fullrandom: True \n"
#define SMOOTH_COLORS
#include "xlockmore.h" /* in xscreensaver distribution */
#else /* STANDALONE */
#include "xlock.h" /* in xlockmore distribution */
#endif /* STANDALONE */
#ifdef MODE_drift
#define DEF_GROW "False" /* Grow fractals instead of animating one at a time,
would then be like flame */
#define DEF_LISS "False" /* if this is defined then instead of a point
bouncing around in a high dimensional sphere, we
use lissojous figures. Only makes sense if
grow is false. */
static Bool grow;
static Bool liss;
static XrmOptionDescRec opts[] =
{
{(char *) "-grow", (char *) ".drift.grow", XrmoptionNoArg, (caddr_t) "on"},
{(char *) "+grow", (char *) ".drift.grow", XrmoptionNoArg, (caddr_t) "off"},
{(char *) "-liss", (char *) ".drift.trail", XrmoptionNoArg, (caddr_t) "on"},
{(char *) "+liss", (char *) ".drift.trail", XrmoptionNoArg, (caddr_t) "off"}
};
static argtype vars[] =
{
{(void *) & grow, (char *) "grow", (char *) "Grow", (char *) DEF_GROW, t_Bool},
{(void *) & liss, (char *) "liss", (char *) "Liss", (char *) DEF_LISS, t_Bool}
};
static OptionStruct desc[] =
{
{(char *) "-/+grow", (char *) "turn on/off growing fractals, else they are animated"},
{(char *) "-/+liss", (char *) "turn on/off using lissojous figures to get points"}
};
ModeSpecOpt drift_opts =
{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
#ifdef USE_MODULES
ModStruct drift_description =
{"drift", "init_drift", "draw_drift", "release_drift",
"refresh_drift", "init_drift", (char *) NULL, &drift_opts,
10000, 30, 1, 1, 64, 1.0, "",
"Shows cosmic drifting flame fractals", 0, NULL};
#endif
#define MAXBATCH1 200 /* mono */
#define MAXBATCH2 20 /* color */
#define FUSE 10 /* discard this many initial iterations */
#define NMAJORVARS 7
#define MAXLEV 10
typedef struct {
/* shape of current flame */
int nxforms;
double f[2][3][MAXLEV]; /* a bunch of non-homogeneous xforms */
int variation[10]; /* for each xform */
/* Animation */
double df[2][3][MAXLEV];
/* high-level control */
int mode; /* 0->slow/single 1->fast/many */
int nfractals; /* draw this many fractals */
int major_variation;
int fractal_len; /* pts/fractal */
int color;
int rainbow; /* more than one color per fractal
1-> computed by adding dimension to fractal */
int width, height; /* of window */
int timer;
/* draw info about current flame */
int fuse; /* iterate this many before drawing */
int total_points; /* draw this many pts before fractal ends */
int npoints; /* how many we've computed but not drawn */
XPoint pts[MAXBATCH1]; /* here they are */
unsigned long pixcol;
/* when drawing in color, we have a buffer per color */
int *ncpoints;
XPoint *cpts;
double x, y, c;
int liss_time;
Bool grow, liss;
short lasthalf;
long saved_random_bits;
int nbits;
} driftstruct;
static driftstruct *drifts = (driftstruct *) NULL;
static short
halfrandom(driftstruct * dp, int mv)
{
unsigned long r;
if (dp->lasthalf) {
r = dp->lasthalf;
dp->lasthalf = 0;
} else {
r = LRAND();
dp->lasthalf = (short) (r >> 16);
}
r = r % mv;
return r;
}
static int
frandom(driftstruct * dp, int n)
{
int result;
if (3 > dp->nbits) {
dp->saved_random_bits = LRAND();
dp->nbits = 31;
}
switch (n) {
case 2:
result = (int) (dp->saved_random_bits & 1);
dp->saved_random_bits >>= 1;
dp->nbits -= 1;
return result;
case 3:
result = (int) (dp->saved_random_bits & 3);
dp->saved_random_bits >>= 2;
dp->nbits -= 2;
if (3 == result)
return frandom(dp, 3);
return result;
case 4:
result = (int) (dp->saved_random_bits & 3);
dp->saved_random_bits >>= 2;
dp->nbits -= 2;
return result;
case 5:
result = (int) (dp->saved_random_bits & 7);
dp->saved_random_bits >>= 3;
dp->nbits -= 3;
if (4 < result)
return frandom(dp, 5);
return result;
default:
(void) fprintf(stderr, "bad arg to frandom\n");
}
return 0;
}
#define DISTRIB_A (halfrandom(dp, 7000) + 9000)
#define DISTRIB_B ((frandom(dp, 3) + 1) * (frandom(dp, 3) + 1) * 120000)
#define LEN(x) (sizeof(x)/sizeof((x)[0]))
static void
initmode(ModeInfo * mi, int mode)
{
driftstruct *dp = &drifts[MI_SCREEN(mi)];
#define VARIATION_LEN 14
dp->mode = mode;
dp->major_variation = halfrandom(dp, VARIATION_LEN);
/* 0, 0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 6, 6 */
dp->major_variation = ((dp->major_variation >= VARIATION_LEN >> 1) &&
(dp->major_variation < VARIATION_LEN - 1)) ?
(dp->major_variation + 1) >> 1 : dp->major_variation >> 1;
if (dp->grow) {
dp->rainbow = 0;
if (mode) {
if (!dp->color || halfrandom(dp, 8)) {
dp->nfractals = halfrandom(dp, 30) + 5;
dp->fractal_len = DISTRIB_A;
} else {
dp->nfractals = halfrandom(dp, 5) + 5;
dp->fractal_len = DISTRIB_B;
}
} else {
dp->rainbow = dp->color;
dp->nfractals = 1;
dp->fractal_len = DISTRIB_B;
}
} else {
dp->nfractals = 1;
dp->rainbow = dp->color;
dp->fractal_len = 2000000;
}
dp->fractal_len = (dp->fractal_len * MI_COUNT(mi)) / 20;
MI_CLEARWINDOW(mi);
}
static void
pick_df_coefs(ModeInfo * mi)
{
driftstruct *dp = &drifts[MI_SCREEN(mi)];
int i, j, k;
double r;
for (i = 0; i < dp->nxforms; i++) {
r = 1e-6;
for (j = 0; j < 2; j++)
for (k = 0; k < 3; k++) {
dp->df[j][k][i] = ((double) halfrandom(dp, 1000) / 500.0 - 1.0);
r += dp->df[j][k][i] * dp->df[j][k][i];
}
r = (3 + halfrandom(dp, 5)) * 0.01 / sqrt(r);
for (j = 0; j < 2; j++)
for (k = 0; k < 3; k++)
dp->df[j][k][i] *= r;
}
}
static void
free_drift(driftstruct *dp)
{
if (dp->ncpoints != NULL) {
free(dp->ncpoints);
dp->ncpoints = (int *) NULL;
}
if (dp->cpts != NULL) {
free(dp->cpts);
dp->cpts = (XPoint *) NULL;
}
}
static void
initfractal(ModeInfo * mi)
{
driftstruct *dp = &drifts[MI_SCREEN(mi)];
int i, j, k;
#define XFORM_LEN 9
dp->fuse = FUSE;
dp->total_points = 0;
if (!dp->ncpoints) {
if ((dp->ncpoints = (int *) malloc(sizeof (int) * MI_NCOLORS(mi))) ==
NULL) {
free_drift(dp);
return;
}
}
if (!dp->cpts) {
if ((dp->cpts = (XPoint *) malloc(MAXBATCH2 * sizeof (XPoint) *
MI_NCOLORS(mi))) == NULL) {
free_drift(dp);
return;
}
}
if (dp->rainbow)
for (i = 0; i < MI_NPIXELS(mi); i++)
dp->ncpoints[i] = 0;
else
dp->npoints = 0;
dp->nxforms = halfrandom(dp, XFORM_LEN);
/* 2, 2, 2, 3, 3, 3, 4, 4, 5 */
dp->nxforms = (dp->nxforms >= XFORM_LEN - 1) + dp->nxforms / 3 + 2;
dp->c = dp->x = dp->y = 0.0;
if (dp->liss && !halfrandom(dp, 10)) {
dp->liss_time = 0;
}
if (!dp->grow)
pick_df_coefs(mi);
for (i = 0; i < dp->nxforms; i++) {
if (NMAJORVARS == dp->major_variation)
dp->variation[i] = halfrandom(dp, NMAJORVARS);
else
dp->variation[i] = dp->major_variation;
for (j = 0; j < 2; j++)
for (k = 0; k < 3; k++) {
if (dp->liss)
dp->f[j][k][i] = sin(dp->liss_time * dp->df[j][k][i]);
else
dp->f[j][k][i] = ((double) halfrandom(dp, 1000) / 500.0 - 1.0);
}
}
if (dp->color)
dp->pixcol = MI_PIXEL(mi, halfrandom(dp, MI_NPIXELS(mi)));
else
dp->pixcol = MI_WHITE_PIXEL(mi);
}
void
init_drift(ModeInfo * mi)
{
driftstruct *dp;
if (drifts == NULL) {
if ((drifts = (driftstruct *) calloc(MI_NUM_SCREENS(mi),
sizeof (driftstruct))) == NULL)
return;
}
dp = &drifts[MI_SCREEN(mi)];
dp->width = MI_WIDTH(mi);
dp->height = MI_HEIGHT(mi);
dp->color = MI_NPIXELS(mi) > 2;
if (MI_IS_FULLRANDOM(mi)) {
if (NRAND(3) == 0)
dp->grow = True;
else {
dp->grow = False;
dp->liss = (Bool) (LRAND() & 1);
}
} else {
dp->grow = grow;
if (dp->grow)
dp->liss = False;
else
dp->liss = liss;
}
initmode(mi, 1);
initfractal(mi);
}
static void
iter(driftstruct * dp)
{
int i = frandom(dp, dp->nxforms);
double nx, ny, nc;
if (i)
nc = (dp->c + 1.0) / 2.0;
else
nc = dp->c / 2.0;
nx = dp->f[0][0][i] * dp->x + dp->f[0][1][i] * dp->y + dp->f[0][2][i];
ny = dp->f[1][0][i] * dp->x + dp->f[1][1][i] * dp->y + dp->f[1][2][i];
switch (dp->variation[i]) {
case 1:
/* sinusoidal */
nx = sin(nx);
ny = sin(ny);
break;
case 2:
{
/* complex */
double r2 = nx * nx + ny * ny + 1e-6;
nx = nx / r2;
ny = ny / r2;
break;
}
case 3:
/* bent */
if (nx < 0.0)
nx = nx * 2.0;
if (ny < 0.0)
ny = ny / 2.0;
break;
case 4:
{
/* swirl */
double r = (nx * nx + ny * ny); /* times k here is fun */
double c1 = sin(r);
double c2 = cos(r);
double t = nx;
if (nx > 1e4 || nx < -1e4 || ny > 1e4 || ny < -1e4)
ny = 1e4;
else
ny = c2 * t + c1 * ny;
nx = c1 * nx - c2 * ny;
break;
}
case 5:
{
/* horseshoe */
double r, c1, c2, t;
/* Avoid atan2: DOMAIN error message */
if (nx == 0.0 && ny == 0.0)
r = 0.0;
else
r = atan2(nx, ny); /* times k here is fun */
c1 = sin(r);
c2 = cos(r);
t = nx;
nx = c1 * nx - c2 * ny;
ny = c2 * t + c1 * ny;
break;
}
case 6:
{
/* drape */
double t;
/* Avoid atan2: DOMAIN error message */
if (nx == 0.0 && ny == 0.0)
t = 0.0;
else
t = atan2(nx, ny) / M_PI;
if (nx > 1e4 || nx < -1e4 || ny > 1e4 || ny < -1e4)
ny = 1e4;
else
ny = sqrt(nx * nx + ny * ny) - 1.0;
nx = t;
break;
}
}
#if 0
/* here are some others */
{
/* broken */
if (nx > 1.0)
nx = nx - 1.0;
if (nx < -1.0)
nx = nx + 1.0;
if (ny > 1.0)
ny = ny - 1.0;
if (ny < -1.0)
ny = ny + 1.0;
break;
}
{
/* complex sine */
double u = nx, v = ny;
double ev = exp(v);
double emv = exp(-v);
nx = (ev + emv) * sin(u) / 2.0;
ny = (ev - emv) * cos(u) / 2.0;
}
{
/* polynomial */
if (nx < 0)
nx = -nx * nx;
else
nx = nx * nx;
if (ny < 0)
ny = -ny * ny;
else
ny = ny * ny;
}
{
/* spherical */
double r = 0.5 + sqrt(nx * nx + ny * ny + 1e-6);
nx = nx / r;
ny = ny / r;
}
{
nx = atan(nx) / M_PI_2
ny = atan(ny) / M_PI_2
}
#endif
/* how to check nan too? some machines don't have finite().
don't need to check ny, it'll propogate */
if (nx > 1e4 || nx < -1e4) {
nx = halfrandom(dp, 1000) / 500.0 - 1.0;
ny = halfrandom(dp, 1000) / 500.0 - 1.0;
dp->fuse = FUSE;
}
dp->x = nx;
dp->y = ny;
dp->c = nc;
}
static void
draw(ModeInfo * mi, driftstruct * dp, Drawable d)
{
Display *display = MI_DISPLAY(mi);
GC gc = MI_GC(mi);
double x = dp->x;
double y = dp->y;
int fixed_x, fixed_y, npix, c, n;
if (dp->fuse) {
dp->fuse--;
return;
}
if (!(x > -1.0 && x < 1.0 && y > -1.0 && y < 1.0))
return;
fixed_x = (int) ((dp->width / 2) * (x + 1.0));
fixed_y = (int) ((dp->height / 2) * (y + 1.0));
if (!dp->rainbow) {
dp->pts[dp->npoints].x = fixed_x;
dp->pts[dp->npoints].y = fixed_y;
dp->npoints++;
if (dp->npoints == MAXBATCH1) {
XSetForeground(display, gc, dp->pixcol);
XDrawPoints(display, d, gc, dp->pts, dp->npoints, CoordModeOrigin);
dp->npoints = 0;
}
} else {
npix = MI_NPIXELS(mi);
c = (int) (dp->c * npix);
if (c < 0)
c = 0;
if (c >= npix)
c = npix - 1;
n = dp->ncpoints[c];
dp->cpts[c * MAXBATCH2 + n].x = fixed_x;
dp->cpts[c * MAXBATCH2 + n].y = fixed_y;
if (++dp->ncpoints[c] == MAXBATCH2) {
XSetForeground(display, gc, MI_PIXEL(mi, c));
XDrawPoints(display, d, gc, &(dp->cpts[c * MAXBATCH2]),
dp->ncpoints[c], CoordModeOrigin);
dp->ncpoints[c] = 0;
}
}
}
static void
draw_flush(ModeInfo * mi, driftstruct * dp, Drawable d)
{
Display *display = MI_DISPLAY(mi);
GC gc = MI_GC(mi);
if (dp->rainbow) {
int npix = MI_NPIXELS(mi);
int i;
for (i = 0; i < npix; i++) {
if (dp->ncpoints[i]) {
XSetForeground(display, gc, MI_PIXEL(mi, i));
XDrawPoints(display, d, gc, &(dp->cpts[i * MAXBATCH2]),
dp->ncpoints[i], CoordModeOrigin);
dp->ncpoints[i] = 0;
}
}
} else {
if (dp->npoints)
XSetForeground(display, gc, dp->pixcol);
XDrawPoints(display, d, gc, dp->pts,
dp->npoints, CoordModeOrigin);
dp->npoints = 0;
}
}
void
draw_drift(ModeInfo * mi)
{
Window window = MI_WINDOW(mi);
driftstruct *dp;
if (drifts == NULL)
return;
dp = &drifts[MI_SCREEN(mi)];
if (dp->ncpoints == NULL)
return;
MI_IS_DRAWN(mi) = True;
dp->timer = 3000;
while (dp->timer) {
iter(dp);
draw(mi, dp, window);
if (dp->total_points++ > dp->fractal_len) {
draw_flush(mi, dp, window);
if (0 == --dp->nfractals) {
initmode(mi, frandom(dp, 2));
}
initfractal(mi);
}
dp->timer--;
}
if (!dp->grow) {
int i, j, k;
draw_flush(mi, dp, window);
if (dp->liss)
dp->liss_time++;
for (i = 0; i < dp->nxforms; i++)
for (j = 0; j < 2; j++)
for (k = 0; k < 3; k++) {
if (dp->liss)
dp->f[j][k][i] = sin(dp->liss_time * dp->df[j][k][i]);
else {
double t = dp->f[j][k][i] += dp->df[j][k][i];
if (t < -1.0 || 1.0 < t)
dp->df[j][k][i] *= -1.0;
}
}
}
}
void
release_drift(ModeInfo * mi)
{
if (drifts != NULL) {
int screen;
for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
free_drift(&drifts[screen]);
free(drifts);
drifts = (driftstruct *) NULL;
}
}
void
refresh_drift(ModeInfo * mi)
{
MI_CLEARWINDOW(mi);
}
#endif /* MODE_drift */