/* -*- Mode: C; tab-width: 4 -*- */ /* lisa --- animated full-loop lisajous figures */ #if !defined( lint ) && !defined( SABER ) static const char sccsid[] = "@(#)lisa.c 5.00 2000/11/01 xlockmore"; #endif /*- * Copyright (c) 1997 by Caleb Cullen. * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation. * * This file is provided AS IS with no warranties of any kind. The author * shall have no liability with respect to the infringement of copyrights, * trade secrets or any patents by this file or any part thereof. In no * event will the author be liable for any lost revenue or profits or * other special, indirect and consequential damages. * * Revision History: * 01-Nov-2000: Allocation checks * 10-May-1997: Compatible with xscreensaver * * The inspiration for this program, Lasp, was written by Adam B. Roach * in 1990, assisted by me, Caleb Cullen. It was written first in C, then * in assembly, and used pre-calculated data tables to graph lisajous * figures on 386 machines and lower. This version bears only superficial * resemblances to the original Lasp. * * The `lissie' module's source code was studied as an example of how * to incorporate a new module into xlock. Resemblances to it are * expected, but not intended to be plaigiaristic. */ #ifdef STANDALONE #define MODE_lisa #define PROGCLASS "Lisa" #define HACK_INIT init_lisa #define HACK_DRAW draw_lisa #define lisa_opts xlockmore_opts #define DEFAULTS "*delay: 25000 \n" \ "*count: 1 \n" \ "*cycles: 256 \n" \ "*size: -1 \n" \ "*ncolors: 200 \n" #define UNIFORM_COLORS #include "xlockmore.h" /* in xscreensaver distribution */ #else /* STANDALONE */ #include "xlock.h" /* in xlockmore distribution */ #endif /* STANDALONE */ #ifdef MODE_lisa #define DEF_ADDITIVE "True" static Bool additive; static XrmOptionDescRec opts[] = { {(char *) "-additive", (char *) ".lisa.additive", XrmoptionNoArg, (caddr_t) "True"}, {(char *) "+additive", (char *) ".lisa.additive", XrmoptionNoArg, (caddr_t) "False"} }; static argtype vars[] = { {(void *) & additive, (char *) "additive", (char *) "Additive", (char *) DEF_ADDITIVE, t_Bool} }; static OptionStruct desc[] = { {(char *) "-/+additive", (char *) "turn on/off additive functions mode"} }; ModeSpecOpt lisa_opts = {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc}; #ifdef USE_MODULES ModStruct lisa_description = {"lisa", "init_lisa", "draw_lisa", "release_lisa", "refresh_lisa", "change_lisa", (char *) NULL, &lisa_opts, 25000, 1, 256, -1, 64, 1.0, "", "Shows animated lisajous loops", 0, NULL}; #endif #define DRAWLINES 1 #define TWOLOOPS 1 #define XVMAX 10 /* Maximum velocities */ #define YVMAX 10 #define LISAMAXFUNCS 2 #define NUMSTDFUNCS 10 #define MAXCYCLES 3 #define MINLISAS 1 #define lisasetcolor() \ if (MI_NPIXELS(mi) > 2) { \ XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_PIXEL(mi, loop->color)); \ if (++(loop->color) >= (unsigned) MI_NPIXELS(mi)) { loop->color=0; } \ } else { XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi)); } #define getRadius(context) \ ((context->width > context->height)?context->height:context->width) * 3 / 8 #define checkRadius(loop, context) \ if ((context->height / 2 > MI_SIZE(mi)) && (context->width / 2 > MI_SIZE(mi))) \ loop->radius = MI_SIZE(mi); \ if ((loop->radius < 0) || \ (loop->radius > loop->center.x) || \ (loop->radius > loop->center.y)) loop->radius = getRadius(context) typedef struct lisafunc_struct { double xcoeff[2], ycoeff[2]; int nx, ny; int indx; } lisafuncs; typedef struct lisa_struct { unsigned long color; int radius, dx, dy, nsteps, nfuncs, melting; double pistep, phi, theta; XPoint center, *lastpoint; lisafuncs *function[LISAMAXFUNCS]; int linewidth; } lisas; typedef struct lisacontext_struct { lisas *lisajous; int width, height, nlisajous, loopcount; int maxcycles; Bool painted; } lisacons; static lisacons *Lisa = (lisacons *) NULL; static lisafuncs Function[NUMSTDFUNCS] = { { {1.0, 2.0}, {1.0, 2.0}, 2, 2, 0}, { {1.0, 2.0}, {1.0, 1.0}, 2, 2, 1}, { {1.0, 3.0}, {1.0, 2.0}, 2, 2, 2}, { {1.0, 3.0}, {1.0, 3.0}, 2, 2, 3}, { {2.0, 4.0}, {1.0, 2.0}, 2, 2, 4}, { {1.0, 4.0}, {1.0, 3.0}, 2, 2, 5}, { {1.0, 4.0}, {1.0, 4.0}, 2, 2, 6}, { {1.0, 5.0}, {1.0, 5.0}, 2, 2, 7}, { {2.0, 5.0}, {2.0, 5.0}, 2, 2, 8}, { {1.0, 0.0}, {1.0, 0.0}, 1, 1, 9} }; static void free_lisa(lisacons *lc) { while (lc->lisajous) { int lctr; for (lctr = 0; lctr < lc->nlisajous; lctr++) { free(lc->lisajous[lctr].lastpoint); } free(lc->lisajous); lc->lisajous = (lisas *) NULL; } } static Bool drawlisa(ModeInfo * mi, lisas * loop) { XPoint *np; XPoint *lp = loop->lastpoint; lisacons *lc = &Lisa[MI_SCREEN(mi)]; lisafuncs **lf = loop->function; int phase = lc->loopcount % loop->nsteps; int pctr, fctr, xctr, yctr; double xprod, yprod, xsum, ysum; /* Allocate the np array */ if ((np = (XPoint *) calloc(loop->nsteps, sizeof (XPoint))) == NULL) { free_lisa(lc); return False; } /* Update the center */ loop->center.x += loop->dx; loop->center.y += loop->dy; checkRadius(loop, lc); if ((loop->center.x - loop->radius) <= 0) { loop->center.x = loop->radius; loop->dx = NRAND(XVMAX); } else if ((loop->center.x + loop->radius) >= lc->width) { loop->center.x = lc->width - loop->radius; loop->dx = -NRAND(XVMAX); }; if ((loop->center.y - loop->radius) <= 0) { loop->center.y = loop->radius; loop->dy = NRAND(YVMAX); } else if ((loop->center.y + loop->radius) >= lc->height) { loop->center.y = lc->height - loop->radius; loop->dy = -NRAND(YVMAX); }; /* Now draw the points, and erase the ones from the last cycle */ for (pctr = 0; pctr < loop->nsteps; pctr++) { fctr = loop->nfuncs; loop->phi = (double) (pctr - phase) * loop->pistep; loop->theta = (double) (pctr + phase) * loop->pistep; xsum = ysum = 0; while (fctr--) { xctr = lf[fctr]->nx; yctr = lf[fctr]->ny; if (additive) { xprod = yprod = 0.0; while (xctr--) xprod += sin(lf[fctr]->xcoeff[xctr] * loop->theta); while (yctr--) yprod += sin(lf[fctr]->ycoeff[yctr] * loop->phi); if (loop->melting) { if (fctr) { xsum += xprod * (double) (loop->nsteps - loop->melting) / (double) loop->nsteps; ysum += yprod * (double) (loop->nsteps - loop->melting) / (double) loop->nsteps; } else { xsum += xprod * (double) loop->melting / (double) loop->nsteps; ysum += yprod * (double) loop->melting / (double) loop->nsteps; } } else { xsum = xprod; ysum = yprod; } if (!fctr) { xsum = xsum * (double) loop->radius / (double) lf[fctr]->nx; ysum = ysum * (double) loop->radius / (double) lf[fctr]->ny; } } else { if (loop->melting) { if (fctr) { yprod = xprod = (double) loop->radius * (double) (loop->nsteps - loop->melting) / (double) (loop->nsteps); } else { yprod = xprod = (double) loop->radius * (double) (loop->melting) / (double) (loop->nsteps); } } else { xprod = yprod = (double) loop->radius; } while (xctr--) xprod *= sin(lf[fctr]->xcoeff[xctr] * loop->theta); while (yctr--) yprod *= sin(lf[fctr]->ycoeff[yctr] * loop->phi); xsum += xprod; ysum += yprod; } } if ((loop->nfuncs > 1) && (!loop->melting)) { xsum /= (double) loop->nfuncs; ysum /= (double) loop->nfuncs; } xsum += (double) loop->center.x; ysum += (double) loop->center.y; np[pctr].x = (int) ceil(xsum); np[pctr].y = (int) ceil(ysum); } if (loop->melting) { if (!--loop->melting) { loop->nfuncs = 1; loop->function[0] = loop->function[1]; } } for (pctr = 0; pctr < loop->nsteps; pctr++) { #if defined DRAWLINES XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), loop->linewidth, LineSolid, CapProjecting, JoinMiter); /* erase the last cycle's point */ XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi)); XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), lp[pctr].x, lp[pctr].y, lp[(pctr + 1) % loop->nsteps].x, lp[(pctr + 1) % loop->nsteps].y); /* Set the new color */ lisasetcolor(); /* plot this cycle's point */ XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), np[pctr].x, np[pctr].y, np[(pctr + 1) % loop->nsteps].x, np[(pctr + 1) % loop->nsteps].y); XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 1, LineSolid, CapProjecting, JoinMiter); #else /* erase the last cycle's point */ XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi)); XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), lp[pctr].x, lp[pctr].y); /* Set the new color */ lisasetcolor(); /* plot this cycle's point */ XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), np[pctr].x, np[pctr].y); #endif } free(lp); loop->lastpoint = np; return True; } static Bool initlisa(ModeInfo * mi, lisas * loop) { lisacons *lc = &Lisa[MI_SCREEN(mi)]; lisafuncs **lf = loop->function; XPoint *lp; int phase, pctr, fctr, xctr, yctr; double xprod, yprod, xsum, ysum; if (MI_NPIXELS(mi) > 2) { loop->color = 0; } else loop->color = MI_WHITE_PIXEL(mi); loop->nsteps = MI_CYCLES(mi); if (loop->nsteps < 1) loop->nsteps = 1; lc->maxcycles = (MAXCYCLES * loop->nsteps) - 1; loop->melting = 0; loop->nfuncs = 1; loop->pistep = 2.0 * M_PI / (double) loop->nsteps; loop->center.x = lc->width / 2; loop->center.y = lc->height / 2; loop->radius = (int) MI_SIZE(mi); checkRadius(loop, lc); loop->dx = NRAND(XVMAX); loop->dy = NRAND(YVMAX); loop->dx++; loop->dy++; lf[0] = &Function[lc->loopcount % NUMSTDFUNCS]; if ((lp = loop->lastpoint = (XPoint *) calloc(loop->nsteps, sizeof (XPoint))) == NULL) { free_lisa(lc); return False; } phase = lc->loopcount % loop->nsteps; for (pctr = 0; pctr < loop->nsteps; pctr++) { loop->phi = (double) (pctr - phase) * loop->pistep; loop->theta = (double) (pctr + phase) * loop->pistep; fctr = loop->nfuncs; xsum = ysum = 0.0; while (fctr--) { xprod = yprod = (double) loop->radius; xctr = lf[fctr]->nx; yctr = lf[fctr]->ny; while (xctr--) xprod *= sin(lf[fctr]->xcoeff[xctr] * loop->theta); while (yctr--) yprod *= sin(lf[fctr]->ycoeff[yctr] * loop->phi); xsum += xprod; ysum += yprod; } if (loop->nfuncs > 1) { xsum /= 2.0; ysum /= 2.0; } xsum += (double) loop->center.x; ysum += (double) loop->center.y; lp[pctr].x = (int) ceil(xsum); lp[pctr].y = (int) ceil(ysum); } #if defined DRAWLINES { loop->linewidth = -8; /* #### make this a resource */ if (loop->linewidth == 0) loop->linewidth = 1; if (loop->linewidth < 0) loop->linewidth = NRAND(-loop->linewidth) + 1; XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), loop->linewidth, LineSolid, CapProjecting, JoinMiter); } #endif for (pctr = 0; pctr < loop->nsteps; pctr++) { /* Set the color */ lisasetcolor(); #if defined DRAWLINES XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), lp[pctr].x, lp[pctr].y, lp[(pctr + 1) % loop->nsteps].x, lp[(pctr + 1) % loop->nsteps].y); #else XDrawPoint(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), lp[pctr].x, lp[pctr].y); #endif } #if defined DRAWLINES XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 1, LineSolid, CapProjecting, JoinMiter); #endif return True; } static void refreshlisa(ModeInfo * mi) { lisacons *lc = &Lisa[MI_SCREEN(mi)]; int lctr; for (lctr = 0; lctr < lc->nlisajous; lctr++) { if (!drawlisa(mi, &lc->lisajous[lctr])) return; } } void refresh_lisa(ModeInfo * mi) { lisacons *lc; if (Lisa == NULL) return; lc = &Lisa[MI_SCREEN(mi)]; if (lc->lisajous == NULL) return; if (lc->painted) { lc->painted = False; MI_CLEARWINDOW(mi); refreshlisa(mi); } } void change_lisa(ModeInfo * mi) { lisas *loop; int lctr; lisacons *lc; if (Lisa == NULL) return; lc = &Lisa[MI_SCREEN(mi)]; if (lc->lisajous == NULL) return; lc->loopcount = 0; for (lctr = 0; lctr < lc->nlisajous; lctr++) { loop = &lc->lisajous[lctr]; loop->function[1] = &Function[(loop->function[0]->indx + 1) % NUMSTDFUNCS]; loop->melting = loop->nsteps - 1; loop->nfuncs = 2; } } void init_lisa(ModeInfo * mi) { int lctr; lisacons *lc; if (Lisa == NULL) { if ((Lisa = (lisacons *) calloc(MI_NUM_SCREENS(mi), sizeof (lisacons))) == NULL) return; } lc = &Lisa[MI_SCREEN(mi)]; lc->width = MI_WIDTH(mi); lc->height = MI_HEIGHT(mi); lc->loopcount = 0; lc->nlisajous = MI_COUNT(mi); if (lc->nlisajous <= 0) lc->nlisajous = 1; MI_CLEARWINDOW(mi); lc->painted = False; if (lc->lisajous == NULL) { if ((lc->lisajous = (lisas *) calloc(lc->nlisajous, sizeof (lisas))) == NULL) return; for (lctr = 0; lctr < lc->nlisajous; lctr++) { if (!initlisa(mi, &lc->lisajous[lctr])) return; lc->loopcount++; } } else { refreshlisa(mi); } } void draw_lisa(ModeInfo * mi) { lisacons *lc; if (Lisa == NULL) return; lc = &Lisa[MI_SCREEN(mi)]; if (lc->lisajous == NULL) return; MI_IS_DRAWN(mi) = True; lc->painted = True; if (++lc->loopcount > lc->maxcycles) { change_lisa(mi); } refreshlisa(mi); } void release_lisa(ModeInfo * mi) { if (Lisa) { int screen; for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) free_lisa(&Lisa[screen]); free(Lisa); Lisa = (lisacons *) NULL; } } #endif /* MODE_lisa */