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