1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* lisa --- animated full-loop lisajous figures */
4 #if !defined( lint ) && !defined( SABER )
5 static const char sccsid[] = "@(#)lisa.c 5.00 2000/11/01 xlockmore";
10 * Copyright (c) 1997 by Caleb Cullen.
12 * Permission to use, copy, modify, and distribute this software and its
13 * documentation for any purpose and without fee is hereby granted,
14 * provided that the above copyright notice appear in all copies and that
15 * both that copyright notice and this permission notice appear in
16 * supporting documentation.
18 * This file is provided AS IS with no warranties of any kind. The author
19 * shall have no liability with respect to the infringement of copyrights,
20 * trade secrets or any patents by this file or any part thereof. In no
21 * event will the author be liable for any lost revenue or profits or
22 * other special, indirect and consequential damages.
25 * 01-Nov-2000: Allocation checks
26 * 10-May-1997: Compatible with xscreensaver
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 lisajous
31 * figures on 386 machines and lower. This version bears only superficial
32 * resemblances to the original Lasp.
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.
41 #define PROGCLASS "Lisa"
42 #define HACK_INIT init_lisa
43 #define HACK_DRAW draw_lisa
44 #define lisa_opts xlockmore_opts
45 #define DEFAULTS "*delay: 25000 \n" \
50 #define UNIFORM_COLORS
51 #include "xlockmore.h" /* in xscreensaver distribution */
53 #else /* STANDALONE */
54 #include "xlock.h" /* in xlockmore distribution */
56 #endif /* STANDALONE */
60 #define DEF_ADDITIVE "True"
64 static XrmOptionDescRec opts[] =
66 {(char *) "-additive", (char *) ".lisa.additive", XrmoptionNoArg, (caddr_t) "True"},
67 {(char *) "+additive", (char *) ".lisa.additive", XrmoptionNoArg, (caddr_t) "False"}
70 static argtype vars[] =
72 {(caddr_t *) & additive, (char *) "additive", (char *) "Additive", (char *) DEF_ADDITIVE, t_Bool}
75 static OptionStruct desc[] =
77 {(char *) "-/+additive", (char *) "turn on/off additive functions mode"}
80 ModeSpecOpt lisa_opts =
81 {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
84 ModStruct lisa_description =
85 {"lisa", "init_lisa", "draw_lisa", "release_lisa",
86 "refresh_lisa", "change_lisa", (char *) NULL, &lisa_opts,
87 25000, 1, 256, -1, 64, 1.0, "",
88 "Shows animated lisajous loops", 0, NULL};
94 #define XVMAX 10 /* Maximum velocities */
96 #define LISAMAXFUNCS 2
97 #define NUMSTDFUNCS 10
100 #define lisasetcolor() \
101 if (MI_NPIXELS(mi) > 2) { \
102 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_PIXEL(mi, loop->color)); \
103 if (++(loop->color) >= (unsigned) MI_NPIXELS(mi)) { loop->color=0; } \
104 } else { XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi)); }
105 #define getRadius(context) \
106 ((context->width > context->height)?context->height:context->width) * 3 / 8
107 #define checkRadius(loop, context) \
108 if ((context->height / 2 > MI_SIZE(mi)) && (context->width / 2 > MI_SIZE(mi))) \
109 loop->radius = MI_SIZE(mi); \
110 if ((loop->radius < 0) || \
111 (loop->radius > loop->center.x) || \
112 (loop->radius > loop->center.y)) loop->radius = getRadius(context)
115 typedef struct lisafunc_struct {
116 double xcoeff[2], ycoeff[2];
121 typedef struct lisa_struct {
123 int radius, dx, dy, nsteps, nfuncs, melting;
124 double pistep, phi, theta;
125 XPoint center, *lastpoint;
126 lisafuncs *function[LISAMAXFUNCS];
130 typedef struct lisacontext_struct {
132 int width, height, nlisajous, loopcount;
137 static lisacons *Lisa = (lisacons *) NULL;
139 static lisafuncs Function[NUMSTDFUNCS] =
143 {1.0, 2.0}, 2, 2, 0},
146 {1.0, 1.0}, 2, 2, 1},
149 {1.0, 2.0}, 2, 2, 2},
152 {1.0, 3.0}, 2, 2, 3},
155 {1.0, 2.0}, 2, 2, 4},
158 {1.0, 3.0}, 2, 2, 5},
161 {1.0, 4.0}, 2, 2, 6},
164 {1.0, 5.0}, 2, 2, 7},
167 {2.0, 5.0}, 2, 2, 8},
174 free_lisa(lisacons *lc)
176 while (lc->lisajous) {
179 for (lctr = 0; lctr < lc->nlisajous; lctr++) {
180 (void) free((void *) lc->lisajous[lctr].lastpoint);
182 (void) free((void *) lc->lisajous);
183 lc->lisajous = (lisas *) NULL;
188 drawlisa(ModeInfo * mi, lisas * loop)
191 XPoint *lp = loop->lastpoint;
192 lisacons *lc = &Lisa[MI_SCREEN(mi)];
193 lisafuncs **lf = loop->function;
194 int phase = lc->loopcount % loop->nsteps;
195 int pctr, fctr, xctr, yctr;
196 double xprod, yprod, xsum, ysum;
198 /* Allocate the np array */
199 if ((np = (XPoint *) calloc(loop->nsteps, sizeof (XPoint))) == NULL) {
204 /* Update the center */
205 loop->center.x += loop->dx;
206 loop->center.y += loop->dy;
207 checkRadius(loop, lc);
208 if ((loop->center.x - loop->radius) <= 0) {
209 loop->center.x = loop->radius;
210 loop->dx = NRAND(XVMAX);
211 } else if ((loop->center.x + loop->radius) >= lc->width) {
212 loop->center.x = lc->width - loop->radius;
213 loop->dx = -NRAND(XVMAX);
215 if ((loop->center.y - loop->radius) <= 0) {
216 loop->center.y = loop->radius;
217 loop->dy = NRAND(YVMAX);
218 } else if ((loop->center.y + loop->radius) >= lc->height) {
219 loop->center.y = lc->height - loop->radius;
220 loop->dy = -NRAND(YVMAX);
223 /* Now draw the points, and erase the ones from the last cycle */
225 for (pctr = 0; pctr < loop->nsteps; pctr++) {
227 loop->phi = (double) (pctr - phase) * loop->pistep;
228 loop->theta = (double) (pctr + phase) * loop->pistep;
236 xprod += sin(lf[fctr]->xcoeff[xctr] * loop->theta);
238 yprod += sin(lf[fctr]->ycoeff[yctr] * loop->phi);
241 xsum += xprod * (double) (loop->nsteps - loop->melting) /
242 (double) loop->nsteps;
243 ysum += yprod * (double) (loop->nsteps - loop->melting) /
244 (double) loop->nsteps;
246 xsum += xprod * (double) loop->melting / (double) loop->nsteps;
247 ysum += yprod * (double) loop->melting / (double) loop->nsteps;
254 xsum = xsum * (double) loop->radius / (double) lf[fctr]->nx;
255 ysum = ysum * (double) loop->radius / (double) lf[fctr]->ny;
260 yprod = xprod = (double) loop->radius *
261 (double) (loop->nsteps - loop->melting) /
262 (double) (loop->nsteps);
264 yprod = xprod = (double) loop->radius *
265 (double) (loop->melting) / (double) (loop->nsteps);
268 xprod = yprod = (double) loop->radius;
271 xprod *= sin(lf[fctr]->xcoeff[xctr] * loop->theta);
273 yprod *= sin(lf[fctr]->ycoeff[yctr] * loop->phi);
278 if ((loop->nfuncs > 1) && (!loop->melting)) {
279 xsum /= (double) loop->nfuncs;
280 ysum /= (double) loop->nfuncs;
282 xsum += (double) loop->center.x;
283 ysum += (double) loop->center.y;
285 np[pctr].x = (int) ceil(xsum);
286 np[pctr].y = (int) ceil(ysum);
289 if (!--loop->melting) {
291 loop->function[0] = loop->function[1];
294 for (pctr = 0; pctr < loop->nsteps; pctr++) {
296 #if defined DRAWLINES
297 XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), loop->linewidth,
298 LineSolid, CapProjecting, JoinMiter);
299 /* erase the last cycle's point */
300 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
301 XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi),
302 MI_GC(mi), lp[pctr].x, lp[pctr].y,
303 lp[(pctr + 1) % loop->nsteps].x,
304 lp[(pctr + 1) % loop->nsteps].y);
306 /* Set the new color */
309 /* plot this cycle's point */
310 XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi),
311 MI_GC(mi), np[pctr].x, np[pctr].y,
312 np[(pctr + 1) % loop->nsteps].x,
313 np[(pctr + 1) % loop->nsteps].y);
314 XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 1,
315 LineSolid, CapProjecting, JoinMiter);
317 /* erase the last cycle's point */
318 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
319 XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi),
320 MI_GC(mi), lp[pctr].x, lp[pctr].y);
322 /* Set the new color */
325 /* plot this cycle's point */
326 XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi),
327 MI_GC(mi), np[pctr].x, np[pctr].y);
330 (void) free((void *) lp);
331 loop->lastpoint = np;
336 initlisa(ModeInfo * mi, lisas * loop)
338 lisacons *lc = &Lisa[MI_SCREEN(mi)];
339 lisafuncs **lf = loop->function;
341 int phase, pctr, fctr, xctr, yctr;
342 double xprod, yprod, xsum, ysum;
344 if (MI_NPIXELS(mi) > 2) {
347 loop->color = MI_WHITE_PIXEL(mi);
348 loop->nsteps = MI_CYCLES(mi);
349 if (loop->nsteps == 0)
351 lc->maxcycles = (MAXCYCLES * loop->nsteps) - 1;
354 loop->pistep = 2.0 * M_PI / (double) loop->nsteps;
355 loop->center.x = lc->width / 2;
356 loop->center.y = lc->height / 2;
357 loop->radius = (int) MI_SIZE(mi);
358 checkRadius(loop, lc);
359 loop->dx = NRAND(XVMAX);
360 loop->dy = NRAND(YVMAX);
363 lf[0] = &Function[lc->loopcount % NUMSTDFUNCS];
364 if ((lp = loop->lastpoint = (XPoint *)
365 calloc(loop->nsteps, sizeof (XPoint))) == NULL) {
369 phase = lc->loopcount % loop->nsteps;
371 for (pctr = 0; pctr < loop->nsteps; pctr++) {
372 loop->phi = (double) (pctr - phase) * loop->pistep;
373 loop->theta = (double) (pctr + phase) * loop->pistep;
377 xprod = yprod = (double) loop->radius;
381 xprod *= sin(lf[fctr]->xcoeff[xctr] * loop->theta);
383 yprod *= sin(lf[fctr]->ycoeff[yctr] * loop->phi);
387 if (loop->nfuncs > 1) {
391 xsum += (double) loop->center.x;
392 ysum += (double) loop->center.y;
394 lp[pctr].x = (int) ceil(xsum);
395 lp[pctr].y = (int) ceil(ysum);
397 #if defined DRAWLINES
399 loop->linewidth = -8; /* #### make this a resource */
401 if (loop->linewidth == 0)
403 if (loop->linewidth < 0)
404 loop->linewidth = NRAND(-loop->linewidth) + 1;
405 XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), loop->linewidth,
406 LineSolid, CapProjecting, JoinMiter);
409 for (pctr = 0; pctr < loop->nsteps; pctr++) {
412 #if defined DRAWLINES
413 XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi),
414 MI_GC(mi), lp[pctr].x, lp[pctr].y,
415 lp[(pctr + 1) % loop->nsteps].x,
416 lp[(pctr + 1) % loop->nsteps].y);
418 XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
419 lp[pctr].x, lp[pctr].y);
422 #if defined DRAWLINES
423 XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 1,
424 LineSolid, CapProjecting, JoinMiter);
430 refreshlisa(ModeInfo * mi)
432 lisacons *lc = &Lisa[MI_SCREEN(mi)];
435 for (lctr = 0; lctr < lc->nlisajous; lctr++) {
436 if (!drawlisa(mi, &lc->lisajous[lctr]))
442 refresh_lisa(ModeInfo * mi)
448 lc = &Lisa[MI_SCREEN(mi)];
449 if (lc->lisajous == NULL)
460 change_lisa(ModeInfo * mi)
468 lc = &Lisa[MI_SCREEN(mi)];
469 if (lc->lisajous == NULL)
473 for (lctr = 0; lctr < lc->nlisajous; lctr++) {
474 loop = &lc->lisajous[lctr];
475 loop->function[1] = &Function[(loop->function[0]->index + 1) %
477 loop->melting = loop->nsteps - 1;
483 init_lisa(ModeInfo * mi)
489 if ((Lisa = (lisacons *) calloc(MI_NUM_SCREENS(mi),
490 sizeof (lisacons))) == NULL)
493 lc = &Lisa[MI_SCREEN(mi)];
494 lc->width = MI_WIDTH(mi);
495 lc->height = MI_HEIGHT(mi);
497 lc->nlisajous = MI_COUNT(mi);
498 if (lc->nlisajous <= 0)
503 if (lc->lisajous == NULL) {
504 if ((lc->lisajous = (lisas *) calloc(lc->nlisajous,
505 sizeof (lisas))) == NULL)
507 for (lctr = 0; lctr < lc->nlisajous; lctr++) {
508 if (!initlisa(mi, &lc->lisajous[lctr]))
518 draw_lisa(ModeInfo * mi)
524 lc = &Lisa[MI_SCREEN(mi)];
525 if (lc->lisajous == NULL)
528 MI_IS_DRAWN(mi) = True;
530 if (++lc->loopcount > lc->maxcycles) {
537 release_lisa(ModeInfo * mi)
542 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
543 free_lisa(&Lisa[screen]);
545 Lisa = (lisacons *) NULL;
549 #endif /* MODE_lisa */