From http://www.jwz.org/xscreensaver/xscreensaver-5.38.tar.gz
[xscreensaver] / hacks / lisa.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* lisa --- animated full-loop lissajous 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, 2006 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  * 23-Feb-2006: fixed color-cycling issues
25  * 01-Nov-2000: Allocation checks
26  * 10-May-1997: Compatible with xscreensaver
27  *
28  * The inspiration for this program, Lasp, was written by Adam B. Roach
29  * in 1990, assisted by me, Caleb Cullen.  It was written first in C, then
30  * in assembly, and used pre-calculated data tables to graph lissajous
31  * figures on 386 machines and lower.  This version bears only superficial
32  * resemblances to the original Lasp.
33  *
34  * The `lissie' module's source code was studied as an example of how
35  * to incorporate a new module into xlock.  Resemblances to it are
36  * expected, but not intended to be plaigiaristic.
37  *
38  * February, 2006: 21st Century Update for Lisa
39  * + fixed color-mapping: the 'beginning' of the loop always uses the
40  *     same (starting) pixel value, causing the loop's coloration to
41  *     appear solid rather than flickering as in the previous version
42  * + all lines/points in a single color are drawn at once using XDrawLines()
43  *     or XDrawPoints(); the artifacting evident in the previous version
44  *     has been masked by the use of CapNotLast to separate individual drawn
45  *     areas with intentional "whitespace" (typically black)
46  * + added many new elements to the Function[] array
47  * + randomized selection of next function
48  * + introduced concept of "rarely-chosen" functions
49  * + cleaned up code somewhat, standardized capitalization, commented all
50  *     #directives with block labels
51  */
52
53 #ifdef STANDALONE
54 # define MODE_lisa
55 # define DEFAULTS       "*delay: 17000 \n" \
56                                         "*count: 1 \n" \
57                                         "*cycles: 768 \n" \
58                                         "*size: 500 \n" \
59                                         "*ncolors: 64 \n" \
60                                         "*fpsSolid: true \n" \
61                                     "*lowrez: True \n" \
62
63 # define UNIFORM_COLORS
64 # define release_lisa 0
65 # define reshape_lisa 0
66 # define lisa_handle_event xlockmore_no_events
67 # include "xlockmore.h"         /* in xscreensaver distribution */
68 #else /* STANDALONE */
69 #include "xlock.h"          /* in xlockmore distribution */
70
71 #endif /* STANDALONE */
72
73 #ifdef MODE_lisa
74
75 #define  DEF_ADDITIVE     "True"
76
77 static Bool additive;
78
79 static XrmOptionDescRec opts[] =
80 {
81         {"-additive", ".lisa.additive", XrmoptionNoArg, "True"},
82         {"+additive", ".lisa.additive", XrmoptionNoArg, "False"}
83 };
84
85 static argtype vars[] =
86 {
87         {&additive, "additive", "Additive", DEF_ADDITIVE, t_Bool}
88 };
89
90 static OptionStruct desc[] =
91 {
92         {"-/+additive", "turn on/off additive functions mode"}
93 };
94
95 ENTRYPOINT ModeSpecOpt lisa_opts =
96 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
97
98 #ifdef USE_MODULES
99 ModStruct   lisa_description =
100 {"lisa", "init_lisa", "draw_lisa", (char *) NULL,
101  "refresh_lisa", "change_lisa", "free_lisa", &lisa_opts,
102  17000, 1, 768, -1, 64, 1.0, "",
103  "Shows animated lissajous figures", 0, NULL};
104
105 #endif
106
107 #define  DRAWLINES    1
108 /* #define  FOLLOW_FUNC_ORDER 1 */
109 #define  TWOLOOPS     1
110 #define  XVMAX        10        /* Maximum velocities */
111 #define  YVMAX        10
112 #define  LISAMAXFUNCS 2
113 #define  NUMSTDFUNCS  28
114 #define  RAREFUNCMIN  25
115 #define  RAREFUNCODDS 4     /* 1:n chance a rare function will be re-randomized */
116 #define  MAXCYCLES    3
117 #define  MINLISAS     1
118 #define  STARTCOLOR   0
119 #define  STARTFUNC    24    /* if negative, is upper-bound on randomization */
120 #define  LINEWIDTH    -8    /* if negative, is upper-bound on randomization */
121 #define  LINESTYLE    LineSolid  /* an insane man might have fun with this :) */
122 #define  LINECAP      CapNotLast /* anything else looks pretty crappy */
123 #define  LINEJOIN     JoinBevel  /* this ought to be fastest */
124 #define  SET_COLOR() \
125          if (MI_NPIXELS(mi) > 2) { \
126            XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_PIXEL(mi, loop->color)); \
127          if (loop->cstep \
128              && pctr % loop->cstep == 0 \
129              && ++(loop->color) >= (unsigned) MI_NPIXELS(mi)) \
130                { loop->color=STARTCOLOR; } \
131          } else { XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi)); } 
132 #define  GET_RADIUS(context) \
133          ((context->width > context->height)?context->height:context->width) * 3 / 8
134 #define  CHECK_RADIUS(loop, context) \
135          if ((context->height / 2 > MI_SIZE(mi)) && (context->width / 2 > MI_SIZE(mi))) \
136            loop->radius = MI_SIZE(mi); \
137          if ((loop->radius < 0) || \
138            (loop->radius > loop->center.x) || \
139            (loop->radius > loop->center.y)) loop->radius = GET_RADIUS(context)
140 #define  PRINT_FUNC(funcptr) \
141                  printf("new function -- #%d:\n\tx = sin(%gs) * sin(%gs)\n\ty = sin(%gt) * sin(%gt)\n", \
142                             funcptr->index, \
143                             funcptr->xcoeff[0], funcptr->xcoeff[1], \
144                             funcptr->ycoeff[0], funcptr->ycoeff[1])
145
146
147 typedef struct lisafunc_struct {
148         double      xcoeff[2], ycoeff[2];
149         int         nx, ny;
150         int         index;
151 } lisafuncs;
152
153 typedef struct lisa_struct {
154     unsigned long color;
155     int         radius, dx, dy, nsteps, nfuncs, melting, cstep;
156     double      pistep, phi, theta;
157     XPoint      center, *lastpoint;
158     lisafuncs  *function[LISAMAXFUNCS];
159     int         linewidth;
160 } lisas;
161
162 typedef struct lisacontext_struct {
163         lisas      *lissajous;
164         int         width, height, nlissajous, loopcount;
165         int         maxcycles;
166         Bool        painted;
167 } lisacons;
168
169 static lisacons *Lisa = (lisacons *) NULL;
170
171 static lisafuncs Function[NUMSTDFUNCS] =
172   {
173         {
174           {1.0, 2.0},
175           {1.0, 2.0}, 2, 2, 0},
176         {
177           {1.0, 2.0},
178           {1.0, 1.0}, 2, 2, 1},
179         {
180           {1.0, 3.0},
181           {1.0, 2.0}, 2, 2, 2},
182         {
183           {1.0, 3.0},
184           {1.0, 3.0}, 2, 2, 3},
185         {
186           {2.0, 4.0},
187           {1.0, 2.0}, 2, 2, 4},
188         {
189           {1.0, 4.0},
190           {1.0, 3.0}, 2, 2, 5},
191         {
192           {1.0, 4.0},
193           {1.0, 4.0}, 2, 2, 6},
194         {
195           {1.0, 5.0},
196           {1.0, 5.0}, 2, 2, 7},
197         {
198           {2.0, 5.0},
199           {2.0, 5.0}, 2, 2, 8},
200         {
201           {1.0, 2.0},
202           {2.0, 5.0}, 2, 2, 9},
203         {
204           {1.0, 2.0},
205           {3.0, 5.0}, 2, 2, 10},
206         {
207           {1.0, 2.0},
208           {2.0, 3.0}, 2, 2, 11},
209         {
210           {1.0, 3.0},
211           {2.0, 3.0}, 2, 2, 12},
212         {
213           {2.0, 3.0},
214           {1.0, 3.0}, 2, 2, 13},
215         {
216           {2.0, 4.0},
217           {1.0, 3.0}, 2, 2, 14},
218         {
219           {1.0, 4.0},
220           {2.0, 3.0}, 2, 2, 15},
221         {
222           {2.0, 4.0},
223           {2.0, 3.0}, 2, 2, 16},
224         {
225           {1.0, 5.0},
226           {2.0, 3.0}, 2, 2, 17},
227         {
228           {2.0, 5.0},
229           {2.0, 3.0}, 2, 2, 18},        
230         {
231           {1.0, 5.0},
232           {2.0, 5.0}, 2, 2, 19},
233         {
234           {1.0, 3.0},
235           {2.0, 7.0}, 2, 2, 20},
236         {
237           {2.0, 3.0},
238           {5.0, 7.0}, 2, 2, 21},
239         {
240           {1.0, 2.0},
241           {3.0, 7.0}, 2, 2, 22},
242         {
243           {2.0, 5.0},
244           {5.0, 7.0}, 2, 2, 23},
245         {
246           {5.0, 7.0},
247           {5.0, 7.0}, 2, 2, 24},
248         {                          /* functions past here are 'rare' and won't  */
249           {2.0, 7.0},              /* show up as often.  tweak the #defines above */
250           {1.0, 7.0}, 2, 2, 25},   /* to see them more frequently */
251         {
252           {2.0, 9.0},
253           {1.0, 7.0}, 2, 2, 26},
254         {
255           {5.0, 11.0},
256           {2.0, 9.0}, 2, 2, 27}
257 };
258
259 int xMaxLines;
260
261 ENTRYPOINT void
262 free_lisa(ModeInfo * mi)
263 {
264         lisacons   *lc = &Lisa[MI_SCREEN(mi)];
265         while (lc->lissajous) {
266                 int    lctr;
267
268                 for (lctr = 0; lctr < lc->nlissajous; lctr++) {
269                         (void) free((void *) lc->lissajous[lctr].lastpoint);
270                 }
271                 (void) free((void *) lc->lissajous);
272                 lc->lissajous = (lisas *) NULL;
273         }
274 }
275
276 static Bool
277 drawlisa(ModeInfo * mi, lisas * loop)
278 {
279         XPoint     *np;
280         XPoint     *lp = loop->lastpoint;
281         lisacons   *lc = &Lisa[MI_SCREEN(mi)];
282         lisafuncs **lf = loop->function;
283         int         phase = lc->loopcount % loop->nsteps;
284         int         pctr, fctr, xctr, yctr, extra_points;
285         double      xprod, yprod, xsum, ysum;
286
287     /* why carry this around in the struct when we can calculate it on demand? */
288     extra_points = loop->cstep - (loop->nsteps % loop->cstep);
289
290         /* Allocate the np (new point) array (with padding) */
291         if ((np = (XPoint *) calloc(loop->nsteps+extra_points, sizeof (XPoint))) == NULL) {
292                 free_lisa(mi);
293                 return False;
294         }
295
296         /* Update the center */
297         loop->center.x += loop->dx;
298         loop->center.y += loop->dy;
299         CHECK_RADIUS(loop, lc);
300
301   /* check for overlaps -- where the figure might go off the screen */
302
303         if ((loop->center.x - loop->radius) <= 0) {
304                 loop->center.x = loop->radius;
305                 loop->dx = NRAND(XVMAX);
306         } else if ((loop->center.x + loop->radius) >= lc->width) {
307                 loop->center.x = lc->width - loop->radius;
308                 loop->dx = -NRAND(XVMAX);
309         };
310         if ((loop->center.y - loop->radius) <= 0) {
311                 loop->center.y = loop->radius;
312                 loop->dy = NRAND(YVMAX);
313         } else if ((loop->center.y + loop->radius) >= lc->height) {
314                 loop->center.y = lc->height - loop->radius;
315                 loop->dy = -NRAND(YVMAX);
316         };
317
318         /* Now draw the points, and erase the ones from the last cycle */
319
320         for (pctr = 0; pctr < loop->nsteps; pctr++) {
321                 fctr = loop->nfuncs;
322                 loop->phi = (double) (pctr - phase) * loop->pistep;
323                 loop->theta = (double) (pctr + phase) * loop->pistep;
324                 xsum = ysum = 0;
325                 while (fctr--) {
326                         xctr = lf[fctr]->nx;
327                         yctr = lf[fctr]->ny;
328                         if (additive) {
329                                 xprod = yprod = 0.0;
330                                 while (xctr--)
331                                         xprod += sin(lf[fctr]->xcoeff[xctr] * loop->theta);
332                                 while (yctr--)
333                                         yprod += sin(lf[fctr]->ycoeff[yctr] * loop->phi);
334                                 if (loop->melting) {
335                                         if (fctr) {
336                                                 xsum += xprod * (double) (loop->nsteps - loop->melting) /
337                                                         (double) loop->nsteps;
338                                                 ysum += yprod * (double) (loop->nsteps - loop->melting) /
339                                                         (double) loop->nsteps;
340                                         } else {
341                                                 xsum += xprod * (double) loop->melting / (double) loop->nsteps;
342                                                 ysum += yprod * (double) loop->melting / (double) loop->nsteps;
343                                         }
344                                 } else {
345                                         xsum = xprod;
346                                         ysum = yprod;
347                                 }
348                                 if (!fctr) {
349                                         xsum = xsum * (double) loop->radius / (double) lf[fctr]->nx;
350                                         ysum = ysum * (double) loop->radius / (double) lf[fctr]->ny;
351                                 }
352                         } else {
353                                 if (loop->melting) {
354                                         if (fctr) {
355                                                 yprod = xprod = (double) loop->radius *
356                                                         (double) (loop->nsteps - loop->melting) /
357                                                         (double) (loop->nsteps);
358                                         } else {
359                                                 yprod = xprod = (double) loop->radius *
360                                                         (double) (loop->melting) / (double) (loop->nsteps);
361                                         }
362                                 } else {
363                                         xprod = yprod = (double) loop->radius;
364                                 }
365                                 while (xctr--)
366                                         xprod *= sin(lf[fctr]->xcoeff[xctr] * loop->theta);
367                                 while (yctr--)
368                                         yprod *= sin(lf[fctr]->ycoeff[yctr] * loop->phi);
369                                 xsum += xprod;
370                                 ysum += yprod;
371                         }
372                 }
373                 if ((loop->nfuncs > 1) && (!loop->melting)) {
374                         xsum /= (double) loop->nfuncs;
375                         ysum /= (double) loop->nfuncs;
376                 }
377                 xsum += (double) loop->center.x;
378                 ysum += (double) loop->center.y;
379
380                 np[pctr].x = (int) ceil(xsum);
381                 np[pctr].y = (int) ceil(ysum);
382         }
383     /* fill in extra points */
384     for (pctr=loop->nsteps; pctr < loop->nsteps+extra_points; pctr++) {
385           np[pctr].x = np[pctr - loop->nsteps].x;
386           np[pctr].y = np[pctr - loop->nsteps].y;
387     }
388         if (loop->melting) {
389                 if (!--loop->melting) {
390                         loop->nfuncs = 1;
391                         loop->function[0] = loop->function[1];
392                 }
393         }
394
395     /* reset starting color each time to prevent ass-like appearance */ 
396     loop->color = STARTCOLOR;
397
398   if (loop->cstep < xMaxLines) {
399         /*      printf("Drawing dashes\n"); */
400         for (pctr = 0; pctr < loop->nsteps; pctr+=loop->cstep) {
401 #if defined DRAWLINES
402                 XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), loop->linewidth,
403                                    LINESTYLE, LINECAP, LINEJOIN);
404                 /* erase the last cycle's point */
405                 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
406                 XDrawLines(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), &lp[pctr], 
407                                    loop->cstep, CoordModeOrigin);
408
409                 /* Set the new color */
410                 SET_COLOR();
411
412                 /* plot this cycle's point */
413                 XDrawLines(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), 
414                                    &np[pctr], loop->cstep, CoordModeOrigin);
415                 XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 1,
416                                                    LINESTYLE, LINECAP, LINEJOIN);
417 #else
418                 /* erase the last cycle's point */
419                 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
420                 XDrawPoints(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
421                                         &lp[pctr], loop->cstep, CoordModeOrigin);
422
423                 /* Set the new color */
424                 SET_COLOR();
425
426                 /* plot this cycle's point */
427                 XDrawPoints(MI_DISPLAY(mi), MI_WINDOW(mi),
428                                         MI_GC(mi), &np[pctr], loop->cstep, CoordModeOrigin);
429 #endif
430         }
431   } else { /* on my system, cstep is larger than 65532/2 if we get here */
432         for (pctr = 0; pctr < loop->nsteps; pctr++) {
433 #if defined DRAWLINES
434           XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), loop->linewidth,
435                                                  LINESTYLE, LINECAP, LINEJOIN);
436           /* erase the last cycle's point */
437           XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
438           XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi),
439                                 MI_GC(mi), lp[pctr].x, lp[pctr].y,
440                                 lp[(pctr + 1) % loop->nsteps].x,
441                                 lp[(pctr + 1) % loop->nsteps].y);
442           
443           /* Set the new color */
444           SET_COLOR();
445           
446           /* plot this cycle's point */
447           XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi),
448                                 MI_GC(mi), np[pctr].x, np[pctr].y,
449                                 np[(pctr + 1) % loop->nsteps].x,
450                                 np[(pctr + 1) % loop->nsteps].y);
451           XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 1,
452                                                  LINESTYLE, LINECAP, LINEJOIN);
453 #else  /* DRAWLINES */
454           /* erase the last cycle's point */
455           XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
456           XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi),
457                                  MI_GC(mi), lp[pctr].x, lp[pctr].y);
458           
459           /* Set the new color */
460           SET_COLOR();
461           
462           /* plot this cycle's point */
463           XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi),
464                                  MI_GC(mi), np[pctr].x, np[pctr].y);
465 #endif  /* DRAWLINES */
466         }
467   }
468         (void) free((void *) lp);
469         loop->lastpoint = np;
470         return True;
471 }
472
473 static Bool 
474 initlisa(ModeInfo * mi, lisas * loop)
475 {
476   lisacons   *lc = &Lisa[MI_SCREEN(mi)];
477   lisafuncs **lf = loop->function;
478   XPoint     *lp;
479   int         phase, pctr, fctr, xctr, yctr, extra_points;
480   double      xprod, yprod, xsum, ysum;
481   
482   xMaxLines = (XMaxRequestSize(MI_DISPLAY(mi))-3)/2;
483   /*  printf("Got xMaxLines = %d\n", xMaxLines); */
484   loop->nsteps = MI_CYCLES(mi);
485   if (loop->nsteps == 0)
486         loop->nsteps = 1;
487   if (MI_NPIXELS(mi) > 2) {
488         loop->color = STARTCOLOR;
489         loop->cstep = (loop->nsteps > MI_NPIXELS(mi)) ? loop->nsteps / MI_NPIXELS(mi) : 1;
490   } else {
491         loop->color = MI_WHITE_PIXEL(mi);
492         loop->cstep = 0;
493   }
494   extra_points = loop->cstep - (loop->nsteps % loop->cstep);
495   lc->maxcycles = (MAXCYCLES * loop->nsteps) - 1;
496   loop->cstep = ( loop->nsteps > MI_NPIXELS(mi) ) ? loop->nsteps / MI_NPIXELS(mi) : 1;
497   /*  printf("Got cstep = %d\n", loop->cstep); */
498   loop->melting = 0;
499   loop->nfuncs = 1;
500   loop->pistep = 2.0 * M_PI / (double) loop->nsteps;
501   loop->center.x = lc->width / 2;
502   loop->center.y = lc->height / 2;
503   loop->radius = (int) MI_SIZE(mi);
504   CHECK_RADIUS(loop, lc);
505   loop->dx = NRAND(XVMAX);
506   loop->dy = NRAND(YVMAX);
507   loop->dx++;
508   loop->dy++;
509 #if defined STARTFUNC
510   lf[0] = &Function[STARTFUNC];
511 #else  /* STARTFUNC */
512   lf[0] = &Function[NRAND(NUMSTDFUNCS)];
513 #endif  /* STARTFUNC */ 
514   
515   if ((lp = loop->lastpoint = (XPoint *)
516            calloc(loop->nsteps+extra_points, sizeof (XPoint))) == NULL) {
517         free_lisa(mi);
518         return False;
519   }
520   phase = lc->loopcount % loop->nsteps;
521   
522 #if defined DEBUG
523   printf( "nsteps = %d\tcstep = %d\tmrs = %d\textra_points = %d\n", 
524                   loop->nsteps, loop->cstep, xMaxLines, extra_points );
525   PRINT_FUNC(lf[0]);
526 #endif  /* DEBUG */
527   
528   for (pctr = 0; pctr < loop->nsteps; pctr++) {
529         loop->phi = (double) (pctr - phase) * loop->pistep;
530         loop->theta = (double) (pctr + phase) * loop->pistep;
531         fctr = loop->nfuncs;
532         xsum = ysum = 0.0;
533         while (fctr--) {
534           xprod = yprod = (double) loop->radius;
535           xctr = lf[fctr]->nx;
536           yctr = lf[fctr]->ny;
537           while (xctr--)
538                 xprod *= sin(lf[fctr]->xcoeff[xctr] * loop->theta);
539           while (yctr--)
540                 yprod *= sin(lf[fctr]->ycoeff[yctr] * loop->phi);
541           xsum += xprod;
542           ysum += yprod;
543         }
544         if (loop->nfuncs > 1) {
545           xsum /= 2.0;
546           ysum /= 2.0;
547         }
548         xsum += (double) loop->center.x;
549         ysum += (double) loop->center.y;
550         
551         lp[pctr].x = (int) ceil(xsum);
552         lp[pctr].y = (int) ceil(ysum);
553   }
554   /* this fills in the extra points, so we can use segment-drawing calls */
555   for (pctr = loop->nsteps; pctr < loop->nsteps + extra_points; pctr++) {
556         lp[pctr].x=lp[pctr - loop->nsteps].x;
557         lp[pctr].y=lp[pctr - loop->nsteps].y;
558   }
559 #if defined DRAWLINES
560   loop->linewidth = LINEWIDTH;  /* #### make this a resource */
561   
562   if (loop->linewidth == 0)
563         loop->linewidth = 1;
564   if (loop->linewidth < 0)
565         loop->linewidth = NRAND(-loop->linewidth) + 1;
566   XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), loop->linewidth,
567                                          LINESTYLE, LINECAP, LINEJOIN);
568 #endif  /* DRAWLINES */
569   
570   if ( loop->cstep < xMaxLines ) { 
571         /* we can send each color segment in a single request 
572          * because the max request length is long enough 
573          * and because we have padded out the array to have extra elements 
574          * to support calls which would otherwise fall off the end*/
575         for (pctr = 0; pctr < loop->nsteps; pctr+=loop->cstep) {
576           /* Set the color */
577           SET_COLOR();
578           
579 #if defined DRAWLINES
580           XDrawLines(MI_DISPLAY(mi), MI_WINDOW(mi),
581                                  MI_GC(mi), &lp[pctr], loop->cstep, CoordModeOrigin );
582 #else  /* DRAWLINES */
583           XDrawPoints(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
584                                   &lp[pctr], loop->cstep, CoordModeOrigin );
585 #endif  /* DRAWLINES */
586         }
587   } else { /* do it one by one as before */
588         for (pctr = 0; pctr < loop->nsteps; pctr++ ) {
589           SET_COLOR();
590           
591 #if defined DRAWLINES
592           XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
593                                 lp[pctr].x, lp[pctr].y,
594                                 lp[pctr+1 % loop->nsteps].x, lp[pctr+1 % loop->nsteps].y);
595 #else  /* DRAWLINES */
596           XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
597                                  lp[pctr].x, lp[pctr].y);
598 #endif  /* DRAWLINES */
599         }
600   }
601   
602 #if defined DRAWLINES
603   XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 1,
604                                          LINESTYLE, LINECAP, LINEJOIN);
605 #endif  /* DRAWLINES */
606   return True;
607 }
608
609 static void
610 refreshlisa(ModeInfo * mi)
611 {
612         lisacons   *lc = &Lisa[MI_SCREEN(mi)];
613         int         lctr;
614
615         for (lctr = 0; lctr < lc->nlissajous; lctr++) {
616                 if (!drawlisa(mi, &lc->lissajous[lctr]))
617                         return;
618         }
619 }
620
621 #ifndef STANDALONE
622 ENTRYPOINT void
623 refresh_lisa(ModeInfo * mi)
624 {
625         lisacons   *lc;
626
627         if (Lisa == NULL)
628                 return;
629         lc = &Lisa[MI_SCREEN(mi)];
630         if (lc->lissajous == NULL)
631                 return;
632
633         if (lc->painted) {
634                 lc->painted = False;
635                 MI_CLEARWINDOW(mi);
636                 refreshlisa(mi);
637         }
638 }
639 #endif
640
641 static void
642 change_lisa(ModeInfo * mi)
643 {
644         lisas      *loop;
645         int         lctr, newfunc;
646         lisacons   *lc;
647
648         if (Lisa == NULL)
649                 return;
650         lc = &Lisa[MI_SCREEN(mi)];
651         if (lc->lissajous == NULL)
652                 return;
653
654         lc->loopcount = 0;
655         for (lctr = 0; lctr < lc->nlissajous; lctr++) {
656                 loop = &lc->lissajous[lctr];       /* count through the loops we're drawing */
657         newfunc = NRAND(NUMSTDFUNCS);     /* choose a new function at random */
658 #if defined FOLLOW_FUNC_ORDER
659         loop->function[1] =
660           &Function[(loop->function[0]->index + 1) % NUMSTDFUNCS];
661 #else  /* FOLLOW_FUNC_ORDER */
662         if (newfunc == loop->function[0]->index) {
663           ++newfunc;
664           newfunc %= NUMSTDFUNCS;       /* take the next if we got the one we have */
665         }
666         if (newfunc >= RAREFUNCMIN \
667                 && !(random() % RAREFUNCODDS) \
668                 && (newfunc = NRAND(NUMSTDFUNCS)) == loop->function[0]->index) {
669           ++newfunc;
670           newfunc %= NUMSTDFUNCS;
671         }
672         loop->function[1] =               /* set 2nd function pointer on the loop */
673           &Function[newfunc];             /* to the new function we just chose */
674 #endif  /* FOLLOW_FUNC_ORDER */         
675 #if defined DEBUG
676         PRINT_FUNC(loop->function[1]);
677 #endif /* DEBUG */
678         loop->melting = loop->nsteps - 1; /* melt the two functions together */ 
679         loop->nfuncs = 2;                 /* simultaneously for a full cycle */  
680   }
681 }
682
683 ENTRYPOINT void
684 init_lisa (ModeInfo * mi)
685 {
686         int         lctr;
687         lisacons   *lc;
688
689         MI_INIT (mi, Lisa);
690         lc = &Lisa[MI_SCREEN(mi)];
691         lc->width = MI_WIDTH(mi);
692         lc->height = MI_HEIGHT(mi);
693         lc->loopcount = 0;
694         lc->nlissajous = MI_COUNT(mi);
695         if (lc->nlissajous <= 0)
696                 lc->nlissajous = 1;
697         MI_CLEARWINDOW(mi);
698         lc->painted = False;
699
700         if (lc->lissajous == NULL) {
701                 if ((lc->lissajous = (lisas *) calloc(lc->nlissajous,
702                                 sizeof (lisas))) == NULL)
703                         return;
704                 for (lctr = 0; lctr < lc->nlissajous; lctr++) {
705                         if (!initlisa(mi, &lc->lissajous[lctr]))
706                                 return;
707                         lc->loopcount++;
708                 }
709         } else {
710                 refreshlisa(mi);
711         }
712 }
713
714 ENTRYPOINT void
715 draw_lisa (ModeInfo * mi)
716 {
717         lisacons   *lc;
718
719         if (Lisa == NULL)
720                 return;
721         lc = &Lisa[MI_SCREEN(mi)];
722         if (lc->lissajous == NULL)
723                 return;
724
725 #ifdef HAVE_JWXYZ       /* Don't second-guess Quartz's double-buffering */
726     XClearWindow (MI_DISPLAY(mi), MI_WINDOW(mi));
727 #endif
728
729         MI_IS_DRAWN(mi) = True;
730         lc->painted = True;
731         if (++lc->loopcount > lc->maxcycles) {
732                 change_lisa(mi);
733         }
734         refreshlisa(mi);
735 }
736
737 XSCREENSAVER_MODULE ("Lisa", lisa)
738
739 #endif /* MODE_lisa */