ftp://ftp.uni-heidelberg.de/pub/X11/contrib/applications/xscreensaver-2.07.tar.gz
[xscreensaver] / hacks / lisa.c
1 /* -*- Mode: C; tab-width: 4 -*-
2  * lisa.c --- animated full-loop lisajous figures
3  */
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)lisa.c        4.03 97/05/10 xlockmore";
6 #endif
7
8 /* Copyright (c) 1997 by Caleb Cullen.
9  *
10  * Permission to use, copy, modify, and distribute this software and its
11  * documentation for any purpose and without fee is hereby granted,
12  * provided that the above copyright notice appear in all copies and that
13  * both that copyright notice and this permission notice appear in
14  * supporting documentation.
15  *
16  * This file is provided AS IS with no warranties of any kind.  The author
17  * shall have no liability with respect to the infringement of copyrights,
18  * trade secrets or any patents by this file or any part thereof.  In no
19  * event will the author be liable for any lost revenue or profits or
20  * other special, indirect and consequential damages.
21  *
22  * Revision History:
23  * 10-May-97: Compatible with xscreensaver
24  *
25  * The inspiration for this program, Lasp, was written by Adam B. Roach 
26  * in 1990, assisted by me, Caleb Cullen.  It was written first in C, then
27  * in assembly, and used pre-calculated data tables to graph lisajous
28  * figures on 386 machines and lower.  This version bears only superficial
29  * resemblances to the original Lasp.
30  *
31  * The `lissie' module's source code was studied as an example of how
32  * to incorporate a new module into xlock.  Resemblances to it are
33  * expected, but not intended to be plaigiaristic.
34  */
35
36 #ifdef STANDALONE
37 # define PROGCLASS                                      "Lisa"
38 # define HACK_INIT                                      init_lisa
39 # define HACK_DRAW                                      draw_lisa
40 # define lisa_opts                                      xlockmore_opts
41 # define DEFAULTS       "*count:                1       \n"                     \
42                                         "*cycles:               256     \n"                     \
43                                         "*delay:                25000   \n"                     \
44                                         "*size:                 -1      \n"                     \
45                                         "*ncolors:              200     \n"
46 # define UNIFORM_COLORS
47 # include "xlockmore.h"                         /* from the xscreensaver distribution */
48 #else  /* !STANDALONE */
49 # include "xlock.h"                                     /* from the xlockmore distribution */
50 #endif /* !STANDALONE */
51
52 ModeSpecOpt lisa_opts = { 
53   0, NULL, 0, NULL, NULL };
54
55 #define  DRAWLINES    1
56 #define  TWOLOOPS     1
57 #define  ADDITIVE     "True"
58 #define  XVMAX        10        /* Maximum velocities */
59 #define  YVMAX        10
60 #define  LISAMAXFUNCS 2
61 #define  NUMSTDFUNCS  10
62 #define  MAXCYCLES    3
63 #define  lisasetcolor() \
64 if (MI_NPIXELS(mi) > 2) { \
65   XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_PIXEL(mi, loop->color)); \
66   if (++(loop->color) >= MI_NPIXELS(mi)) { loop->color=0; } \
67   } else { XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WIN_WHITE_PIXEL(mi)); }
68 #define getRadius(context) \
69   ((context->width > context->height)?context->height:context->width) * 3 / 8
70 #define checkRadius(loop, context) \
71   if ((context->height / 2 > MI_SIZE(mi)) && (context->width / 2 > MI_SIZE(mi))) \
72       loop->radius = MI_SIZE(mi); \
73   if ((loop->radius < 0) || \
74       (loop->radius > loop->center.x) || \
75       (loop->radius > loop->center.y)) loop->radius = getRadius(context)
76
77
78 typedef struct lisafunc_struct {
79         double      xcoeff[2], ycoeff[2];
80         int         nx, ny;
81         int         index;
82 } lisafuncs;
83
84 typedef struct lisa_struct {
85         int         radius, color, dx, dy, nsteps, nfuncs, melting;
86         double      pistep, phi, theta;
87         XPoint      center, *lastpoint;
88         lisafuncs  *function[LISAMAXFUNCS];
89 } lisas;
90
91 typedef struct lisacontext_struct {
92         lisas      *lisajous;
93         int         width, height, nlisajous, loopcount;
94         int         maxcycles;
95 } lisacons;
96
97 static lisacons *Lisa = NULL;
98 static Bool additive;
99
100 #ifndef STANDALONE
101 static XrmOptionDescRec lisa_xrm_opts[] =
102 {
103         {"-additive", ".lisa.additive", XrmoptionNoArg, (caddr_t) "True"},
104         {"+additive", ".lisa.additive", XrmoptionNoArg, (caddr_t) "False"}
105 };
106
107 static argtype lisa_vars[] =
108 {
109         {(caddr_t *) & additive, "additive", "Additive", ADDITIVE, t_Bool}
110 };
111
112 static OptionStruct lisa_vars_desc[] =
113 {
114         {"-/+additive", "turn on/off additive functions mode"}
115 };
116
117 ModeSpecOpt lisa_opts =
118 {2, lisa_xrm_opts, 1, lisa_vars, lisa_vars_desc};
119 #endif /* STANDALONE */
120
121
122 void refresh_lisa(ModeInfo * mi);
123 void change_lisa(ModeInfo * mi);
124
125
126 static lisafuncs Function[NUMSTDFUNCS] =
127 {
128         {
129                 {1.0, 2.0},
130                 {1.0, 2.0}, 2, 2, 0},
131         {
132                 {1.0, 2.0},
133                 {1.0, 1.0}, 2, 2, 1},
134         {
135                 {1.0, 3.0},
136                 {1.0, 2.0}, 2, 2, 2},
137         {
138                 {1.0, 3.0},
139                 {1.0, 3.0}, 2, 2, 3},
140         {
141                 {2.0, 4.0},
142                 {1.0, 2.0}, 2, 2, 4},
143         {
144                 {1.0, 4.0},
145                 {1.0, 3.0}, 2, 2, 5},
146         {
147                 {1.0, 4.0},
148                 {1.0, 4.0}, 2, 2, 6},
149         {
150                 {1.0, 5.0},
151                 {1.0, 5.0}, 2, 2, 7},
152         {
153                 {2.0, 5.0},
154                 {2.0, 5.0}, 2, 2, 8},
155         {
156                 {1.0, 0.0},
157                 {1.0, 0.0}, 1, 1, 9}
158 };
159
160 static void
161 drawlisa(ModeInfo * mi, lisas * loop)
162 {
163         XPoint     *np;
164         XPoint     *lp = loop->lastpoint;
165         lisacons   *lc = &Lisa[MI_SCREEN(mi)];
166         lisafuncs **lf = loop->function;
167         int         phase = lc->loopcount % loop->nsteps;
168         int         pctr, fctr, xctr, yctr;
169         double      xprod, yprod, xsum, ysum;
170
171         /* Allocate the np array */
172         np = (XPoint *) calloc(loop->nsteps, sizeof (XPoint));
173
174         /* Update the center */
175         loop->center.x += loop->dx;
176         loop->center.y += loop->dy;
177         checkRadius(loop, lc);
178         if ((loop->center.x - loop->radius) <= 0) {
179                 loop->center.x = loop->radius;
180                 loop->dx = NRAND(XVMAX);
181         } else if ((loop->center.x + loop->radius) >= lc->width) {
182                 loop->center.x = lc->width - loop->radius;
183                 loop->dx = -NRAND(XVMAX);
184         };
185         if ((loop->center.y - loop->radius) <= 0) {
186                 loop->center.y = loop->radius;
187                 loop->dy = NRAND(YVMAX);
188         } else if ((loop->center.y + loop->radius) >= lc->height) {
189                 loop->center.y = lc->height - loop->radius;
190                 loop->dy = -NRAND(YVMAX);
191         };
192
193         /* Now draw the points, and erase the ones from the last cycle */
194
195         for (pctr = 0; pctr < loop->nsteps; pctr++) {
196                 fctr = loop->nfuncs;
197                 loop->phi = (double) (pctr - phase) * loop->pistep;
198                 loop->theta = (double) (pctr + phase) * loop->pistep;
199                 xsum = ysum = 0;
200                 while (fctr--) {
201                         xctr = lf[fctr]->nx;
202                         yctr = lf[fctr]->ny;
203                         if (additive) {
204                                 xprod = yprod = 0.0;
205                                 while (xctr--)
206                                         xprod += sin(lf[fctr]->xcoeff[xctr] * loop->theta);
207                                 while (yctr--)
208                                         yprod += sin(lf[fctr]->ycoeff[yctr] * loop->phi);
209                                 if (loop->melting) {
210                                         if (fctr) {
211                                                 xsum += xprod \
212                                                         *(double) (loop->nsteps - loop->melting) \
213                                                         /(double) loop->nsteps;
214                                                 ysum += yprod \
215                                                         *(double) (loop->nsteps - loop->melting) \
216                                                         /(double) loop->nsteps;
217                                         } else {
218                                                 xsum += xprod \
219                                                         *(double) loop->melting \
220                                                         /(double) loop->nsteps;
221                                                 ysum += yprod \
222                                                         *(double) loop->melting \
223                                                         /(double) loop->nsteps;
224                                         }
225                                 } else {
226                                         xsum = xprod;
227                                         ysum = yprod;
228                                 }
229                                 if (!fctr) {
230                                         xsum = xsum \
231                                                 *(double) loop->radius \
232                                                 /(double) lf[fctr]->nx;
233                                         ysum = ysum \
234                                                 *(double) loop->radius \
235                                                 /(double) lf[fctr]->ny;
236                                 }
237                         } else {
238                                 if (loop->melting) {
239                                         if (fctr) {
240                                                 yprod = xprod = (double) loop->radius \
241                                                         *(double) (loop->nsteps - loop->melting) \
242                                                         /(double) (loop->nsteps);
243                                         } else {
244                                                 yprod = xprod = (double) loop->radius \
245                                                         *(double) (loop->melting) \
246                                                         /(double) (loop->nsteps);
247                                         }
248                                 } else {
249                                         xprod = yprod = (double) loop->radius;
250                                 }
251                                 while (xctr--)
252                                         xprod *= sin(lf[fctr]->xcoeff[xctr] * loop->theta);
253                                 while (yctr--)
254                                         yprod *= sin(lf[fctr]->ycoeff[yctr] * loop->phi);
255                                 xsum += xprod;
256                                 ysum += yprod;
257                         }
258                 }
259                 if ((loop->nfuncs > 1) && (!loop->melting)) {
260                         xsum /= (double) loop->nfuncs;
261                         ysum /= (double) loop->nfuncs;
262                 }
263                 xsum += (double) loop->center.x;
264                 ysum += (double) loop->center.y;
265
266                 np[pctr].x = (int) ceil(xsum);
267                 np[pctr].y = (int) ceil(ysum);
268         }
269         if (loop->melting) {
270                 if (!--loop->melting) {
271                         loop->nfuncs = 1;
272                         loop->function[0] = loop->function[1];
273                 }
274         }
275         for (pctr = 0; pctr < loop->nsteps; pctr++) {
276
277 #if defined DRAWLINES
278                 /* erase the last cycle's point */
279                 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WIN_BLACK_PIXEL(mi));
280                 XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), \
281                           MI_GC(mi), lp[pctr].x, lp[pctr].y, \
282                           lp[(pctr + 1) % loop->nsteps].x, \
283                           lp[(pctr + 1) % loop->nsteps].y);
284
285                 /* Set the new color */
286                 lisasetcolor();
287
288                 /* plot this cycle's point */
289                 XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), \
290                           MI_GC(mi), np[pctr].x, np[pctr].y, \
291                           np[(pctr + 1) % loop->nsteps].x, \
292                           np[(pctr + 1) % loop->nsteps].y);
293 #else
294                 /* erase the last cycle's point */
295                 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WIN_BLACK_PIXEL(mi));
296                 XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), \
297                            MI_GC(mi), lp[pctr].x, lp[pctr].y);
298
299                 /* Set the new color */
300                 lisasetcolor();
301
302                 /* plot this cycle's point */
303                 XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), \
304                            MI_GC(mi), np[pctr].x, np[pctr].y);
305 #endif
306         }
307         (void) free((void *) lp);
308         loop->lastpoint = np;
309 }
310
311 static void
312 initlisa(ModeInfo * mi, lisas * loop)
313 {
314         lisacons   *lc = &Lisa[MI_SCREEN(mi)];
315         lisafuncs **lf = loop->function;
316         XPoint     *lp;
317         int         phase, pctr, fctr, xctr, yctr;
318         double      xprod, yprod, xsum, ysum;
319
320         if (MI_NPIXELS(mi) > 2) {
321                 loop->color = 0;
322         } else
323                 loop->color = MI_WIN_WHITE_PIXEL(mi);
324         loop->nsteps = MI_CYCLES(mi);
325         if (loop->nsteps == 0)
326                 loop->nsteps = 1;
327         lc->maxcycles = (MAXCYCLES * loop->nsteps) - 1;
328         loop->melting = 0;
329         loop->nfuncs = 1;
330         loop->pistep = 2.0 * M_PI / (double) loop->nsteps;
331         loop->center.x = lc->width / 2;
332         loop->center.y = lc->height / 2;
333         loop->radius = MI_SIZE(mi);
334         checkRadius(loop, lc);
335         loop->dx = NRAND(XVMAX);
336         loop->dy = NRAND(YVMAX);
337         loop->dx++;
338         loop->dy++;
339         lf[0] = &Function[lc->loopcount % NUMSTDFUNCS];
340         if ((lp = loop->lastpoint = (XPoint *)
341              calloc(loop->nsteps, sizeof (XPoint))) == NULL)
342                 return;
343         phase = lc->loopcount % loop->nsteps;
344
345         for (pctr = 0; pctr < loop->nsteps; pctr++) {
346                 loop->phi = (double) (pctr - phase) * loop->pistep;
347                 loop->theta = (double) (pctr + phase) * loop->pistep;
348                 fctr = loop->nfuncs;
349                 xsum = ysum = 0.0;
350                 while (fctr--) {
351                         xprod = yprod = (double) loop->radius;
352                         xctr = lf[fctr]->nx;
353                         yctr = lf[fctr]->ny;
354                         while (xctr--)
355                                 xprod *= sin(lf[fctr]->xcoeff[xctr] * loop->theta);
356                         while (yctr--)
357                                 yprod *= sin(lf[fctr]->ycoeff[yctr] * loop->phi);
358                         xsum += xprod;
359                         ysum += yprod;
360                 }
361                 if (loop->nfuncs > 1) {
362                         xsum /= 2.0;
363                         ysum /= 2.0;
364                 }
365                 xsum += (double) loop->center.x;
366                 ysum += (double) loop->center.y;
367
368                 lp[pctr].x = (int) ceil(xsum);
369                 lp[pctr].y = (int) ceil(ysum);
370         }
371         for (pctr = 0; pctr < loop->nsteps; pctr++) {
372                 /* Set the color */
373                 lisasetcolor();
374 #if defined DRAWLINES
375                 XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), \
376                           MI_GC(mi), lp[pctr].x, lp[pctr].y, \
377                           lp[(pctr + 1) % loop->nsteps].x, \
378                           lp[(pctr + 1) % loop->nsteps].y);
379 #else
380                 XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), \
381                            lp[pctr].x, lp[pctr].y);
382 #endif
383         }
384 }
385
386 void
387 init_lisa(ModeInfo * mi)
388 {
389         lisacons   *lc;
390         int         lctr;
391
392         if (Lisa == NULL) {
393                 if ((Lisa = (lisacons *) calloc(MI_NUM_SCREENS(mi), sizeof (lisacons))) \
394                     == NULL)
395                         return;
396         }
397         lc = &Lisa[MI_SCREEN(mi)];
398         lc->width = MI_WIN_WIDTH(mi);
399         lc->height = MI_WIN_HEIGHT(mi);
400         lc->nlisajous = MI_BATCHCOUNT(mi);
401         lc->loopcount = 0;
402
403         if (lc->lisajous == NULL) {
404                 if ((lc->lisajous = (lisas *) calloc(lc->nlisajous, sizeof (lisas))) \
405                     == NULL)
406                         return;
407                 XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
408                 for (lctr = 0; lctr < lc->nlisajous; lctr++) {
409                         initlisa(mi, &lc->lisajous[lctr]);
410                         lc->loopcount++;
411                 }
412         } else {
413                 refresh_lisa(mi);
414         }
415         XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
416 }
417
418 void
419 draw_lisa(ModeInfo * mi)
420 {
421         lisacons   *lc = &Lisa[MI_SCREEN(mi)];
422
423         if (++lc->loopcount > lc->maxcycles) {
424                 change_lisa(mi);
425         }
426         refresh_lisa(mi);
427 }
428
429 void
430 refresh_lisa(ModeInfo * mi)
431 {
432         lisacons   *lc = &Lisa[MI_SCREEN(mi)];
433         int         lctr;
434
435         for (lctr = 0; lctr < lc->nlisajous; lctr++) {
436                 drawlisa(mi, &lc->lisajous[lctr]);
437         }
438 }
439
440 void
441 release_lisa(ModeInfo * mi)
442 {
443         lisacons   *lc;
444         int         lctr, sctr;
445
446         if (Lisa) {
447                 for (sctr = 0; sctr < MI_NUM_SCREENS(mi); sctr++) {
448                         lc = &Lisa[sctr];
449                         while (lc->lisajous) {
450                                 for (lctr = 0; lctr < lc->nlisajous; lctr++) {
451                                         (void) free(lc->lisajous[lctr].lastpoint);
452                                 }
453                                 (void) free(lc->lisajous);
454                                 lc->lisajous = NULL;
455                         }
456                 }
457                 (void) free(Lisa);
458                 Lisa = NULL;
459         }
460 }
461
462 void
463 change_lisa(ModeInfo * mi)
464 {
465         lisacons   *lc = &Lisa[MI_SCREEN(mi)];
466         lisas      *loop;
467         int         lctr;
468
469         lc->loopcount = 0;
470         for (lctr = 0; lctr < lc->nlisajous; lctr++) {
471                 loop = &lc->lisajous[lctr];
472                 loop->function[1] = &Function[(loop->function[0]->index + 1) %
473                                               NUMSTDFUNCS];
474                 loop->melting = loop->nsteps - 1;
475                 loop->nfuncs = 2;
476         }
477 }