1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* lisa --- animated full-loop lisajous figures */
5 static const char sccsid[] = "@(#)lisa.c 5.00 2000/11/01 xlockmore";
9 * Copyright (c) 1997 by Caleb Cullen.
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.
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.
24 * 01-Nov-2000: Allocation checks
25 * 10-May-1997: Compatible with xscreensaver
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.
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.
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" \
49 #define UNIFORM_COLORS
50 #include "xlockmore.h" /* in xscreensaver distribution */
52 #else /* STANDALONE */
53 #include "xlock.h" /* in xlockmore distribution */
55 #endif /* STANDALONE */
59 #define DEF_ADDITIVE "True"
63 static XrmOptionDescRec opts[] =
65 {"-additive", ".lisa.additive", XrmoptionNoArg, "True"},
66 {"+additive", ".lisa.additive", XrmoptionNoArg, "False"}
69 static argtype vars[] =
71 {&additive, "additive", "Additive", DEF_ADDITIVE, t_Bool}
74 static OptionStruct desc[] =
76 {"-/+additive", "turn on/off additive functions mode"}
79 ModeSpecOpt lisa_opts =
80 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
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};
93 #define XVMAX 10 /* Maximum velocities */
95 #define LISAMAXFUNCS 2
96 #define NUMSTDFUNCS 10
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)
114 typedef struct lisafunc_struct {
115 double xcoeff[2], ycoeff[2];
120 typedef struct lisa_struct {
122 int radius, dx, dy, nsteps, nfuncs, melting;
123 double pistep, phi, theta;
124 XPoint center, *lastpoint;
125 lisafuncs *function[LISAMAXFUNCS];
129 typedef struct lisacontext_struct {
131 int width, height, nlisajous, loopcount;
136 static lisacons *Lisa = (lisacons *) NULL;
138 static lisafuncs Function[NUMSTDFUNCS] =
142 {1.0, 2.0}, 2, 2, 0},
145 {1.0, 1.0}, 2, 2, 1},
148 {1.0, 2.0}, 2, 2, 2},
151 {1.0, 3.0}, 2, 2, 3},
154 {1.0, 2.0}, 2, 2, 4},
157 {1.0, 3.0}, 2, 2, 5},
160 {1.0, 4.0}, 2, 2, 6},
163 {1.0, 5.0}, 2, 2, 7},
166 {2.0, 5.0}, 2, 2, 8},
173 free_lisa(lisacons *lc)
175 while (lc->lisajous) {
178 for (lctr = 0; lctr < lc->nlisajous; lctr++) {
179 (void) free((void *) lc->lisajous[lctr].lastpoint);
181 (void) free((void *) lc->lisajous);
182 lc->lisajous = (lisas *) NULL;
187 drawlisa(ModeInfo * mi, lisas * loop)
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;
197 /* Allocate the np array */
198 if ((np = (XPoint *) calloc(loop->nsteps, sizeof (XPoint))) == NULL) {
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);
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);
222 /* Now draw the points, and erase the ones from the last cycle */
224 for (pctr = 0; pctr < loop->nsteps; pctr++) {
226 loop->phi = (double) (pctr - phase) * loop->pistep;
227 loop->theta = (double) (pctr + phase) * loop->pistep;
235 xprod += sin(lf[fctr]->xcoeff[xctr] * loop->theta);
237 yprod += sin(lf[fctr]->ycoeff[yctr] * loop->phi);
240 xsum += xprod * (double) (loop->nsteps - loop->melting) /
241 (double) loop->nsteps;
242 ysum += yprod * (double) (loop->nsteps - loop->melting) /
243 (double) loop->nsteps;
245 xsum += xprod * (double) loop->melting / (double) loop->nsteps;
246 ysum += yprod * (double) loop->melting / (double) loop->nsteps;
253 xsum = xsum * (double) loop->radius / (double) lf[fctr]->nx;
254 ysum = ysum * (double) loop->radius / (double) lf[fctr]->ny;
259 yprod = xprod = (double) loop->radius *
260 (double) (loop->nsteps - loop->melting) /
261 (double) (loop->nsteps);
263 yprod = xprod = (double) loop->radius *
264 (double) (loop->melting) / (double) (loop->nsteps);
267 xprod = yprod = (double) loop->radius;
270 xprod *= sin(lf[fctr]->xcoeff[xctr] * loop->theta);
272 yprod *= sin(lf[fctr]->ycoeff[yctr] * loop->phi);
277 if ((loop->nfuncs > 1) && (!loop->melting)) {
278 xsum /= (double) loop->nfuncs;
279 ysum /= (double) loop->nfuncs;
281 xsum += (double) loop->center.x;
282 ysum += (double) loop->center.y;
284 np[pctr].x = (int) ceil(xsum);
285 np[pctr].y = (int) ceil(ysum);
288 if (!--loop->melting) {
290 loop->function[0] = loop->function[1];
293 for (pctr = 0; pctr < loop->nsteps; pctr++) {
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);
305 /* Set the new color */
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);
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);
321 /* Set the new color */
324 /* plot this cycle's point */
325 XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi),
326 MI_GC(mi), np[pctr].x, np[pctr].y);
329 (void) free((void *) lp);
330 loop->lastpoint = np;
335 initlisa(ModeInfo * mi, lisas * loop)
337 lisacons *lc = &Lisa[MI_SCREEN(mi)];
338 lisafuncs **lf = loop->function;
340 int phase, pctr, fctr, xctr, yctr;
341 double xprod, yprod, xsum, ysum;
343 if (MI_NPIXELS(mi) > 2) {
346 loop->color = MI_WHITE_PIXEL(mi);
347 loop->nsteps = MI_CYCLES(mi);
348 if (loop->nsteps == 0)
350 lc->maxcycles = (MAXCYCLES * loop->nsteps) - 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);
362 lf[0] = &Function[lc->loopcount % NUMSTDFUNCS];
363 if ((lp = loop->lastpoint = (XPoint *)
364 calloc(loop->nsteps, sizeof (XPoint))) == NULL) {
368 phase = lc->loopcount % loop->nsteps;
370 for (pctr = 0; pctr < loop->nsteps; pctr++) {
371 loop->phi = (double) (pctr - phase) * loop->pistep;
372 loop->theta = (double) (pctr + phase) * loop->pistep;
376 xprod = yprod = (double) loop->radius;
380 xprod *= sin(lf[fctr]->xcoeff[xctr] * loop->theta);
382 yprod *= sin(lf[fctr]->ycoeff[yctr] * loop->phi);
386 if (loop->nfuncs > 1) {
390 xsum += (double) loop->center.x;
391 ysum += (double) loop->center.y;
393 lp[pctr].x = (int) ceil(xsum);
394 lp[pctr].y = (int) ceil(ysum);
396 #if defined DRAWLINES
398 loop->linewidth = -8; /* #### make this a resource */
400 if (loop->linewidth == 0)
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);
408 for (pctr = 0; pctr < loop->nsteps; pctr++) {
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);
417 XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
418 lp[pctr].x, lp[pctr].y);
421 #if defined DRAWLINES
422 XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 1,
423 LineSolid, CapProjecting, JoinMiter);
429 refreshlisa(ModeInfo * mi)
431 lisacons *lc = &Lisa[MI_SCREEN(mi)];
434 for (lctr = 0; lctr < lc->nlisajous; lctr++) {
435 if (!drawlisa(mi, &lc->lisajous[lctr]))
441 refresh_lisa(ModeInfo * mi)
447 lc = &Lisa[MI_SCREEN(mi)];
448 if (lc->lisajous == NULL)
459 change_lisa(ModeInfo * mi)
467 lc = &Lisa[MI_SCREEN(mi)];
468 if (lc->lisajous == NULL)
472 for (lctr = 0; lctr < lc->nlisajous; lctr++) {
473 loop = &lc->lisajous[lctr];
474 loop->function[1] = &Function[(loop->function[0]->index + 1) %
476 loop->melting = loop->nsteps - 1;
482 init_lisa(ModeInfo * mi)
488 if ((Lisa = (lisacons *) calloc(MI_NUM_SCREENS(mi),
489 sizeof (lisacons))) == NULL)
492 lc = &Lisa[MI_SCREEN(mi)];
493 lc->width = MI_WIDTH(mi);
494 lc->height = MI_HEIGHT(mi);
496 lc->nlisajous = MI_COUNT(mi);
497 if (lc->nlisajous <= 0)
502 if (lc->lisajous == NULL) {
503 if ((lc->lisajous = (lisas *) calloc(lc->nlisajous,
504 sizeof (lisas))) == NULL)
506 for (lctr = 0; lctr < lc->nlisajous; lctr++) {
507 if (!initlisa(mi, &lc->lisajous[lctr]))
517 draw_lisa(ModeInfo * mi)
523 lc = &Lisa[MI_SCREEN(mi)];
524 if (lc->lisajous == NULL)
527 MI_IS_DRAWN(mi) = True;
529 if (++lc->loopcount > lc->maxcycles) {
536 release_lisa(ModeInfo * mi)
541 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
542 free_lisa(&Lisa[screen]);
544 Lisa = (lisacons *) NULL;
548 #endif /* MODE_lisa */