550 lines
14 KiB
C
550 lines
14 KiB
C
/* -*- 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 */
|