/* -*- 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 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 */ #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 */