xenocara/app/xlockmore/modes/qix.c
2006-11-26 11:07:42 +00:00

511 lines
14 KiB
C

/* -*- Mode: C; tab-width: 4 -*- */
/* qix --- vector swirl */
#if !defined( lint ) && !defined( SABER )
static const char sccsid[] = "@(#)qix.c 5.00 2000/11/01 xlockmore";
#endif
/*-
* Copyright (c) 1991 by Patrick J. Naughton.
*
* 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
* 03-Mar-1998: Combined with Darrick Brown's "geometry".
* 10-May-1997: Compatible with xscreensaver
* 29-Jul-1990: support for multiple screens.
* made check_bounds_?() a macro.
* fixed initial parameter setup.
* 15-Dec-1989: Fix for proper skipping of {White,Black}Pixel() in colors.
* 08-Oct-1989: Fixed bug in memory allocation in init_qix().
* Moved seconds() to an extern.
* 23-Sep-1989: Switch to random() and fixed bug w/ less than 4 lines.
* 20-Sep-1989: Lint.
* 24-Mar-1989: Written.
*/
#ifdef STANDALONE
#define MODE_qix
#define PROGCLASS "Qix"
#define HACK_INIT init_qix
#define HACK_DRAW draw_qix
#define qix_opts xlockmore_opts
#define DEFAULTS "*delay: 30000 \n" \
"*count: -5 \n" \
"*cycles: 32 \n" \
"*ncolors: 200 \n" \
"*fullrandom: True \n"
#define SMOOTH_COLORS
#include "xlockmore.h" /* in xscreensaver distribution */
#else /* STANDALONE */
#include "xlock.h" /* in xlockmore distribution */
#endif /* STANDALONE */
#ifdef MODE_qix
#define DEF_COMPLETE "False"
#define DEF_KALEID "False"
#define DEF_SOLID "False"
static Bool complete;
static Bool kaleid;
static Bool solid;
static XrmOptionDescRec opts[] =
{
{(char *) "-complete", (char *) ".qix.complete", XrmoptionNoArg, (caddr_t) "on"},
{(char *) "+complete", (char *) ".qix.complete", XrmoptionNoArg, (caddr_t) "off"},
{(char *) "-kaleid", (char *) ".qix.kaleid", XrmoptionNoArg, (caddr_t) "on"},
{(char *) "+kaleid", (char *) ".qix.kaleid", XrmoptionNoArg, (caddr_t) "off"},
{(char *) "-solid", (char *) ".qix.solid", XrmoptionNoArg, (caddr_t) "on"},
{(char *) "+solid", (char *) ".qix.solid", XrmoptionNoArg, (caddr_t) "off"}
};
static argtype vars[] =
{
{(void *) & complete, (char *) "complete", (char *) "Complete", (char *) DEF_COMPLETE, t_Bool},
{(void *) & kaleid, (char *) "kaleid", (char *) "Kaleid", (char *) DEF_KALEID, t_Bool},
{(void *) & solid, (char *) "solid", (char *) "Solid", (char *) DEF_SOLID, t_Bool}
};
static OptionStruct desc[] =
{
{(char *) "-/+complete", (char *) "turn on/off complete morphing graph"},
{(char *) "-/+kaleid", (char *) "turn on/off complete kaleidoscope"},
{(char *) "-/+solid", (char *) "turn on/off fills"}
};
ModeSpecOpt qix_opts =
{sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc};
#ifdef USE_MODULES
ModStruct qix_description =
{"qix", "init_qix", "draw_qix", "release_qix",
"refresh_qix", "init_qix", (char *) NULL, &qix_opts,
30000, -5, 32, 1, 64, 1.0, "",
"Shows spinning lines a la Qix(tm)", 0, NULL};
#endif
/* How many segments to draw per cycle when redrawing */
#define REDRAWSTEP 3
#define MINPOINTS 2
/*-
* remember when complete: the number of lines to be drawn is 1+2+3+...+N or
* N(N+1)/2
*/
/* I forget now, did Newton discover this as a boy? */
#define NEWT(n) (n*(n+1)/2)
typedef struct {
int pix;
int first, last;
XPoint *delta, *position;
int npoints;
int offset;
int max_delta;
int width, height, mid, midx, midy;
int nlines, linecount;
int redrawing, redrawpos;
XPoint *lineq, *points;
Bool complete, kaleid, solid;
} qixstruct;
static qixstruct *qixs = (qixstruct *) NULL;
#define check_bounds(qp, val, del, min, max) \
{ \
if ((val) < (min)) { \
*(del) = NRAND((qp)->max_delta) + (qp)->offset; \
} else if ((val) >= (max)) { \
*(del) = -(NRAND((qp)->max_delta)) - (qp)->offset; \
} \
}
static void
free_qix(qixstruct *qp)
{
if (qp->lineq != NULL) {
free(qp->lineq);
qp->lineq = (XPoint *) NULL;
}
if (qp->points != NULL) {
free(qp->points);
qp->points = (XPoint *) NULL;
}
if (qp->delta != NULL) {
free(qp->delta);
qp->delta = (XPoint *) NULL;
}
if (qp->position != NULL) {
free(qp->position);
qp->position = (XPoint *) NULL;
}
}
void
init_qix(ModeInfo * mi)
{
qixstruct *qp;
int i;
if (qixs == NULL) {
if ((qixs = (qixstruct *) calloc(MI_NUM_SCREENS(mi),
sizeof (qixstruct))) == NULL)
return;
}
qp = &qixs[MI_SCREEN(mi)];
qp->width = MI_WIDTH(mi);
qp->height = MI_HEIGHT(mi);
qp->mid = MAX(qp->width, qp->height) / 2;
qp->midx = qp->width / 2;
qp->midy = qp->height / 2;
qp->max_delta = 16;
qp->redrawing = 0;
qp->linecount = 0;
if (qp->width < 100) { /* icon window */
qp->max_delta /= 4;
}
qp->offset = qp->max_delta / 3;
qp->last = 0;
if (MI_NPIXELS(mi) > 2)
qp->pix = NRAND(MI_NPIXELS(mi));
qp->npoints = MI_COUNT(mi);
if (qp->npoints < -MINPOINTS)
qp->npoints = NRAND(qp->npoints - MINPOINTS + 1) + MINPOINTS;
/* Absolute minimum */
if (qp->npoints < MINPOINTS)
qp->npoints = MINPOINTS;
if (MI_IS_FULLRANDOM(mi))
qp->complete = (Bool) (!NRAND(4));
else
qp->complete = complete;
if (qp->complete) {
qp->kaleid = False;
} else {
if (MI_IS_FULLRANDOM(mi))
qp->kaleid = (Bool) (!NRAND(3));
else
qp->kaleid = kaleid;
}
if (qp->complete || qp->kaleid) {
qp->solid = False;
} else {
if (MI_IS_FULLRANDOM(mi))
qp->solid = (Bool) (LRAND() & 1);
else
qp->solid = solid;
}
if (qp->delta)
free(qp->delta);
if (qp->position)
free(qp->position);
if ((qp->delta = (XPoint *) malloc(qp->npoints *
sizeof (XPoint))) == NULL) {
free_qix(qp);
return;
}
if ((qp->position = (XPoint *) malloc(qp->npoints *
sizeof (XPoint))) == NULL) {
free_qix(qp);
return;
}
for (i = 0; i < qp->npoints; i++) {
qp->delta[i].x = NRAND(qp->max_delta) + qp->offset;
qp->delta[i].y = NRAND(qp->max_delta) + qp->offset;
qp->position[i].x = NRAND(qp->width);
qp->position[i].y = NRAND(qp->height);
}
qp->nlines = (qp->npoints == 2) ? (MI_CYCLES(mi) + 1) * 2 :
((MI_CYCLES(mi) + qp->npoints) / qp->npoints) * qp->npoints;
if (qp->complete) {
qp->max_delta /= 4;
qp->nlines = qp->npoints;
}
if (qp->kaleid) {
qp->nlines = (MI_CYCLES(mi) + 1) * 8; /* Fudge it so its compatible */
}
if (qp->lineq) {
free(qp->lineq);
qp->lineq = (XPoint *) NULL;
}
if (qp->points) {
free(qp->points);
qp->points = (XPoint *) NULL;
}
if (!qp->kaleid) {
if ((qp->lineq = (XPoint *) malloc(qp->nlines *
sizeof (XPoint))) == NULL) {
free_qix(qp);
return;
}
for (i = 0; i < qp->nlines; i++)
qp->lineq[i].x = qp->lineq[i].y = -1; /* move initial point off screen */
if (qp->solid) {
if ((qp->points = (XPoint *) malloc(2 * qp->nlines *
sizeof (XPoint))) == NULL) {
free_qix(qp);
return;
}
}
}
MI_CLEARWINDOW(mi);
}
void
draw_qix(ModeInfo * mi)
{
Display *display = MI_DISPLAY(mi);
GC gc = MI_GC(mi);
qixstruct *qp = &qixs[MI_SCREEN(mi)];
int i, j, k, l, prev;
qp->first = (qp->last + qp->npoints) % qp->nlines;
prev = (qp->last - qp->npoints + qp->nlines) % qp->nlines;
MI_IS_DRAWN(mi) = True;
for (i = 0; i < qp->npoints; i++) {
qp->position[i].x += qp->delta[i].x;
qp->position[i].y += qp->delta[i].y;
if (NRAND(20) < 1) {
check_bounds(qp, qp->position[i].x, &qp->delta[i].x,
qp->width / 3, 2 * qp->width / 3);
} else {
check_bounds(qp, qp->position[i].x, &qp->delta[i].x, 0, qp->width);
}
if (NRAND(20) < 1) {
check_bounds(qp, qp->position[i].y, &qp->delta[i].y,
qp->height / 3, 2 * qp->height / 3);
} else {
check_bounds(qp, qp->position[i].y, &qp->delta[i].y, 0, qp->height);
}
}
XSetForeground(display, gc, MI_BLACK_PIXEL(mi));
if (qp->complete && qp->npoints > 3) {
for (i = 0; i < qp->npoints - 1; i++)
for (j = i + 1; j < qp->npoints; j++) {
XDrawLine(display, MI_WINDOW(mi), gc,
qp->lineq[qp->first + i].x,
qp->lineq[qp->first + i].y,
qp->lineq[qp->first + j].x,
qp->lineq[qp->first + j].y);
}
} else if (!qp->kaleid) {
if (qp->solid) {
if (qp->lineq[qp->last].x != -1 ||
qp->lineq[qp->last].y != -1) {
for (i = 0; i < qp->npoints; i++) {
qp->points[i] = qp->lineq[qp->last + i];
qp->points[i + qp->npoints] =
qp->lineq[qp->first + qp->npoints - 1 - i];
}
XFillPolygon(display, MI_WINDOW(mi), gc,
qp->points, 2 * qp->npoints,
Complex, CoordModeOrigin);
}
} else {
for (i = 1; i < qp->npoints +
((qp->npoints == 2) ? -1 : 0); i++)
XDrawLine(display, MI_WINDOW(mi), gc,
qp->lineq[qp->first + i - 1].x,
qp->lineq[qp->first + i - 1].y,
qp->lineq[qp->first + i].x,
qp->lineq[qp->first + i].y);
XDrawLine(display, MI_WINDOW(mi), gc,
qp->lineq[qp->first].x, qp->lineq[qp->first].y,
qp->lineq[qp->first + qp->npoints - 1].x,
qp->lineq[qp->first + qp->npoints - 1].y);
}
}
if (MI_NPIXELS(mi) > 2) {
XSetForeground(display, gc, MI_PIXEL(mi, qp->pix));
if (++qp->pix >= MI_NPIXELS(mi))
qp->pix = 0;
} else
XSetForeground(display, gc, MI_WHITE_PIXEL(mi));
if (qp->complete && qp->npoints > 3) {
for (i = 0; i < qp->npoints - 1; i++)
for (j = i + 1; j < qp->npoints; j++)
XDrawLine(display, MI_WINDOW(mi), gc,
qp->position[i].x, qp->position[i].y,
qp->position[j].x, qp->position[j].y);
} else if (qp->kaleid) {
for (i = 0; i < qp->npoints; i++) {
XSegment segs[8];
XPoint small[2];
j = (i + 1) % qp->npoints;
small[0].x = ABS(qp->position[i].x - qp->mid);
small[0].y = ABS(qp->position[i].y - qp->mid);
small[1].x = ABS(qp->position[j].x - qp->mid);
small[1].y = ABS(qp->position[j].y - qp->mid);
segs[0].x1 = qp->midx + small[0].x;
segs[0].y1 = qp->midy + small[0].y;
segs[0].x2 = qp->midx + small[1].x;
segs[0].y2 = qp->midy + small[1].y;
segs[1].x1 = qp->midx - small[0].x;
segs[1].y1 = qp->midy + small[0].y;
segs[1].x2 = qp->midx - small[1].x;
segs[1].y2 = qp->midy + small[1].y;
segs[2].x1 = qp->midx - small[0].x;
segs[2].y1 = qp->midy - small[0].y;
segs[2].x2 = qp->midx - small[1].x;
segs[2].y2 = qp->midy - small[1].y;
segs[3].x1 = qp->midx + small[0].x;
segs[3].y1 = qp->midy - small[0].y;
segs[3].x2 = qp->midx + small[1].x;
segs[3].y2 = qp->midy - small[1].y;
segs[4].x1 = qp->midx + small[0].y;
segs[4].y1 = qp->midy + small[0].x;
segs[4].x2 = qp->midx + small[1].y;
segs[4].y2 = qp->midy + small[1].x;
segs[5].x1 = qp->midx + small[0].y;
segs[5].y1 = qp->midy - small[0].x;
segs[5].x2 = qp->midx + small[1].y;
segs[5].y2 = qp->midy - small[1].x;
segs[6].x1 = qp->midx - small[0].y;
segs[6].y1 = qp->midy - small[0].x;
segs[6].x2 = qp->midx - small[1].y;
segs[6].y2 = qp->midy - small[1].x;
segs[7].x1 = qp->midx - small[0].y;
segs[7].y1 = qp->midy + small[0].x;
segs[7].x2 = qp->midx - small[1].y;
segs[7].y2 = qp->midy + small[1].x;
XDrawSegments(display, MI_WINDOW(mi), gc, segs, 8);
if (qp->npoints == 2) /* do not repeat drawing the same line */
break;
}
if (++qp->linecount >= qp->nlines) {
qp->linecount = 0;
MI_CLEARWINDOW(mi);
}
} else {
if (qp->solid) {
if (qp->lineq[prev].x != -1 ||
qp->lineq[prev].y != -1) {
for (i = 0; i < qp->npoints; i++) {
qp->points[i] = qp->lineq[prev + i];
qp->points[i + qp->npoints] =
qp->position[qp->npoints - 1 - i];
}
XFillPolygon(display, MI_WINDOW(mi), gc,
qp->points, 2 * qp->npoints,
Complex, CoordModeOrigin);
}
} else {
for (i = 1; i < qp->npoints + ((qp->npoints == 2) ? -1 : 0); i++)
XDrawLine(display, MI_WINDOW(mi), gc,
qp->position[i - 1].x,
qp->position[i - 1].y,
qp->position[i].x,
qp->position[i].y);
XDrawLine(display, MI_WINDOW(mi), gc,
qp->position[0].x, qp->position[0].y,
qp->position[qp->npoints - 1].x,
qp->position[qp->npoints - 1].y);
}
}
if (!qp->kaleid)
for (i = 0; i < qp->npoints; i++) {
qp->lineq[qp->last].x = qp->position[i].x;
qp->lineq[qp->last].y = qp->position[i].y;
qp->last++;
if (qp->last >= qp->nlines)
qp->last = 0;
}
if (qp->redrawing) {
for (i = 0; i < REDRAWSTEP; i++) {
int jold = (qp->last - qp->redrawpos + qp->nlines - qp->npoints) % qp->nlines;
j = (qp->first - qp->redrawpos + qp->nlines - qp->npoints) % qp->nlines;
if (qp->complete && qp->npoints > 3) {
for (k = 0; k < qp->npoints - 1; k++)
for (l = k + 1; l < qp->npoints; l++)
XDrawLine(display, MI_WINDOW(mi), gc,
qp->lineq[j + k].x,
qp->lineq[j + k].y,
qp->lineq[j + l].x,
qp->lineq[j + l].y);
} else if (!qp->kaleid) {
if (qp->solid) {
if (qp->lineq[jold].x != -1 ||
qp->lineq[jold].y != -1) {
for (i = 0; i < qp->npoints; i++) {
qp->points[i] = qp->lineq[jold + i];
qp->points[i + qp->npoints] =
qp->lineq[j + qp->npoints - 1 - i];
}
XFillPolygon(display, MI_WINDOW(mi), gc,
qp->points, 2 * qp->npoints,
Complex, CoordModeOrigin);
}
} else {
for (k = 1; k < qp->npoints +
((qp->npoints == 2) ? -1 : 0); k++)
XDrawLine(display, MI_WINDOW(mi), gc,
qp->lineq[j + k - 1].x,
qp->lineq[j + k - 1].y,
qp->lineq[j + k].x,
qp->lineq[j + k].y);
XDrawLine(display, MI_WINDOW(mi), gc,
qp->lineq[j].x, qp->lineq[j].y,
qp->lineq[j + qp->npoints - 1].x,
qp->lineq[j + qp->npoints - 1].y);
}
}
qp->redrawpos += qp->npoints;
if (qp->redrawpos >= qp->nlines - qp->npoints) {
qp->redrawing = 0;
break;
}
}
}
}
void
release_qix(ModeInfo * mi)
{
if (qixs != NULL) {
int screen;
for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
free_qix(&qixs[screen]);
free(qixs);
qixs = (qixstruct *) NULL;
}
}
void
refresh_qix(ModeInfo * mi)
{
qixstruct *qp = &qixs[MI_SCREEN(mi)];
MI_CLEARWINDOW(mi);
qp->redrawing = 1;
qp->redrawpos = 0;
}
#endif /* MODE_qix */