357 lines
8.4 KiB
C
357 lines
8.4 KiB
C
|
/* -*- Mode: C; tab-width: 4 -*- */
|
||
|
/* flame --- recursive fractal cosmic flames */
|
||
|
|
||
|
#if !defined( lint ) && !defined( SABER )
|
||
|
static const char sccsid[] = "@(#)flame.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: Compatible with xscreensaver
|
||
|
* 11-Aug-1995: Got rid of polynomial since it was crashing xlock on some
|
||
|
* machines.
|
||
|
* 01-Jun-1995: This should look more like the original with some updates 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_flame
|
||
|
#define PROGCLASS "Flame"
|
||
|
#define HACK_INIT init_flame
|
||
|
#define HACK_DRAW draw_flame
|
||
|
#define flame_opts xlockmore_opts
|
||
|
#define DEFAULTS "*delay: 750000 \n" \
|
||
|
"*count: 20 \n" \
|
||
|
"*cycles: 10000 \n" \
|
||
|
"*ncolors: 200 \n"
|
||
|
#define UNIFORM_COLORS
|
||
|
#define BRIGHT_COLORS
|
||
|
#include "xlockmore.h" /* in xscreensaver distribution */
|
||
|
#else /* STANDALONE */
|
||
|
#include "xlock.h" /* in xlockmore distribution */
|
||
|
|
||
|
#endif /* STANDALONE */
|
||
|
|
||
|
#ifdef MODE_flame
|
||
|
|
||
|
ModeSpecOpt flame_opts =
|
||
|
{0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
|
||
|
|
||
|
#ifdef USE_MODULES
|
||
|
ModStruct flame_description =
|
||
|
{"flame", "init_flame", "draw_flame", "release_flame",
|
||
|
"refresh_flame", "init_flame", (char *) NULL, &flame_opts,
|
||
|
750000, 20, 10000, 1, 64, 1.0, "",
|
||
|
"Shows cosmic flame fractals", 0, NULL};
|
||
|
|
||
|
#endif
|
||
|
|
||
|
#define MAXLEV 4
|
||
|
#define MAXKINDS 9
|
||
|
#define MAXBATCH 12
|
||
|
|
||
|
typedef struct {
|
||
|
double f[2][3][MAXLEV]; /* three non-homogeneous transforms */
|
||
|
int variation;
|
||
|
int max_levels;
|
||
|
int cur_level;
|
||
|
int snum;
|
||
|
int anum;
|
||
|
int width, height;
|
||
|
int num_points;
|
||
|
int total_points;
|
||
|
int pixcol;
|
||
|
int cycles;
|
||
|
int alt;
|
||
|
XPoint pts[MAXBATCH];
|
||
|
short lasthalf;
|
||
|
} flamestruct;
|
||
|
|
||
|
static flamestruct *flames = (flamestruct *) NULL;
|
||
|
|
||
|
static short
|
||
|
halfrandom(flamestruct * fp, int mv)
|
||
|
{
|
||
|
unsigned long r;
|
||
|
|
||
|
if (fp->lasthalf) {
|
||
|
r = fp->lasthalf;
|
||
|
fp->lasthalf = 0;
|
||
|
} else {
|
||
|
r = LRAND();
|
||
|
fp->lasthalf = (short) (r >> 16);
|
||
|
}
|
||
|
return r % mv;
|
||
|
}
|
||
|
|
||
|
static Bool
|
||
|
recurse(ModeInfo * mi, flamestruct * fp,
|
||
|
register double x, register double y, register int l)
|
||
|
{
|
||
|
int i;
|
||
|
double nx, ny;
|
||
|
|
||
|
if (l == fp->max_levels) {
|
||
|
fp->total_points++;
|
||
|
if (fp->total_points > fp->cycles) /* how long each fractal runs */
|
||
|
return False;
|
||
|
|
||
|
if (x > -1.0 && x < 1.0 && y > -1.0 && y < 1.0) {
|
||
|
fp->pts[fp->num_points].x = (int) ((fp->width / 2) * (x + 1.0));
|
||
|
fp->pts[fp->num_points].y = (int) ((fp->height / 2) * (y + 1.0));
|
||
|
fp->num_points++;
|
||
|
if (fp->num_points >= MAXBATCH) { /* point buffer size */
|
||
|
XDrawPoints(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), fp->pts,
|
||
|
fp->num_points, CoordModeOrigin);
|
||
|
fp->num_points = 0;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
for (i = 0; i < fp->snum; i++) {
|
||
|
nx = fp->f[0][0][i] * x + fp->f[0][1][i] * y + fp->f[0][2][i];
|
||
|
ny = fp->f[1][0][i] * x + fp->f[1][1][i] * y + fp->f[1][2][i];
|
||
|
if (i < fp->anum) {
|
||
|
switch (fp->variation) {
|
||
|
case 0: /* sinusoidal */
|
||
|
nx = sin(nx);
|
||
|
ny = sin(ny);
|
||
|
break;
|
||
|
case 1: /* complex */
|
||
|
{
|
||
|
double r2 = nx * nx + ny * ny + 1e-6;
|
||
|
|
||
|
nx = nx / r2;
|
||
|
ny = ny / r2;
|
||
|
}
|
||
|
break;
|
||
|
case 2: /* bent */
|
||
|
if (nx < 0.0)
|
||
|
nx = nx * 2.0;
|
||
|
if (ny < 0.0)
|
||
|
ny = ny / 2.0;
|
||
|
break;
|
||
|
case 3: /* 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 4: /* 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 5: /* 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;
|
||
|
case 6: /* 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;
|
||
|
case 7: /* spherical */
|
||
|
{
|
||
|
double r = 0.5 + sqrt(nx * nx + ny * ny + 1e-6);
|
||
|
|
||
|
nx = nx / r;
|
||
|
ny = ny / r;
|
||
|
}
|
||
|
break;
|
||
|
case 8: /* */
|
||
|
nx = atan(nx) / M_PI_2;
|
||
|
ny = atan(ny) / M_PI_2;
|
||
|
break;
|
||
|
#if 0
|
||
|
/* core dumps on some machines, why not all? */
|
||
|
case 9: /* 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;
|
||
|
}
|
||
|
break;
|
||
|
case 10: /* polynomial */
|
||
|
if (nx < 0)
|
||
|
nx = -nx * nx;
|
||
|
else
|
||
|
nx = nx * nx;
|
||
|
if (ny < 0)
|
||
|
ny = -ny * ny;
|
||
|
else
|
||
|
ny = ny * ny;
|
||
|
break;
|
||
|
#endif
|
||
|
default:
|
||
|
nx = sin(nx);
|
||
|
ny = sin(ny);
|
||
|
}
|
||
|
}
|
||
|
if (!recurse(mi, fp, nx, ny, l + 1))
|
||
|
return False;
|
||
|
}
|
||
|
}
|
||
|
return True;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
init_flame(ModeInfo * mi)
|
||
|
{
|
||
|
Display *display = MI_DISPLAY(mi);
|
||
|
GC gc = MI_GC(mi);
|
||
|
flamestruct *fp;
|
||
|
|
||
|
if (flames == NULL) {
|
||
|
if ((flames = (flamestruct *) calloc(MI_NUM_SCREENS(mi),
|
||
|
sizeof (flamestruct))) == NULL)
|
||
|
return;
|
||
|
}
|
||
|
fp = &flames[MI_SCREEN(mi)];
|
||
|
|
||
|
fp->width = MI_WIDTH(mi);
|
||
|
fp->height = MI_HEIGHT(mi);
|
||
|
if (MI_COUNT(mi) < 1)
|
||
|
fp->max_levels = 1;
|
||
|
else
|
||
|
fp->max_levels = MI_COUNT(mi);
|
||
|
fp->cycles = MI_CYCLES(mi);
|
||
|
|
||
|
MI_CLEARWINDOW(mi);
|
||
|
|
||
|
if (MI_NPIXELS(mi) > 2) {
|
||
|
fp->pixcol = halfrandom(fp, MI_NPIXELS(mi));
|
||
|
XSetForeground(display, gc, MI_PIXEL(mi, fp->pixcol));
|
||
|
} else {
|
||
|
XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
|
||
|
}
|
||
|
fp->variation = NRAND(MAXKINDS);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
draw_flame(ModeInfo * mi)
|
||
|
{
|
||
|
Display *display = MI_DISPLAY(mi);
|
||
|
int i, j, k;
|
||
|
flamestruct *fp;
|
||
|
|
||
|
if (flames == NULL)
|
||
|
return;
|
||
|
fp = &flames[MI_SCREEN(mi)];
|
||
|
|
||
|
if (!(fp->cur_level++ % fp->max_levels)) {
|
||
|
MI_CLEARWINDOW(mi);
|
||
|
if (MI_NPIXELS(mi) <= 2)
|
||
|
XSetForeground(display, MI_GC(mi), MI_WHITE_PIXEL(mi));
|
||
|
fp->alt = !fp->alt;
|
||
|
} else {
|
||
|
if (MI_NPIXELS(mi) > 2) {
|
||
|
XSetForeground(display, MI_GC(mi), MI_PIXEL(mi, fp->pixcol));
|
||
|
if (--fp->pixcol < 0)
|
||
|
fp->pixcol = MI_NPIXELS(mi) - 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
MI_IS_DRAWN(mi) = True;
|
||
|
|
||
|
/* number of functions */
|
||
|
fp->snum = 2 + (fp->cur_level % (MAXLEV - 1));
|
||
|
|
||
|
/* how many of them are of alternate form */
|
||
|
if (fp->alt)
|
||
|
fp->anum = 0;
|
||
|
else
|
||
|
fp->anum = halfrandom(fp, fp->snum) + 2;
|
||
|
|
||
|
/* 6 coefs per function */
|
||
|
for (k = 0; k < fp->snum; k++) {
|
||
|
for (i = 0; i < 2; i++)
|
||
|
for (j = 0; j < 3; j++)
|
||
|
fp->f[i][j][k] = ((double) (LRAND() & 1023) / 512.0 - 1.0);
|
||
|
}
|
||
|
fp->num_points = 0;
|
||
|
fp->total_points = 0;
|
||
|
(void) recurse(mi, fp, 0.0, 0.0, 0);
|
||
|
XDrawPoints(display, MI_WINDOW(mi), MI_GC(mi),
|
||
|
fp->pts, fp->num_points, CoordModeOrigin);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
release_flame(ModeInfo * mi)
|
||
|
{
|
||
|
if (flames != NULL) {
|
||
|
free(flames);
|
||
|
flames = (flamestruct *) NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
refresh_flame(ModeInfo * mi)
|
||
|
{
|
||
|
MI_CLEARWINDOW(mi);
|
||
|
}
|
||
|
|
||
|
#endif /* MODE_flame */
|