af680ea2057e4a3e4ff80c174011fc38943c659a
[xscreensaver] / hacks / lisa.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* lisa --- animated full-loop lisajous figures */
3
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)lisa.c        4.04 97/07/28 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       "*delay:                25000   \n"                     \
42                                         "*count:                1       \n"                     \
43                                         "*cycles:               256     \n"                     \
44                                         "*size:                 -1      \n"                     \
45                                         "*ncolors:              200     \n"
46 # define UNIFORM_COLORS
47 # include "xlockmore.h"                         /* from the xscreensaver distribution */
48   void refresh_lisa(ModeInfo * mi);
49   void change_lisa(ModeInfo * mi);
50 #else  /* !STANDALONE */
51 # include "xlock.h"                                     /* from the xlockmore distribution */
52 #endif /* !STANDALONE */
53
54 #define  DEF_ADDITIVE     "True"
55
56 static Bool additive;
57
58 static XrmOptionDescRec lisa_xrm_opts[] =
59 {
60         {"-additive", ".lisa.additive", XrmoptionNoArg, (caddr_t) "True"},
61         {"+additive", ".lisa.additive", XrmoptionNoArg, (caddr_t) "False"}
62 };
63
64 static argtype lisa_vars[] =
65 {
66         {(caddr_t *) & additive, "additive", "Additive", DEF_ADDITIVE, t_Bool}
67 };
68
69 static OptionStruct lisa_vars_desc[] =
70 {
71         {"-/+additive", "turn on/off additive functions mode"}
72 };
73
74 ModeSpecOpt lisa_opts =
75 {2, lisa_xrm_opts, 1, lisa_vars, lisa_vars_desc};
76
77
78 #define  DRAWLINES    1
79 #define  TWOLOOPS     1
80 #define  XVMAX        10        /* Maximum velocities */
81 #define  YVMAX        10
82 #define  LISAMAXFUNCS 2
83 #define  NUMSTDFUNCS  10
84 #define  MAXCYCLES    3
85 #define  MINLISAS 1
86 #define  lisasetcolor() \
87 if (MI_NPIXELS(mi) > 2) { \
88   XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_PIXEL(mi, loop->color)); \
89   if (++(loop->color) >= MI_NPIXELS(mi)) { loop->color=0; } \
90   } else { XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WIN_WHITE_PIXEL(mi)); }
91 #define getRadius(context) \
92   ((context->width > context->height)?context->height:context->width) * 3 / 8
93 #define checkRadius(loop, context) \
94   if ((context->height / 2 > MI_SIZE(mi)) && (context->width / 2 > MI_SIZE(mi))) \
95       loop->radius = MI_SIZE(mi); \
96   if ((loop->radius < 0) || \
97       (loop->radius > loop->center.x) || \
98       (loop->radius > loop->center.y)) loop->radius = getRadius(context)
99
100
101 typedef struct lisafunc_struct {
102         double      xcoeff[2], ycoeff[2];
103         int         nx, ny;
104         int         index;
105 } lisafuncs;
106
107 typedef struct lisa_struct {
108         int         radius, color, dx, dy, nsteps, nfuncs, melting;
109         double      pistep, phi, theta;
110         XPoint      center, *lastpoint;
111         lisafuncs  *function[LISAMAXFUNCS];
112 } lisas;
113
114 typedef struct lisacontext_struct {
115         lisas      *lisajous;
116         int         width, height, nlisajous, loopcount;
117         int         maxcycles;
118 } lisacons;
119
120 static lisacons *Lisa = NULL;
121
122 static lisafuncs Function[NUMSTDFUNCS] =
123 {
124         {
125                 {1.0, 2.0},
126                 {1.0, 2.0}, 2, 2, 0},
127         {
128                 {1.0, 2.0},
129                 {1.0, 1.0}, 2, 2, 1},
130         {
131                 {1.0, 3.0},
132                 {1.0, 2.0}, 2, 2, 2},
133         {
134                 {1.0, 3.0},
135                 {1.0, 3.0}, 2, 2, 3},
136         {
137                 {2.0, 4.0},
138                 {1.0, 2.0}, 2, 2, 4},
139         {
140                 {1.0, 4.0},
141                 {1.0, 3.0}, 2, 2, 5},
142         {
143                 {1.0, 4.0},
144                 {1.0, 4.0}, 2, 2, 6},
145         {
146                 {1.0, 5.0},
147                 {1.0, 5.0}, 2, 2, 7},
148         {
149                 {2.0, 5.0},
150                 {2.0, 5.0}, 2, 2, 8},
151         {
152                 {1.0, 0.0},
153                 {1.0, 0.0}, 1, 1, 9}
154 };
155
156 static void
157 drawlisa(ModeInfo * mi, lisas * loop)
158 {
159         XPoint     *np;
160         XPoint     *lp = loop->lastpoint;
161         lisacons   *lc = &Lisa[MI_SCREEN(mi)];
162         lisafuncs **lf = loop->function;
163         int         phase = lc->loopcount % loop->nsteps;
164         int         pctr, fctr, xctr, yctr;
165         double      xprod, yprod, xsum, ysum;
166
167         /* Allocate the np array */
168         np = (XPoint *) calloc(loop->nsteps, sizeof (XPoint));
169
170         /* Update the center */
171         loop->center.x += loop->dx;
172         loop->center.y += loop->dy;
173         checkRadius(loop, lc);
174         if ((loop->center.x - loop->radius) <= 0) {
175                 loop->center.x = loop->radius;
176                 loop->dx = NRAND(XVMAX);
177         } else if ((loop->center.x + loop->radius) >= lc->width) {
178                 loop->center.x = lc->width - loop->radius;
179                 loop->dx = -NRAND(XVMAX);
180         };
181         if ((loop->center.y - loop->radius) <= 0) {
182                 loop->center.y = loop->radius;
183                 loop->dy = NRAND(YVMAX);
184         } else if ((loop->center.y + loop->radius) >= lc->height) {
185                 loop->center.y = lc->height - loop->radius;
186                 loop->dy = -NRAND(YVMAX);
187         };
188
189         /* Now draw the points, and erase the ones from the last cycle */
190
191         for (pctr = 0; pctr < loop->nsteps; pctr++) {
192                 fctr = loop->nfuncs;
193                 loop->phi = (double) (pctr - phase) * loop->pistep;
194                 loop->theta = (double) (pctr + phase) * loop->pistep;
195                 xsum = ysum = 0;
196                 while (fctr--) {
197                         xctr = lf[fctr]->nx;
198                         yctr = lf[fctr]->ny;
199                         if (additive) {
200                                 xprod = yprod = 0.0;
201                                 while (xctr--)
202                                         xprod += sin(lf[fctr]->xcoeff[xctr] * loop->theta);
203                                 while (yctr--)
204                                         yprod += sin(lf[fctr]->ycoeff[yctr] * loop->phi);
205                                 if (loop->melting) {
206                                         if (fctr) {
207                                                 xsum += xprod \
208                                                         *(double) (loop->nsteps - loop->melting) \
209                                                         /(double) loop->nsteps;
210                                                 ysum += yprod \
211                                                         *(double) (loop->nsteps - loop->melting) \
212                                                         /(double) loop->nsteps;
213                                         } else {
214                                                 xsum += xprod \
215                                                         *(double) loop->melting \
216                                                         /(double) loop->nsteps;
217                                                 ysum += yprod \
218                                                         *(double) loop->melting \
219                                                         /(double) loop->nsteps;
220                                         }
221                                 } else {
222                                         xsum = xprod;
223                                         ysum = yprod;
224                                 }
225                                 if (!fctr) {
226                                         xsum = xsum \
227                                                 *(double) loop->radius \
228                                                 /(double) lf[fctr]->nx;
229                                         ysum = ysum \
230                                                 *(double) loop->radius \
231                                                 /(double) lf[fctr]->ny;
232                                 }
233                         } else {
234                                 if (loop->melting) {
235                                         if (fctr) {
236                                                 yprod = xprod = (double) loop->radius \
237                                                         *(double) (loop->nsteps - loop->melting) \
238                                                         /(double) (loop->nsteps);
239                                         } else {
240                                                 yprod = xprod = (double) loop->radius \
241                                                         *(double) (loop->melting) \
242                                                         /(double) (loop->nsteps);
243                                         }
244                                 } else {
245                                         xprod = yprod = (double) loop->radius;
246                                 }
247                                 while (xctr--)
248                                         xprod *= sin(lf[fctr]->xcoeff[xctr] * loop->theta);
249                                 while (yctr--)
250                                         yprod *= sin(lf[fctr]->ycoeff[yctr] * loop->phi);
251                                 xsum += xprod;
252                                 ysum += yprod;
253                         }
254                 }
255                 if ((loop->nfuncs > 1) && (!loop->melting)) {
256                         xsum /= (double) loop->nfuncs;
257                         ysum /= (double) loop->nfuncs;
258                 }
259                 xsum += (double) loop->center.x;
260                 ysum += (double) loop->center.y;
261
262                 np[pctr].x = (int) ceil(xsum);
263                 np[pctr].y = (int) ceil(ysum);
264         }
265         if (loop->melting) {
266                 if (!--loop->melting) {
267                         loop->nfuncs = 1;
268                         loop->function[0] = loop->function[1];
269                 }
270         }
271         for (pctr = 0; pctr < loop->nsteps; pctr++) {
272
273 #if defined DRAWLINES
274                 /* erase the last cycle's point */
275                 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WIN_BLACK_PIXEL(mi));
276                 XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), \
277                           MI_GC(mi), lp[pctr].x, lp[pctr].y, \
278                           lp[(pctr + 1) % loop->nsteps].x, \
279                           lp[(pctr + 1) % loop->nsteps].y);
280
281                 /* Set the new color */
282                 lisasetcolor();
283
284                 /* plot this cycle's point */
285                 XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), \
286                           MI_GC(mi), np[pctr].x, np[pctr].y, \
287                           np[(pctr + 1) % loop->nsteps].x, \
288                           np[(pctr + 1) % loop->nsteps].y);
289 #else
290                 /* erase the last cycle's point */
291                 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WIN_BLACK_PIXEL(mi));
292                 XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), \
293                            MI_GC(mi), lp[pctr].x, lp[pctr].y);
294
295                 /* Set the new color */
296                 lisasetcolor();
297
298                 /* plot this cycle's point */
299                 XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), \
300                            MI_GC(mi), np[pctr].x, np[pctr].y);
301 #endif
302         }
303         (void) free((void *) lp);
304         loop->lastpoint = np;
305 }
306
307 static void
308 initlisa(ModeInfo * mi, lisas * loop)
309 {
310         lisacons   *lc = &Lisa[MI_SCREEN(mi)];
311         lisafuncs **lf = loop->function;
312         XPoint     *lp;
313         int         phase, pctr, fctr, xctr, yctr;
314         double      xprod, yprod, xsum, ysum;
315
316         if (MI_NPIXELS(mi) > 2) {
317                 loop->color = 0;
318         } else
319                 loop->color = MI_WIN_WHITE_PIXEL(mi);
320         loop->nsteps = MI_CYCLES(mi);
321         if (loop->nsteps == 0)
322                 loop->nsteps = 1;
323         lc->maxcycles = (MAXCYCLES * loop->nsteps) - 1;
324         loop->melting = 0;
325         loop->nfuncs = 1;
326         loop->pistep = 2.0 * M_PI / (double) loop->nsteps;
327         loop->center.x = lc->width / 2;
328         loop->center.y = lc->height / 2;
329         loop->radius = MI_SIZE(mi);
330         checkRadius(loop, lc);
331         loop->dx = NRAND(XVMAX);
332         loop->dy = NRAND(YVMAX);
333         loop->dx++;
334         loop->dy++;
335         lf[0] = &Function[lc->loopcount % NUMSTDFUNCS];
336         if ((lp = loop->lastpoint = (XPoint *)
337              calloc(loop->nsteps, sizeof (XPoint))) == NULL)
338                 return;
339         phase = lc->loopcount % loop->nsteps;
340
341         for (pctr = 0; pctr < loop->nsteps; pctr++) {
342                 loop->phi = (double) (pctr - phase) * loop->pistep;
343                 loop->theta = (double) (pctr + phase) * loop->pistep;
344                 fctr = loop->nfuncs;
345                 xsum = ysum = 0.0;
346                 while (fctr--) {
347                         xprod = yprod = (double) loop->radius;
348                         xctr = lf[fctr]->nx;
349                         yctr = lf[fctr]->ny;
350                         while (xctr--)
351                                 xprod *= sin(lf[fctr]->xcoeff[xctr] * loop->theta);
352                         while (yctr--)
353                                 yprod *= sin(lf[fctr]->ycoeff[yctr] * loop->phi);
354                         xsum += xprod;
355                         ysum += yprod;
356                 }
357                 if (loop->nfuncs > 1) {
358                         xsum /= 2.0;
359                         ysum /= 2.0;
360                 }
361                 xsum += (double) loop->center.x;
362                 ysum += (double) loop->center.y;
363
364                 lp[pctr].x = (int) ceil(xsum);
365                 lp[pctr].y = (int) ceil(ysum);
366         }
367         for (pctr = 0; pctr < loop->nsteps; pctr++) {
368                 /* Set the color */
369                 lisasetcolor();
370 #if defined DRAWLINES
371                 XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), \
372                           MI_GC(mi), lp[pctr].x, lp[pctr].y, \
373                           lp[(pctr + 1) % loop->nsteps].x, \
374                           lp[(pctr + 1) % loop->nsteps].y);
375 #else
376                 XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), \
377                            lp[pctr].x, lp[pctr].y);
378 #endif
379         }
380
381         {
382           int line_width = -15;  /* #### make this a resource */
383           if (line_width == 0)
384                 line_width = -8;
385           if (line_width < 0)
386                 line_width = NRAND(-line_width)+1;
387           XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), line_width,
388                                                  LineSolid, CapProjecting, JoinMiter);
389         }
390 }
391
392 void
393 init_lisa(ModeInfo * mi)
394 {
395         lisacons   *lc;
396         int         lctr;
397
398         if (Lisa == NULL) {
399                 if ((Lisa = (lisacons *) calloc(MI_NUM_SCREENS(mi), sizeof (lisacons))) \
400                     == NULL)
401                         return;
402         }
403         lc = &Lisa[MI_SCREEN(mi)];
404         lc->width = MI_WIN_WIDTH(mi);
405         lc->height = MI_WIN_HEIGHT(mi);
406         lc->loopcount = 0;
407         lc->nlisajous = MI_BATCHCOUNT(mi);
408         if (lc->nlisajous <= 0)
409                 lc->nlisajous = 1;
410
411         if (lc->lisajous == NULL) {
412                 if ((lc->lisajous = (lisas *) calloc(lc->nlisajous, sizeof (lisas))) \
413                     == NULL)
414                         return;
415                 XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
416                 for (lctr = 0; lctr < lc->nlisajous; lctr++) {
417                         initlisa(mi, &lc->lisajous[lctr]);
418                         lc->loopcount++;
419                 }
420         } else {
421                 refresh_lisa(mi);
422         }
423         XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
424 }
425
426 void
427 draw_lisa(ModeInfo * mi)
428 {
429         lisacons   *lc = &Lisa[MI_SCREEN(mi)];
430
431         if (++lc->loopcount > lc->maxcycles) {
432                 change_lisa(mi);
433         }
434         refresh_lisa(mi);
435 }
436
437 void
438 refresh_lisa(ModeInfo * mi)
439 {
440         lisacons   *lc = &Lisa[MI_SCREEN(mi)];
441         int         lctr;
442
443         for (lctr = 0; lctr < lc->nlisajous; lctr++) {
444                 drawlisa(mi, &lc->lisajous[lctr]);
445         }
446 }
447
448 void
449 release_lisa(ModeInfo * mi)
450 {
451         lisacons   *lc;
452         int         lctr, sctr;
453
454         if (Lisa) {
455                 for (sctr = 0; sctr < MI_NUM_SCREENS(mi); sctr++) {
456                         lc = &Lisa[sctr];
457                         while (lc->lisajous) {
458                                 for (lctr = 0; lctr < lc->nlisajous; lctr++) {
459                                         (void) free(lc->lisajous[lctr].lastpoint);
460                                 }
461                                 (void) free(lc->lisajous);
462                                 lc->lisajous = NULL;
463                         }
464                 }
465                 (void) free(Lisa);
466                 Lisa = NULL;
467         }
468 }
469
470 void
471 change_lisa(ModeInfo * mi)
472 {
473         lisacons   *lc = &Lisa[MI_SCREEN(mi)];
474         lisas      *loop;
475         int         lctr;
476
477         lc->loopcount = 0;
478         for (lctr = 0; lctr < lc->nlisajous; lctr++) {
479                 loop = &lc->lisajous[lctr];
480                 loop->function[1] = &Function[(loop->function[0]->index + 1) %
481                                               NUMSTDFUNCS];
482                 loop->melting = loop->nsteps - 1;
483                 loop->nfuncs = 2;
484         }
485 }