/* -*- 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 */