http://www.jwz.org/xscreensaver/xscreensaver-5.13.tar.gz
[xscreensaver] / hacks / worm.c
1
2 /* -*- Mode: C; tab-width: 4 -*- */
3 /* worm --- draw wiggly worms */
4
5 #if 0
6 static const char sccsid[] = "@(#)worm.c        4.04 97/07/28 xlockmore";
7 #endif
8
9 /*-
10  * Copyright (c) 1991 by Patrick J. Naughton.
11  *
12  * Permission to use, copy, modify, and distribute this software and its
13  * documentation for any purpose and without fee is hereby granted,
14  * provided that the above copyright notice appear in all copies and that
15  * both that copyright notice and this permission notice appear in
16  * supporting documentation.
17  *
18  * This file is provided AS IS with no warranties of any kind.  The author
19  * shall have no liability with respect to the infringement of copyrights,
20  * trade secrets or any patents by this file or any part thereof.  In no
21  * event will the author be liable for any lost revenue or profits or
22  * other special, indirect and consequential damages.
23  *
24  * Revision History:
25  * 10-May-97: Compatible with xscreensaver
26  * 03-Sep-96: fixed bug in allocation of space for worms, added 3d support
27  *            Henrik Theiling <theiling@coli.uni-sb.de>
28  * 27-Sep-95: put back malloc
29  * 23-Sep-93: got rid of "rint". (David Bagley)
30  * 27-Sep-91: got rid of all malloc calls since there were no calls to free().
31  * 25-Sep-91: Integrated into X11R5 contrib xlock.
32  *
33  * Adapted from a concept in the Dec 87 issue of Scientific American p. 142.
34  *
35  * SunView version: Brad Taylor <brad@sun.com>
36  * X11 version: Dave Lemke <lemke@ncd.com>
37  * xlock version: Boris Putanec <bp@cs.brown.edu>
38  */
39
40 #ifdef STANDALONE
41 # define DEFAULTS       "*delay:  17000 \n"     \
42                                         "*count:  -20 \n"               \
43                                         "*cycles:  10 \n"               \
44                                         "*size:   -3 \n"                \
45                                         "*ncolors: 150 \n"              \
46                                         "*use3d:   False \n"    \
47                                         "*delta3d: 1.5 \n"              \
48                                         "*right3d: red \n"              \
49                                         "*left3d:  blue \n"             \
50                                         "*both3d:  magenta \n"  \
51                                         "*none3d:  black \n" \
52                                         "*fpsSolid:  true \n" \
53
54 # define SMOOTH_COLORS
55 # define reshape_worm 0
56 # define worm_handle_event 0
57 # include "xlockmore.h"         /* in xscreensaver distribution */
58 #else /* STANDALONE */
59 # include "xlock.h"             /* in xlockmore distribution */
60 #endif /* STANDALONE */
61
62 ENTRYPOINT ModeSpecOpt worm_opts =
63 {0, NULL, 0, NULL, NULL};
64
65 #define MINSIZE 1
66
67 #define SEGMENTS  36
68 #define MINWORMS 1
69
70 #define MAXZ      750
71 #define MINZ      100
72 #define SCREENZ   200
73 #define GETZDIFF(z) (MI_DELTA3D(mi)*20.0*(1.0-(SCREENZ)/((float)(z)+MINZ)))
74 #define IRINT(x) ((int)(((x)>0.0)?(x)+0.5:(x)-0.5))
75
76 /* How many segments to draw per cycle when redrawing */
77 #define REDRAWSTEP 3
78
79 typedef struct {
80         XPoint     *circ;
81         int        *diffcirc;
82         int         dir, dir2;
83         int         tail;
84         int         x, y, z;
85         int         redrawing, redrawpos;
86 } wormstuff;
87
88 typedef struct {
89         int         xsize, ysize, zsize;
90         int         wormlength;
91         int         nc;
92         int         nw;
93         int         circsize;
94         wormstuff  *worm;
95         XRectangle *rects;      /* [NUMCOLORS * batchcount/NUMCOLORS+1] */
96         int         maxsize;
97         int        *size;
98         unsigned int chromo;
99 } wormstruct;
100
101 static float sintab[SEGMENTS];
102 static float costab[SEGMENTS];
103 static int  init_table = 0;
104
105 static wormstruct *worms = NULL;
106
107 static void
108 worm_doit(ModeInfo * mi, int which, unsigned long color)
109 {
110         Display    *display = MI_DISPLAY(mi);
111         Window      window = MI_WINDOW(mi);
112         GC          gc = MI_GC(mi);
113         wormstruct *wp = &worms[MI_SCREEN(mi)];
114         wormstuff  *ws = &wp->worm[which];
115         int         x, y, z;
116         int         diff;
117
118         ws->tail++;
119         if (ws->tail == wp->wormlength)
120                 ws->tail = 0;
121
122         x = ws->circ[ws->tail].x;
123         y = ws->circ[ws->tail].y;
124
125         if (MI_WIN_IS_USE3D(mi)) {
126                 diff = ws->diffcirc[ws->tail];
127                 if (MI_WIN_IS_INSTALL(mi)) {
128                         XSetForeground(display, gc, MI_NONE_COLOR(mi));
129                         XFillRectangle(display, window, gc, x - diff, y,
130                                        wp->circsize, wp->circsize);
131                         XFillRectangle(display, window, gc, x + diff, y,
132                                        wp->circsize, wp->circsize);
133                 } else {
134                         XClearArea(display, window, x - diff, y,
135                                    wp->circsize, wp->circsize, False);
136                         XClearArea(display, window, x + diff, y,
137                                    wp->circsize, wp->circsize, False);
138                 }
139         } else
140                 XClearArea(display, window, x, y, wp->circsize, wp->circsize, False);
141
142         if (LRAND() & 1)
143                 ws->dir = (ws->dir + 1) % SEGMENTS;
144         else
145                 ws->dir = (ws->dir + SEGMENTS - 1) % SEGMENTS;
146
147         x = (ws->x + IRINT((float) wp->circsize * costab[ws->dir]) +
148              wp->xsize) % wp->xsize;
149         y = (ws->y + IRINT((float) wp->circsize * sintab[ws->dir]) +
150              wp->ysize) % wp->ysize;
151
152         ws->circ[ws->tail].x = x;
153         ws->circ[ws->tail].y = y;
154         ws->x = x;
155         ws->y = y;
156
157         if (MI_WIN_IS_USE3D(mi)) {
158                 if (LRAND() & 1)
159                         ws->dir2 = (ws->dir2 + 1) % SEGMENTS;
160                 else
161                         ws->dir2 = (ws->dir2 + SEGMENTS - 1) % SEGMENTS;
162                 /* for the z-axis the wrap-around looks bad, so worms should just turn around. */
163                 z = (int) (ws->z + wp->circsize * sintab[ws->dir2]);
164                 if (z < 0 || z >= wp->zsize)
165                         z = (int) (ws->z - wp->circsize * sintab[ws->dir2]);
166
167                 diff = (int) (GETZDIFF(z) + 0.5);       /* ROUND */
168                 ws->diffcirc[ws->tail] = diff;
169
170                 ws->z = z;
171
172                 /* right eye */
173                 color = 0;
174                 wp->rects[color * wp->maxsize + wp->size[color]].x = x + diff;
175                 wp->rects[color * wp->maxsize + wp->size[color]].y = y;
176                 wp->size[color]++;
177
178                 /* left eye */
179                 color = 1;
180                 wp->rects[color * wp->maxsize + wp->size[color]].x = x - diff;
181                 wp->rects[color * wp->maxsize + wp->size[color]].y = y;
182                 wp->size[color]++;
183
184 #if 0
185                 if (ws->redrawing) {    /* Too hard for now */
186                         int         j;
187
188                         for (j = 0; j < REDRAWSTEP; j++) {
189                                 int         k = (ws->tail - ws->redrawpos + wp->wormlength)
190                                 % wp->wormlength;
191
192                                 color = 0;
193                                 wp->rects[color * wp->maxsize + wp->size[color]].x =
194                                         ws->circ[k].x + ws->diffcirc[k];
195                                 wp->rects[color * wp->maxsize + wp->size[color]].y =
196                                         ws->circ[k].y;
197                                 wp->size[color]++;
198
199                                 color = 1;
200                                 wp->rects[color * wp->maxsize + wp->size[color]].x =
201                                         ws->circ[k].x - ws->diffcirc[k];
202                                 wp->rects[color * wp->maxsize + wp->size[color]].y =
203                                         ws->circ[k].y;
204                                 wp->size[color]++;
205
206                                 if (++(ws->redrawpos) >= wp->wormlength) {
207                                         ws->redrawing = 0;
208                                         break;
209                                 }
210                         }
211                 }
212 #endif
213
214         } else {
215
216                 wp->rects[color * wp->maxsize + wp->size[color]].x = x;
217                 wp->rects[color * wp->maxsize + wp->size[color]].y = y;
218                 wp->size[color]++;
219                 if (ws->redrawing) {
220                         int         j;
221
222                         ws->redrawpos++;
223                         /* Compensates for the changed ws->tail
224                            since the last callback. */
225
226                         for (j = 0; j < REDRAWSTEP; j++) {
227                                 int         k = (ws->tail - ws->redrawpos + wp->wormlength)
228                                 % wp->wormlength;
229
230                                 wp->rects[color * wp->maxsize + wp->size[color]].x = ws->circ[k].x;
231                                 wp->rects[color * wp->maxsize + wp->size[color]].y = ws->circ[k].y;
232                                 wp->size[color]++;
233
234                                 if (++(ws->redrawpos) >= wp->wormlength) {
235                                         ws->redrawing = 0;
236                                         break;
237                                 }
238                         }
239                 }
240         }
241 }
242
243 static void
244 free_worms(wormstruct * wp)
245 {
246         int         wn;
247
248         if (wp->worm) {
249                 for (wn = 0; wn < wp->nw; wn++) {
250                         if (wp->worm[wn].circ)
251                                 (void) free((void *) wp->worm[wn].circ);
252                         if (wp->worm[wn].diffcirc)
253                                 (void) free((void *) wp->worm[wn].diffcirc);
254                 }
255                 (void) free((void *) wp->worm);
256                 wp->worm = NULL;
257         }
258         if (wp->rects) {
259                 (void) free((void *) wp->rects);
260                 wp->rects = NULL;
261         }
262         if (wp->size) {
263                 (void) free((void *) wp->size);
264                 wp->size = NULL;
265         }
266 }
267
268 ENTRYPOINT void
269 init_worm (ModeInfo * mi)
270 {
271         wormstruct *wp;
272         int         size = MI_SIZE(mi);
273         int         i, j;
274
275         if (worms == NULL) {
276                 if ((worms = (wormstruct *) calloc(MI_NUM_SCREENS(mi),
277                                                sizeof (wormstruct))) == NULL)
278                         return;
279         }
280         wp = &worms[MI_SCREEN(mi)];
281         if (MI_NPIXELS(mi) <= 2 || MI_WIN_IS_USE3D(mi))
282                 wp->nc = 2;
283         else
284                 wp->nc = MI_NPIXELS(mi);
285         if (wp->nc > NUMCOLORS)
286                 wp->nc = NUMCOLORS;
287
288         free_worms(wp);
289         wp->nw = MI_BATCHCOUNT(mi);
290         if (wp->nw < -MINWORMS)
291                 wp->nw = NRAND(-wp->nw - MINWORMS + 1) + MINWORMS;
292         else if (wp->nw < MINWORMS)
293                 wp->nw = MINWORMS;
294         if (!wp->worm)
295                 wp->worm = (wormstuff *) malloc(wp->nw * sizeof (wormstuff));
296
297         if (!wp->size)
298                 wp->size = (int *) malloc(NUMCOLORS * sizeof (int));
299
300         wp->maxsize = (REDRAWSTEP + 1) * wp->nw;        /*  / wp->nc + 1; */
301         if (!wp->rects)
302                 wp->rects =
303                         (XRectangle *) malloc(wp->maxsize * NUMCOLORS * sizeof (XRectangle));
304
305
306         if (!init_table) {
307                 init_table = 1;
308                 for (i = 0; i < SEGMENTS; i++) {
309                         sintab[i] = SINF(i * 2.0 * M_PI / SEGMENTS);
310                         costab[i] = COSF(i * 2.0 * M_PI / SEGMENTS);
311                 }
312         }
313         wp->xsize = MI_WIN_WIDTH(mi);
314         wp->ysize = MI_WIN_HEIGHT(mi);
315         wp->zsize = MAXZ - MINZ + 1;
316         if (MI_NPIXELS(mi) > 2)
317                 wp->chromo = NRAND(MI_NPIXELS(mi));
318
319         if (size < -MINSIZE)
320                 wp->circsize = NRAND(-size - MINSIZE + 1) + MINSIZE;
321         else if (size < MINSIZE)
322                 wp->circsize = MINSIZE;
323         else
324                 wp->circsize = size;
325
326         for (i = 0; i < wp->nc; i++) {
327                 for (j = 0; j < wp->maxsize; j++) {
328                         wp->rects[i * wp->maxsize + j].width = wp->circsize;
329                         wp->rects[i * wp->maxsize + j].height = wp->circsize;
330
331                 }
332         }
333         (void) memset((char *) wp->size, 0, wp->nc * sizeof (int));
334
335         wp->wormlength = (int) sqrt(wp->xsize + wp->ysize) *
336                 MI_CYCLES(mi) / 8;      /* Fudge this to something reasonable */
337         for (i = 0; i < wp->nw; i++) {
338                 wp->worm[i].circ = (XPoint *) malloc(wp->wormlength * sizeof (XPoint));
339                 wp->worm[i].diffcirc = (int *) malloc(wp->wormlength * sizeof (int));
340
341                 for (j = 0; j < wp->wormlength; j++) {
342                         wp->worm[i].circ[j].x = wp->xsize / 2;
343                         wp->worm[i].circ[j].y = wp->ysize / 2;
344                         if (MI_WIN_IS_USE3D(mi))
345                                 wp->worm[i].diffcirc[j] = 0;
346                 }
347                 wp->worm[i].dir = NRAND(SEGMENTS);
348                 wp->worm[i].dir2 = NRAND(SEGMENTS);
349                 wp->worm[i].tail = 0;
350                 wp->worm[i].x = wp->xsize / 2;
351                 wp->worm[i].y = wp->ysize / 2;
352                 wp->worm[i].z = SCREENZ - MINZ;
353                 wp->worm[i].redrawing = 0;
354         }
355
356         if (MI_WIN_IS_INSTALL(mi) && MI_WIN_IS_USE3D(mi)) {
357                 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_NONE_COLOR(mi));
358                 XFillRectangle(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
359                                0, 0, wp->xsize, wp->ysize);
360         } else
361                 XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
362 }
363
364 ENTRYPOINT void
365 draw_worm (ModeInfo * mi)
366 {
367         Display    *display = MI_DISPLAY(mi);
368         Window      window = MI_WINDOW(mi);
369         GC          gc = MI_GC(mi);
370         wormstruct *wp = &worms[MI_SCREEN(mi)];
371         unsigned long wcolor;
372         int         i;
373
374         (void) memset((char *) wp->size, 0, wp->nc * sizeof (int));
375
376         for (i = 0; i < wp->nw; i++) {
377                 if (MI_NPIXELS(mi) > 2) {
378                         wcolor = (i + wp->chromo) % wp->nc;
379
380                         worm_doit(mi, i, wcolor);
381                 } else
382                         worm_doit(mi, i, (unsigned long) 0);
383         }
384
385         if (MI_WIN_IS_USE3D(mi)) {
386                 if (MI_WIN_IS_INSTALL(mi))
387                         XSetFunction(display, gc, GXor);
388                 XSetForeground(display, gc, MI_RIGHT_COLOR(mi));
389                 XFillRectangles(display, window, gc, &(wp->rects[0]), wp->size[0]);
390
391                 XSetForeground(display, gc, MI_LEFT_COLOR(mi));
392                 XFillRectangles(display, window, gc, &(wp->rects[wp->maxsize]), wp->size[1]);
393                 if (MI_WIN_IS_INSTALL(mi))
394                         XSetFunction(display, gc, GXcopy);
395         } else if (MI_NPIXELS(mi) > 2) {
396                 for (i = 0; i < wp->nc; i++) {
397                         XSetForeground(display, gc, MI_PIXEL(mi, i));
398                         XFillRectangles(display, window, gc, &(wp->rects[i * wp->maxsize]), wp->size[i]);
399                 }
400         } else {
401                 XSetForeground(display, gc, MI_WIN_WHITE_PIXEL(mi));
402                 XFillRectangles(display, window, gc,
403                                 &(wp->rects[0]), wp->size[0]);
404         }
405
406         if (++wp->chromo == (unsigned long) wp->nc)
407                 wp->chromo = 0;
408 }
409
410 ENTRYPOINT void
411 release_worm(ModeInfo * mi)
412 {
413         if (worms != NULL) {
414                 int         screen;
415
416                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
417                         free_worms(&worms[screen]);
418                 (void) free((void *) worms);
419                 worms = NULL;
420         }
421 }
422
423 ENTRYPOINT void
424 refresh_worm (ModeInfo * mi)
425 {
426         if (MI_WIN_IS_USE3D(mi))
427                 /* The 3D code does drawing&clearing by XORing.  We do not
428                    want to go to too much trouble here to make it redraw
429                    correctly. */
430                 XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
431         else if (worms != NULL) {
432                 wormstruct *wp = &worms[MI_SCREEN(mi)];
433                 int         i;
434
435                 for (i = 0; i < wp->nw; i++) {
436                         wp->worm[i].redrawing = 1;
437                         wp->worm[i].redrawpos = 0;
438                 }
439         }
440 }
441
442 XSCREENSAVER_MODULE ("Worm", worm)