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

476 lines
12 KiB
C

/* -*- Mode: C; tab-width: 4 -*- */
/* worm --- draw wiggly worms */
#if !defined( lint ) && !defined( SABER )
static const char sccsid[] = "@(#)worm.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
* 03-Sep-1996: fixed bug in allocation of space for worms, added 3d support
* Henrik Theiling <theiling@coli.uni-sb.de>
* 27-Sep-1995: put back malloc
* 23-Sep-1993: got rid of "rint". (David Bagley)
* 27-Sep-1991: got rid of all malloc calls since there were no calls to free().
* 25-Sep-1991: Integrated into X11R5 contrib xlock.
*
* Adapted from a concept in the Dec 87 issue of Scientific American p. 142.
*
* SunView version: Brad Taylor <brad@sun.com>
* X11 version: Dave Lemke <lemke@ncd.com>
* xlock version: Boris Putanec <bp@cs.brown.edu>
*/
#ifdef STANDALONE
#define MODE_worm
#define PROGCLASS "Worm"
#define HACK_INIT init_worm
#define HACK_DRAW draw_worm
#define worm_opts xlockmore_opts
#define DEFAULTS "*delay: 17000 \n" \
"*count: -20 \n" \
"*cycles: 10 \n" \
"*size: -3 \n" \
"*ncolors: 200 \n" \
"*use3d: False \n" \
"*delta3d: 1.5 \n" \
"*right3d: red \n" \
"*left3d: blue \n" \
"*both3d: magenta \n" \
"*none3d: black \n"
#define SMOOTH_COLORS
#include "xlockmore.h" /* in xscreensaver distribution */
#else /* STANDALONE */
#include "xlock.h" /* in xlockmore distribution */
#endif /* STANDALONE */
#ifdef MODE_worm
ModeSpecOpt worm_opts =
{0, (XrmOptionDescRec *) NULL, 0, (argtype *) NULL, (OptionStruct *) NULL};
#ifdef USE_MODULES
ModStruct worm_description =
{"worm", "init_worm", "draw_worm", "release_worm",
"refresh_worm", "init_worm", (char *) NULL, &worm_opts,
17000, -20, 10, -3, 64, 1.0, "",
"Shows wiggly worms", 0, NULL};
#endif
#define MINSIZE 1
#define SEGMENTS 36
#define MINWORMS 1
#define MAXZ 750
#define MINZ 100
#define SCREENZ 200
#define GETZDIFF(z) (MI_DELTA3D(mi)*20.0*(1.0-(SCREENZ)/((float)(z)+MINZ)))
#define IRINT(x) ((int)(((x)>0.0)?(x)+0.5:(x)-0.5))
/* How many segments to draw per cycle when redrawing */
#define REDRAWSTEP 3
typedef struct {
XPoint *circ;
int *diffcirc;
int dir, dir2;
int tail;
int x, y, z;
int redrawing, redrawpos;
} wormstuff;
typedef struct {
int xsize, ysize, zsize;
int wormlength;
int nc;
int nw;
int circsize;
wormstuff *worm;
XRectangle *rects;
int maxsize;
int *size;
unsigned int chromo;
} wormstruct;
static float sintab[SEGMENTS];
static float costab[SEGMENTS];
static int init_table = 0;
static wormstruct *worms = (wormstruct *) NULL;
static void
worm_doit(ModeInfo * mi, int which, unsigned long color)
{
Display *display = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
GC gc = MI_GC(mi);
wormstruct *wp = &worms[MI_SCREEN(mi)];
wormstuff *ws = &wp->worm[which];
int x, y, z;
int diff;
ws->tail++;
if (ws->tail == wp->wormlength)
ws->tail = 0;
x = ws->circ[ws->tail].x;
y = ws->circ[ws->tail].y;
if (MI_IS_USE3D(mi)) {
diff = ws->diffcirc[ws->tail];
if (MI_IS_INSTALL(mi)) {
XSetForeground(display, gc, MI_NONE_COLOR(mi));
} else {
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
}
XFillRectangle(display, window, gc, x - diff, y,
wp->circsize, wp->circsize);
XFillRectangle(display, window, gc, x + diff, y,
wp->circsize, wp->circsize);
} else {
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
XFillRectangle(display, window, gc, x, y, wp->circsize, wp->circsize);
}
if (LRAND() & 1)
ws->dir = (ws->dir + 1) % SEGMENTS;
else
ws->dir = (ws->dir + SEGMENTS - 1) % SEGMENTS;
x = (ws->x + IRINT((float) wp->circsize * costab[ws->dir]) +
wp->xsize) % wp->xsize;
y = (ws->y + IRINT((float) wp->circsize * sintab[ws->dir]) +
wp->ysize) % wp->ysize;
ws->circ[ws->tail].x = x;
ws->circ[ws->tail].y = y;
ws->x = x;
ws->y = y;
if (MI_IS_USE3D(mi)) {
if (LRAND() & 1)
ws->dir2 = (ws->dir2 + 1) % SEGMENTS;
else
ws->dir2 = (ws->dir2 + SEGMENTS - 1) % SEGMENTS;
/* for the z-axis the wrap-around looks bad,
* so worms should just turn around.
*/
z = (int) (ws->z + wp->circsize * sintab[ws->dir2]);
if (z < 0 || z >= wp->zsize)
z = (int) (ws->z - wp->circsize * sintab[ws->dir2]);
diff = (int) (GETZDIFF(z) + 0.5); /* ROUND */
ws->diffcirc[ws->tail] = diff;
ws->z = z;
/* right eye */
color = 0;
wp->rects[color * wp->maxsize + wp->size[color]].x = x + diff;
wp->rects[color * wp->maxsize + wp->size[color]].y = y;
wp->size[color]++;
/* left eye */
color = 1;
wp->rects[color * wp->maxsize + wp->size[color]].x = x - diff;
wp->rects[color * wp->maxsize + wp->size[color]].y = y;
wp->size[color]++;
#if 0
if (ws->redrawing) { /* Too hard for now */
int j;
for (j = 0; j < REDRAWSTEP; j++) {
int k = (ws->tail - ws->redrawpos + wp->wormlength)
% wp->wormlength;
color = 0;
wp->rects[color * wp->maxsize + wp->size[color]].x =
ws->circ[k].x + ws->diffcirc[k];
wp->rects[color * wp->maxsize + wp->size[color]].y =
ws->circ[k].y;
wp->size[color]++;
color = 1;
wp->rects[color * wp->maxsize + wp->size[color]].x =
ws->circ[k].x - ws->diffcirc[k];
wp->rects[color * wp->maxsize + wp->size[color]].y =
ws->circ[k].y;
wp->size[color]++;
if (++(ws->redrawpos) >= wp->wormlength) {
ws->redrawing = 0;
break;
}
}
}
#endif
} else {
wp->rects[color * wp->maxsize + wp->size[color]].x = x;
wp->rects[color * wp->maxsize + wp->size[color]].y = y;
wp->size[color]++;
if (ws->redrawing) {
int j;
ws->redrawpos++;
/* Compensates for the changed ws->tail
since the last callback. */
for (j = 0; j < REDRAWSTEP; j++) {
int k = (ws->tail - ws->redrawpos + wp->wormlength)
% wp->wormlength;
wp->rects[color * wp->maxsize + wp->size[color]].x = ws->circ[k].x;
wp->rects[color * wp->maxsize + wp->size[color]].y = ws->circ[k].y;
wp->size[color]++;
if (++(ws->redrawpos) >= wp->wormlength) {
ws->redrawing = 0;
break;
}
}
}
}
}
static void
free_worm(wormstruct * wp)
{
int wn;
if (wp->worm) {
for (wn = 0; wn < wp->nw; wn++) {
if (wp->worm[wn].circ)
free(wp->worm[wn].circ);
if (wp->worm[wn].diffcirc)
free(wp->worm[wn].diffcirc);
}
free(wp->worm);
wp->worm = (wormstuff *) NULL;
}
if (wp->rects) {
free(wp->rects);
wp->rects = (XRectangle *) NULL;
}
if (wp->size) {
free(wp->size);
wp->size = (int *) NULL;
}
}
void
init_worm(ModeInfo * mi)
{
wormstruct *wp;
int size = MI_SIZE(mi);
int i, j;
if (worms == NULL) {
if ((worms = (wormstruct *) calloc(MI_NUM_SCREENS(mi),
sizeof (wormstruct))) == NULL)
return;
}
wp = &worms[MI_SCREEN(mi)];
if (MI_NPIXELS(mi) <= 2 || MI_IS_USE3D(mi))
wp->nc = 2;
else
wp->nc = MI_NPIXELS(mi);
free_worm(wp);
wp->nw = MI_COUNT(mi);
if (wp->nw < -MINWORMS)
wp->nw = NRAND(-wp->nw - MINWORMS + 1) + MINWORMS;
else if (wp->nw < MINWORMS)
wp->nw = MINWORMS;
if (!wp->worm)
if ((wp->worm = (wormstuff *) malloc(wp->nw *
sizeof (wormstuff))) == NULL) {
free_worm(wp);
return;
}
if (!wp->size)
if ((wp->size = (int *) malloc(MI_NPIXELS(mi) *
sizeof (int))) == NULL) {
free_worm(wp);
return;
}
wp->maxsize = (REDRAWSTEP + 1) * wp->nw; /* / wp->nc + 1; */
if (!wp->rects)
if ((wp->rects = (XRectangle *) malloc(wp->maxsize * MI_NPIXELS(mi) *
sizeof (XRectangle))) == NULL) {
free_worm(wp);
return;
}
if (!init_table) {
init_table = 1;
for (i = 0; i < SEGMENTS; i++) {
sintab[i] = SINF(i * 2.0 * M_PI / SEGMENTS);
costab[i] = COSF(i * 2.0 * M_PI / SEGMENTS);
}
}
wp->xsize = MI_WIDTH(mi);
wp->ysize = MI_HEIGHT(mi);
wp->zsize = MAXZ - MINZ + 1;
if (MI_NPIXELS(mi) > 2)
wp->chromo = NRAND(MI_NPIXELS(mi));
if (size < -MINSIZE)
wp->circsize = NRAND(-size - MINSIZE + 1) + MINSIZE;
else if (size < MINSIZE)
wp->circsize = MINSIZE;
else
wp->circsize = size;
for (i = 0; i < wp->nc; i++) {
for (j = 0; j < wp->maxsize; j++) {
wp->rects[i * wp->maxsize + j].width = wp->circsize;
wp->rects[i * wp->maxsize + j].height = wp->circsize;
}
}
(void) memset((char *) wp->size, 0, wp->nc * sizeof (int));
wp->wormlength = (int) sqrt((double) wp->xsize + wp->ysize) *
MI_CYCLES(mi) / 8; /* Fudge this to something reasonable */
if (wp->wormlength < 1)
wp->wormlength = 1;
for (i = 0; i < wp->nw; i++) {
if (((wp->worm[i].circ = (XPoint *) malloc(wp->wormlength *
sizeof (XPoint))) == NULL) ||
((wp->worm[i].diffcirc = (int *) malloc(wp->wormlength *
sizeof (int))) == NULL)) {
free_worm(wp);
return;
}
for (j = 0; j < wp->wormlength; j++) {
wp->worm[i].circ[j].x = wp->xsize / 2;
wp->worm[i].circ[j].y = wp->ysize / 2;
if (MI_IS_USE3D(mi))
wp->worm[i].diffcirc[j] = 0;
}
wp->worm[i].dir = NRAND(SEGMENTS);
wp->worm[i].dir2 = NRAND(SEGMENTS);
wp->worm[i].tail = 0;
wp->worm[i].x = wp->xsize / 2;
wp->worm[i].y = wp->ysize / 2;
wp->worm[i].z = SCREENZ - MINZ;
wp->worm[i].redrawing = 0;
}
if (MI_IS_INSTALL(mi) && MI_IS_USE3D(mi)) {
MI_CLEARWINDOWCOLOR(mi, MI_NONE_COLOR(mi));
} else {
MI_CLEARWINDOW(mi);
}
}
void
draw_worm(ModeInfo * mi)
{
Display *display = MI_DISPLAY(mi);
Window window = MI_WINDOW(mi);
GC gc = MI_GC(mi);
wormstruct *wp = &worms[MI_SCREEN(mi)];
unsigned long wcolor;
int i;
(void) memset((char *) wp->size, 0, wp->nc * sizeof (int));
MI_IS_DRAWN(mi) = True;
for (i = 0; i < wp->nw; i++) {
if (MI_NPIXELS(mi) > 2) {
wcolor = (i + wp->chromo) % wp->nc;
worm_doit(mi, i, wcolor);
} else
worm_doit(mi, i, (unsigned long) 0);
}
if (MI_IS_USE3D(mi)) {
if (MI_IS_INSTALL(mi))
XSetFunction(display, gc, GXor);
XSetForeground(display, gc, MI_RIGHT_COLOR(mi));
XFillRectangles(display, window, gc, &(wp->rects[0]), wp->size[0]);
XSetForeground(display, gc, MI_LEFT_COLOR(mi));
XFillRectangles(display, window, gc, &(wp->rects[wp->maxsize]), wp->size[1]);
if (MI_IS_INSTALL(mi))
XSetFunction(display, gc, GXcopy);
} else if (MI_NPIXELS(mi) > 2) {
for (i = 0; i < wp->nc; i++) {
XSetForeground(display, gc, MI_PIXEL(mi, i));
XFillRectangles(display, window, gc, &(wp->rects[i * wp->maxsize]), wp->size[i]);
}
} else {
XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
XFillRectangles(display, window, gc,
&(wp->rects[0]), wp->size[0]);
}
if (++wp->chromo == (unsigned long) wp->nc)
wp->chromo = 0;
}
void
release_worm(ModeInfo * mi)
{
if (worms != NULL) {
int screen;
for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
free_worm(&worms[screen]);
free(worms);
worms = (wormstruct *) NULL;
}
}
void
refresh_worm(ModeInfo * mi)
{
if (MI_IS_INSTALL(mi) && MI_IS_USE3D(mi)) {
MI_CLEARWINDOWCOLOR(mi, MI_NONE_COLOR(mi));
} else {
MI_CLEARWINDOW(mi);
}
/* The 3D code does drawing&clearing by XORing. We do not
want to go to too much trouble here to make it redraw
correctly. */
if (!MI_IS_USE3D(mi) && worms != NULL) {
wormstruct *wp = &worms[MI_SCREEN(mi)];
int i;
for (i = 0; i < wp->nw; i++) {
wp->worm[i].redrawing = 1;
wp->worm[i].redrawpos = 0;
}
}
}
#endif /* MODE_worm */