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