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