/* -*- Mode: C; tab-width: 4 -*- */ /* helix --- string art */ #if !defined( lint ) && !defined( SABER ) static const char sccsid[] = "@(#)helix.c 5.00 2000/11/01 xlockmore"; #endif /*- * Copyright (c) 1992 by Jamie Zawinski * * 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 * 06-Apr-1997: new ellipse code from Dan Stromberg * 11-Aug-1995: found some typos, looks more interesting now * 08-Aug-1995: speed up thanks to Heath A. Kehoe * 17-Jun-1995: removed sleep statements * 02-Sep-1993: xlock version David Bagley * 1992: xscreensaver version Jamie Zawinski */ /*- * original copyright * Copyright (c) 1992 by Jamie Zawinski * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided * that the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. */ #ifdef STANDALONE #define MODE_helix #define PROGCLASS "Helix" #define HACK_INIT init_helix #define HACK_DRAW draw_helix #define helix_opts xlockmore_opts #define DEFAULTS "*delay: 25000 \n" \ "*cycles: 100 \n" \ "*ncolors: 200 \n" \ "*fullrandom: True \n" #define BRIGHT_COLORS #include "xlockmore.h" /* in xscreensaver distribution */ #else /* STANDALONE */ #include "xlock.h" /* in xlockmore distribution */ #endif /* STANDALONE */ #ifdef MODE_helix #define DEF_ELLIPSE "False" static Bool ellipse; static XrmOptionDescRec opts[] = { {(char *) "-ellipse", (char *) ".helix.ellipse", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+ellipse", (char *) ".helix.ellipse", XrmoptionNoArg, (caddr_t) "off"} }; static argtype vars[] = { {(void *) & ellipse, (char *) "ellipse", (char *) "Ellipse", (char *) DEF_ELLIPSE, t_Bool} }; static OptionStruct desc[] = { {(char *) "-/+ellipse", (char *) "turn on/off ellipse format"} }; ModeSpecOpt helix_opts = {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc}; #ifdef USE_MODULES ModStruct helix_description = {"helix", "init_helix", "draw_helix", "release_helix", "refresh_helix", "init_helix", (char *) NULL, &helix_opts, 25000, 1, 100, 1, 64, 1.0, "", "Shows string art", 0, NULL}; #endif #define ANGLES 360 static double cos_array[ANGLES], sin_array[ANGLES]; typedef struct { Bool painted; int width, height; int xmid, ymid; int color; int time; int radius1, radius2, d_angle, factor1, factor2, factor3, factor4; int redraw; Bool ellipse; int d_angle_offset, dir; int offset; int density; int count; } helixstruct; static helixstruct *helixes = (helixstruct *) NULL; static int gcd(int a, int b) { while (b > 0) { int tmp; tmp = a % b; a = b; b = tmp; } return (a < 0 ? -a : a); } static void helix(ModeInfo * mi, int radius1, int radius2, int d_angle, int factor1, int factor2, int factor3, int factor4) { Display *display = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); GC gc = MI_GC(mi); helixstruct *hp = &helixes[MI_SCREEN(mi)]; int x_1, y_1, x_2, y_2, angle, limit; int i; if (MI_NPIXELS(mi) > 2) { XSetForeground(display, gc, MI_PIXEL(mi, hp->color)); if (++hp->color >= MI_NPIXELS(mi)) hp->color = 0; } else XSetForeground(display, gc, MI_WHITE_PIXEL(mi)); x_2 = hp->xmid; y_2 = hp->ymid + radius1; angle = 0; limit = 1 + (ANGLES / gcd(ANGLES, d_angle)); for (i = 0; i < limit; i++) { int tmp; #define pmod(x,y) (tmp=((x)%(y)),(tmp>=0?tmp:tmp+y)) x_1 = hp->xmid + (int) (((double) radius1) * sin_array[pmod((angle * factor1), ANGLES)]); y_1 = hp->ymid + (int) (((double) radius2) * cos_array[pmod((angle * factor2), ANGLES)]); if (MI_NPIXELS(mi) > 2) { XSetForeground(display, gc, MI_PIXEL(mi, hp->color)); if (++hp->color >= MI_NPIXELS(mi)) hp->color = 0; } XDrawLine(display, window, gc, x_1, y_1, x_2, y_2); x_2 = hp->xmid + (int) (((double) radius2) * sin_array[pmod((angle * factor3), ANGLES)]); y_2 = hp->ymid + (int) (((double) radius1) * cos_array[pmod((angle * factor4), ANGLES)]); if (MI_NPIXELS(mi) > 2) { XSetForeground(display, gc, MI_PIXEL(mi, hp->color)); if (++hp->color >= MI_NPIXELS(mi)) hp->color = 0; } XDrawLine(display, window, gc, x_1, y_1, x_2, y_2); angle += d_angle; } } static void trig(ModeInfo * mi) { Display *display = MI_DISPLAY(mi); GC gc = MI_GC(mi); helixstruct *hp = &helixes[MI_SCREEN(mi)]; int x_1, y_1, x_2, y_2; int tmp, angle; #define pmod(x,y) (tmp=((x)%(y)),(tmp>=0?tmp:tmp+y)) while (ABS(hp->d_angle) <= ANGLES) { angle = hp->d_angle + hp->d_angle_offset; x_1 = (int) (sin_array[pmod(angle * hp->factor1, ANGLES)] * hp->xmid) + hp->xmid; y_1 = (int) (cos_array[pmod(angle * hp->factor1, ANGLES)] * hp->ymid) + hp->ymid; x_2 = (int) (sin_array[pmod(angle * hp->factor2 + hp->offset, ANGLES)] * hp->xmid) + hp->xmid; y_2 = (int) (cos_array[pmod(angle * hp->factor2 + hp->offset, ANGLES)] * hp->ymid) + hp->ymid; XDrawLine(display, MI_WINDOW(mi), gc, x_1, y_1, x_2, y_2); if (MI_NPIXELS(mi) > 2) { XSetForeground(display, gc, MI_PIXEL(mi, hp->color)); if (++hp->color >= MI_NPIXELS(mi)) hp->color = 0; } tmp = (int) ANGLES / (2 * hp->density * hp->factor1 * hp->factor2); if (tmp == 0) /* Do not want it getting stuck... */ tmp = 1; /* Would not need if floating point */ hp->d_angle += hp->dir * tmp; } } static void random_helix(ModeInfo * mi) { helixstruct *hp = &helixes[MI_SCREEN(mi)]; int radius; double divisor; radius = MIN(hp->xmid, hp->ymid); hp->d_angle = 0; hp->factor1 = 2; hp->factor2 = 2; hp->factor3 = 2; hp->factor4 = 2; divisor = ((LRAND() / MAXRAND * 3.0 + 1) * (((LRAND() & 1) * 2) - 1)); if ((LRAND() & 1) == 0) { hp->radius1 = radius; hp->radius2 = (int) ((double) radius / divisor); } else { hp->radius2 = radius; hp->radius1 = (int) ((double) radius / divisor); } while (gcd(ANGLES, hp->d_angle) >= 2) hp->d_angle = NRAND(ANGLES); #define random_factor() \ (int) (((NRAND(7)) ? ((LRAND() & 1) + 1) : 3) * (((LRAND() & 1) * 2) - 1)) while (gcd(gcd(gcd(hp->factor1, hp->factor2), hp->factor3), hp->factor4) != 1) { hp->factor1 = random_factor(); hp->factor2 = random_factor(); hp->factor3 = random_factor(); hp->factor4 = random_factor(); } helix(mi, hp->radius1, hp->radius2, hp->d_angle, hp->factor1, hp->factor2, hp->factor3, hp->factor4); } static void random_trig(ModeInfo * mi) { Display *display = MI_DISPLAY(mi); GC gc = MI_GC(mi); helixstruct *hp = &helixes[MI_SCREEN(mi)]; hp->d_angle = 0; hp->factor1 = NRAND(8) + 1; do hp->factor2 = NRAND(8) + 1; while (hp->factor1 == hp->factor2); hp->dir = (LRAND() & 1) ? 1 : -1; hp->d_angle_offset = NRAND(ANGLES); hp->offset = (NRAND(ANGLES / 4 - 1) + 1) / 4; hp->density = 1 << (NRAND(4) + 4); /* Higher density, higher ANGLES */ if (MI_NPIXELS(mi) > 2) { XSetForeground(display, gc, MI_PIXEL(mi, hp->color)); if (++hp->color >= MI_NPIXELS(mi)) hp->color = 0; } else XSetForeground(display, gc, MI_WHITE_PIXEL(mi)); trig(mi); } void init_helix(ModeInfo * mi) { int i; static int first = 1; helixstruct *hp; if (helixes == NULL) { if ((helixes = (helixstruct *) calloc(MI_NUM_SCREENS(mi), sizeof (helixstruct))) == NULL) return; } hp = &helixes[MI_SCREEN(mi)]; if (first) { first = 0; for (i = 0; i < ANGLES; i++) { cos_array[i] = cos((((double) i) / (double) (ANGLES / 2)) * M_PI); sin_array[i] = sin((((double) i) / (double) (ANGLES / 2)) * M_PI);; } } hp->ellipse = ellipse; if (MI_IS_FULLRANDOM(mi)) hp->ellipse = (Bool) (!NRAND(5)); /* 1:5 chance of running ellipse stuff */ hp->width = MI_WIDTH(mi); hp->height = MI_HEIGHT(mi); hp->xmid = hp->width / 2; hp->ymid = hp->height / 2; hp->redraw = 0; MI_CLEARWINDOW(mi); hp->painted = False; if (MI_NPIXELS(mi) > 2) hp->color = NRAND(MI_NPIXELS(mi)); hp->time = 0; if (hp->ellipse) { random_trig(mi); } else random_helix(mi); } void draw_helix(ModeInfo * mi) { helixstruct *hp; if (helixes == NULL) return; hp = &helixes[MI_SCREEN(mi)]; MI_IS_DRAWN(mi) = True; if (++hp->time > MI_CYCLES(mi)) init_helix(mi); else hp->painted = True; if (hp->redraw) { if (hp->ellipse) { trig(mi); } else { helix(mi, hp->radius1, hp->radius2, hp->d_angle, hp->factor1, hp->factor2, hp->factor3, hp->factor4); } hp->redraw = 0; } } void release_helix(ModeInfo * mi) { if (helixes != NULL) { free(helixes); helixes = (helixstruct *) NULL; } } void refresh_helix(ModeInfo * mi) { helixstruct *hp; if (helixes == NULL) return; hp = &helixes[MI_SCREEN(mi)]; if (hp->painted) { MI_CLEARWINDOW(mi); helix(mi, hp->radius1, hp->radius2, hp->d_angle, hp->factor1, hp->factor2, hp->factor3, hp->factor4); hp->painted = False; } } #endif /* MODE_helix */