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