/* -*- Mode: C; tab-width: 4 -*- */ /* kaleid --- Brewster's Kaleidoscope */ #if !defined( lint ) && !defined( SABER ) static const char sccsid[] = "@(#)kaleid.c 5.00 2000/11/01 xlockmore"; #endif /*- * * kaleid.c - Brewster's Kaleidoscope (Sir David Brewster invented the * kaleidoscope in 1816 and patented it in 1817.) * * Copyright (c) 1998 by Robert Adam, II * * 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 * 1998: Written * * * -batchcount n number of pens [default 4] * * -cycle n percentage of black in the pattern (0%-95%) * * -size n symmetry mode [default -9] * <0 = random from 0 to -number * >0 = mirrored (-alternate) * rotated (+alternate) * * * -/+disconnected turn on/off disconnected pen movement * * -/+serial turn on/off sequential allocation of colors * * -/+alternate turn on/off alternate display mode * * -/+spiral turn on/off spiral mode * * -/+spots turn on/off spots mode * * -/+quad turn on/off quad mirrored/rotated mode similar * to size 4, works with alternate * * -/+oct turn on/off oct mirrored/rotated moded similar * to size 8, works with alternate * * -/+linear select Cartesian/Polar coordinate mode. Cartesian * uses straight lines similar to -oct and -quad * mode instead of the curved lines of Polar mode. * [default off] * */ #ifdef STANDALONE #define MODE_kaleid #define PROGCLASS "Kaleid" #define HACK_INIT init_kaleid #define HACK_DRAW draw_kaleid #define kaleid_opts xlockmore_opts #define DEFAULTS "*delay: 80000 \n" \ "*count: 4 \n" \ "*cycles: 40 \n" \ "*size: -9 \n" \ "*ncolors: 200 \n" \ "*disconnected: True \n" \ "*serial: False \n" \ "*alternate: False \n" \ "*spiral: False \n" \ "*spots: False \n" \ "*linear: False \n" \ "*quad: False \n" \ "*oct: False \n" "*fullrandom: False \n" #define UNIFORM_COLORS #define BRIGHT_COLORS #include "xlockmore.h" /* in xscreensaver distribution */ #else /* STANDALONE */ #include "xlock.h" /* in xlockmore distribution */ #endif /* STANDALONE */ #ifdef MODE_kaleid #include #define DEF_DISCONNECTED "True" #define DEF_SERIAL "False" #define DEF_ALTERNATE "False" #define DEF_QUAD "False" #define DEF_OCT "False" #define DEF_LINEAR "False" #define DEF_SPIRAL "False" #define DEF_SPOTS "False" static Bool Disconnected; static Bool Serial; static Bool Alternate; static Bool Quad; static Bool Oct; static Bool Linear; static Bool Spiral; static Bool Spots; static XrmOptionDescRec opts[] = { {(char *) "-disconnected", (char *) ".kaleid.disconnected", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+disconnected", (char *) ".kaleid.disconnected", XrmoptionNoArg, (caddr_t) "off"}, {(char *) "-serial", (char *) ".kaleid.serial", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+serial", (char *) ".kaleid.serial", XrmoptionNoArg, (caddr_t) "off"}, {(char *) "-alternate", (char *) ".kaleid.alternate", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+alternate", (char *) ".kaleid.alternate", XrmoptionNoArg, (caddr_t) "off"}, {(char *) "-spiral", (char *) ".kaleid.spiral", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+spiral", (char *) ".kaleid.spiral", XrmoptionNoArg, (caddr_t) "off"}, {(char *) "-spots", (char *) ".kaleid.spots", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+spots", (char *) ".kaleid.spots", XrmoptionNoArg, (caddr_t) "off"}, {(char *) "-quad", (char *) ".kaleid.quad", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+quad", (char *) ".kaleid.quad", XrmoptionNoArg, (caddr_t) "off"}, {(char *) "-oct", (char *) ".kaleid.oct", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+oct", (char *) ".kaleid.oct", XrmoptionNoArg, (caddr_t) "off"}, {(char *) "-linear", (char *) ".kaleid.linear", XrmoptionNoArg, (caddr_t) "on"}, {(char *) "+linear", (char *) ".kaleid.linear", XrmoptionNoArg, (caddr_t) "off"} }; static argtype vars[] = { {(void *) & Disconnected, (char *) "disconnected", (char *) "Disconnected", (char *) DEF_DISCONNECTED, t_Bool}, {(void *) & Serial, (char *) "serial", (char *) "Serial", (char *) DEF_SERIAL, t_Bool}, {(void *) & Alternate, (char *) "alternate", (char *) "Alternate", (char *) DEF_ALTERNATE, t_Bool}, {(void *) & Spiral, (char *) "spiral", (char *) "Spiral", (char *) DEF_SPIRAL, t_Bool}, {(void *) & Spots, (char *) "spots", (char *) "Spots", (char *) DEF_SPOTS, t_Bool}, {(void *) & Quad, (char *) "quad", (char *) "Quad", (char *) DEF_QUAD, t_Bool}, {(void *) & Oct, (char *) "oct", (char *) "Oct", (char *) DEF_OCT, t_Bool}, {(void *) & Linear, (char *) "linear", (char *) "Linear", (char *) DEF_LINEAR, t_Bool} }; static OptionStruct desc[] = { {(char *) "-/+disconnected", (char *) "turn on/off disconnected pen movement"}, {(char *) "-/+serial", (char *) "turn on/off sequential color selection"}, {(char *) "-/+alternate", (char *) "turn on/off alternate display mode"}, {(char *) "-/+spiral", (char *) "turn on/off angular bounding mode"}, {(char *) "-/+spots", (char *) "turn on/off circle mode"}, {(char *) "-/+quad", (char *) "turn on/off quad mirrored display mode"}, {(char *) "-/+oct", (char *) "turn on/off oct mirrored display mode"}, {(char *) "-/+linear", (char *) "select Cartesian/Polar coordinate display mode"} }; ModeSpecOpt kaleid_opts = {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc}; #ifdef USE_MODULES ModStruct kaleid_description = { "kaleid", "init_kaleid", "draw_kaleid", "release_kaleid", "refresh_kaleid", "init_kaleid", (char *) NULL, &kaleid_opts, 80000, 4, 40, -9, 64, 0.6, "", "Shows Brewster's Kaleidoscope", 0, NULL }; #endif #define INTRAND(min,max) (NRAND(((max+1)-(min)))+(min)) #define MINPENS 1 #define MINSIZE 1 #define QUAD (-4) #define OCT (-8) #define SpotsMult 3 #define MinVelocity 6 #define MaxVelocity 4 #define MinRadialVelocity 6 #define MaxRadialVelocity 30 #define MinAngularVelocity 5 #define MaxAngularVelocity 3 #define WidthPercent 25 #define ChangeChance 2 #define ToRadians 0.017453293 #define ToDegrees 57.29577951 #define PolarToCartX(r, t) ((r) * cos((t) * ToRadians)) #define PolarToCartY(r, t) ((r) * sin((t) * ToRadians)) #define CartToRadius(x, y) (sqrt(((x)*(x)) + ((y)*(y)))) #define CartToAngle(x,y) ((((x) == 0.0) && ((y) == 0.0)) ? 0.0 : (atan2((y),(x)) * ToDegrees)) typedef struct { double cx; double cy; double ox; double oy; double xv; double yv; int curlwidth; int pix; int White; Bool RadiusOut; Bool AngleOut; Bool DeferredChange; } penstruct; typedef struct { penstruct *pen; int PercentBlack; int PenCount; int maxlwidth; double width, widthby2; double height, heightby2; double radius; double slice; int bouncetype; int modetype; Bool alternate, disconnected, serial, linear, spiral, spots; } kaleidstruct; static kaleidstruct *kaleids = (kaleidstruct *) NULL; /*- * */ static void QuadMirrored( kaleidstruct * kp, int pn, XSegment segs[4] ) { kp->pen[pn].cx = kp->pen[pn].cx + kp->pen[pn].xv; if (kp->pen[pn].cx < 0.0) { kp->pen[pn].cx = -kp->pen[pn].cx; kp->pen[pn].xv = -kp->pen[pn].xv; } else if (kp->pen[pn].cx >= kp->widthby2) { kp->pen[pn].cx = (kp->widthby2 - 1.0) - (kp->pen[pn].cx - kp->widthby2); kp->pen[pn].xv = -kp->pen[pn].xv; } kp->pen[pn].cy = kp->pen[pn].cy + kp->pen[pn].yv; if (kp->pen[pn].cy < 0.0) { kp->pen[pn].cy = -kp->pen[pn].cy; kp->pen[pn].yv = -kp->pen[pn].yv; } else if (kp->pen[pn].cy >= kp->heightby2) { kp->pen[pn].cy = (kp->heightby2 - 1.0) - (kp->pen[pn].cy - kp->heightby2); kp->pen[pn].yv = -kp->pen[pn].yv; } segs[0].x1 = (int) kp->pen[pn].ox; segs[0].y1 = (int) kp->pen[pn].oy; segs[0].x2 = (int) kp->pen[pn].cx; segs[0].y2 = (int) kp->pen[pn].cy; segs[1].x1 = (int) (kp->width - kp->pen[pn].ox); segs[1].y1 = (int) kp->pen[pn].oy; segs[1].x2 = (int) (kp->width - kp->pen[pn].cx); segs[1].y2 = (int) kp->pen[pn].cy; segs[2].x1 = (int) (kp->width - kp->pen[pn].ox); segs[2].y1 = (int) (kp->height - kp->pen[pn].oy); segs[2].x2 = (int) (kp->width - kp->pen[pn].cx); segs[2].y2 = (int) (kp->height - kp->pen[pn].cy); segs[3].x1 = (int) kp->pen[pn].ox; segs[3].y1 = (int) (kp->height - kp->pen[pn].oy); segs[3].x2 = (int) kp->pen[pn].cx; segs[3].y2 = (int) (kp->height - kp->pen[pn].cy); } /*- * */ static void QuadRotated( kaleidstruct * kp, int pn, XSegment segs[4] ) { double oxscaled2y; double oyscaled2x; double cxscaled2y; double cyscaled2x; kp->pen[pn].cx = kp->pen[pn].cx + kp->pen[pn].xv; if (kp->pen[pn].cx < 0.0) { kp->pen[pn].cx = -kp->pen[pn].cx; kp->pen[pn].xv = -kp->pen[pn].xv; } else if (kp->pen[pn].cx >= kp->widthby2) { kp->pen[pn].cx = (kp->widthby2 - 1.0) - (kp->pen[pn].cx - kp->widthby2); kp->pen[pn].xv = -kp->pen[pn].xv; } kp->pen[pn].cy = kp->pen[pn].cy + kp->pen[pn].yv; if (kp->pen[pn].cy < 0.0) { kp->pen[pn].cy = -kp->pen[pn].cy; kp->pen[pn].yv = -kp->pen[pn].yv; } else if (kp->pen[pn].cy >= kp->heightby2) { kp->pen[pn].cy = (kp->heightby2 - 1.0) - (kp->pen[pn].cy - kp->heightby2); kp->pen[pn].yv = -kp->pen[pn].yv; } segs[0].x1 = (int) kp->pen[pn].ox; segs[0].y1 = (int) kp->pen[pn].oy; segs[0].x2 = (int) kp->pen[pn].cx; segs[0].y2 = (int) kp->pen[pn].cy; oxscaled2y = ((kp->pen[pn].ox * kp->heightby2) / kp->widthby2); oyscaled2x = ((kp->pen[pn].oy * kp->widthby2) / kp->heightby2); cxscaled2y = ((kp->pen[pn].cx * kp->heightby2) / kp->widthby2); cyscaled2x = ((kp->pen[pn].cy * kp->widthby2) / kp->heightby2); segs[1].x1 = (int) (kp->width - oyscaled2x); segs[1].y1 = (int) oxscaled2y; segs[1].x2 = (int) (kp->width - cyscaled2x); segs[1].y2 = (int) cxscaled2y; segs[2].x1 = (int) (kp->width - kp->pen[pn].ox); segs[2].y1 = (int) (kp->height - kp->pen[pn].oy); segs[2].x2 = (int) (kp->width - kp->pen[pn].cx); segs[2].y2 = (int) (kp->height - kp->pen[pn].cy); segs[3].x1 = (int) oyscaled2x; segs[3].y1 = (int) (kp->height - oxscaled2y); segs[3].x2 = (int) cyscaled2x; segs[3].y2 = (int) (kp->height - cxscaled2y); } /*- * */ static void GeneralPolarMoveAndBounce( kaleidstruct * kp, int pn ) { kp->pen[pn].cx = kp->pen[pn].cx + kp->pen[pn].xv; if (kp->pen[pn].cx < 0.0) { kp->pen[pn].cx = -kp->pen[pn].cx; kp->pen[pn].xv = -kp->pen[pn].xv; } else if (kp->pen[pn].cx >= kp->radius) { kp->pen[pn].cx = (kp->radius - 1.0) - (kp->pen[pn].cx - kp->radius); kp->pen[pn].xv = -kp->pen[pn].xv; } switch (kp->bouncetype) { case 0: { kp->pen[pn].cy = kp->pen[pn].cy + kp->pen[pn].yv; break; } case 1: { kp->pen[pn].cy = kp->pen[pn].cy + ((kp->pen[pn].yv * kp->pen[pn].cx) / kp->radius); break; } case 2: { kp->pen[pn].cy = kp->pen[pn].cy + ((kp->pen[pn].yv * (kp->radius - kp->pen[pn].cx)) / kp->radius); break; } default: { kp->pen[pn].cy = kp->slice / 2.0; kp->pen[pn].cx = kp->radius / 2.0; break; } } if (kp->pen[pn].cy < 0) { if (kp->spiral) { kp->pen[pn].RadiusOut = False; kp->pen[pn].cy = kp->pen[pn].cy + 360.0; } else { kp->pen[pn].RadiusOut = True; if (kp->pen[pn].oy >= 0) { kp->pen[pn].yv = -kp->pen[pn].yv; } } } else if (kp->spiral) { if (kp->pen[pn].cy > 360.0) { kp->pen[pn].RadiusOut = False; kp->pen[pn].cy = kp->pen[pn].cy - 360.0; } } else if (kp->pen[pn].cy >= kp->slice) { kp->pen[pn].RadiusOut = True; if (kp->pen[pn].oy < kp->slice) { kp->pen[pn].yv = -kp->pen[pn].yv; } } else { kp->pen[pn].RadiusOut = False; } } /*- * */ static void GeneralRotated( kaleidstruct * kp, int pn, XSegment * segs ) { double Angle; int segnum; GeneralPolarMoveAndBounce(kp, pn); Angle = 0.0; for (segnum = 0; segnum < kp->modetype; segnum += 1) { if (! kp->spots) { segs[segnum].x1 = (int) (PolarToCartX(kp->pen[pn].ox, kp->pen[pn].oy + Angle ) + kp->widthby2 ); segs[segnum].y1 = (int) (PolarToCartY(kp->pen[pn].ox, kp->pen[pn].oy + Angle ) + kp->heightby2 ); } segs[segnum].x2 = (int) (PolarToCartX(kp->pen[pn].cx, kp->pen[pn].cy + Angle ) + kp->widthby2 ); segs[segnum].y2 = (int) (PolarToCartY(kp->pen[pn].cx, kp->pen[pn].cy + Angle ) + kp->heightby2 ); if (kp->spots) { segs[segnum].x1 = segs[segnum].x2; segs[segnum].y1 = segs[segnum].y2; } Angle += kp->slice; } } /*- * */ static void GeneralMirrored( kaleidstruct * kp, int pn, XSegment * segs ) { double Angle; int segnum; GeneralPolarMoveAndBounce(kp, pn); Angle = 0.0; for (segnum = 0; segnum < kp->modetype; segnum += 2) { if (! kp->spots) { segs[segnum].x1 = (int) (PolarToCartX(kp->pen[pn].ox, kp->pen[pn].oy + Angle ) + kp->widthby2 ); segs[segnum].y1 = (int) (PolarToCartY(kp->pen[pn].ox, kp->pen[pn].oy + Angle ) + kp->heightby2 ); } segs[segnum].x2 = (int) (PolarToCartX(kp->pen[pn].cx, kp->pen[pn].cy + Angle ) + kp->widthby2 ); segs[segnum].y2 = (int) (PolarToCartY(kp->pen[pn].cx, kp->pen[pn].cy + Angle ) + kp->heightby2 ); if (kp->spots) { segs[segnum].x1 = segs[segnum].x2; segs[segnum].y1 = segs[segnum].y2; } Angle += (2.0 * kp->slice); } Angle = 360.0 - kp->slice; for (segnum = 1; segnum < kp->modetype; segnum += 2) { if (! kp->spots) { segs[segnum].x1 = (int) (PolarToCartX(kp->pen[pn].ox, (kp->slice - kp->pen[pn].oy) + Angle ) + kp->widthby2 ); segs[segnum].y1 = (int) (PolarToCartY(kp->pen[pn].ox, (kp->slice - kp->pen[pn].oy) + Angle ) + kp->heightby2 ); } segs[segnum].x2 = (int) (PolarToCartX(kp->pen[pn].cx, (kp->slice - kp->pen[pn].cy) + Angle ) + kp->widthby2 ); segs[segnum].y2 = (int) (PolarToCartY(kp->pen[pn].cx, (kp->slice - kp->pen[pn].cy) + Angle ) + kp->heightby2 ); if (kp->spots) { segs[segnum].x1 = segs[segnum].x2; segs[segnum].y1 = segs[segnum].y2; } Angle -= (2.0 * kp->slice); } } /*- * */ static void OctMirrored( kaleidstruct * kp, int pn, XSegment * segs ) { double xdiag; double ydiag; double oxscaled2y, cxscaled2y; double oyscaled2x, cyscaled2x; /* * I know that the "bounce" is not really accurate, but I like the way * it looks. */ xdiag = (kp->widthby2 * kp->pen[pn].oy) / kp->heightby2; kp->pen[pn].cx = kp->pen[pn].cx + kp->pen[pn].xv; if (kp->pen[pn].cx < xdiag) { kp->pen[pn].cx = xdiag + (xdiag - kp->pen[pn].cx); kp->pen[pn].xv = -kp->pen[pn].xv; } else if (kp->pen[pn].cx >= kp->widthby2) { kp->pen[pn].cx = (kp->widthby2 - 1.0) - (kp->pen[pn].cx - kp->widthby2); kp->pen[pn].xv = -kp->pen[pn].xv; } ydiag = (kp->heightby2 * kp->pen[pn].cx) / kp->widthby2; kp->pen[pn].cy = kp->pen[pn].cy + kp->pen[pn].yv; if (kp->pen[pn].cy < 0.0) { kp->pen[pn].cy = -kp->pen[pn].cy; kp->pen[pn].yv = -kp->pen[pn].yv; } else if (kp->pen[pn].cy > ydiag) { kp->pen[pn].cy = ydiag - (kp->pen[pn].cy - ydiag); kp->pen[pn].yv = -kp->pen[pn].yv; } segs[0].x1 = (int) kp->pen[pn].ox; segs[0].y1 = (int) kp->pen[pn].oy; segs[0].x2 = (int) kp->pen[pn].cx; segs[0].y2 = (int) kp->pen[pn].cy; segs[1].x1 = (int) (kp->width - kp->pen[pn].ox); segs[1].y1 = (int) kp->pen[pn].oy; segs[1].x2 = (int) (kp->width - kp->pen[pn].cx); segs[1].y2 = (int) kp->pen[pn].cy; segs[4].x1 = segs[1].x1; segs[4].y1 = (int) kp->height - segs[0].y1; segs[4].x2 = segs[1].x2; segs[4].y2 = (int) kp->height - segs[0].y2; segs[5].x1 = segs[0].x1; segs[5].y1 = (int) kp->height - segs[0].y1; segs[5].x2 = segs[0].x2; segs[5].y2 = (int) kp->height - segs[0].y2; oxscaled2y = ((kp->pen[pn].ox * kp->heightby2) / kp->widthby2); oyscaled2x = ((kp->pen[pn].oy * kp->widthby2) / kp->heightby2); cxscaled2y = ((kp->pen[pn].cx * kp->heightby2) / kp->widthby2); cyscaled2x = ((kp->pen[pn].cy * kp->widthby2) / kp->heightby2); segs[7].x1 = (int) oyscaled2x; segs[7].y1 = (int) oxscaled2y; segs[7].x2 = (int) cyscaled2x; segs[7].y2 = (int) cxscaled2y; segs[2].x1 = (int) kp->width - segs[7].x1; segs[2].y1 = segs[7].y1; segs[2].x2 = (int) kp->width - segs[7].x2; segs[2].y2 = segs[7].y2; segs[6].x1 = segs[7].x1; segs[6].y1 = (int) kp->height - segs[7].y1; segs[6].x2 = segs[7].x2; segs[6].y2 = (int) kp->height - segs[7].y2; segs[3].x1 = (int) kp->width - segs[7].x1; segs[3].y1 = segs[6].y1; segs[3].x2 = (int) kp->width - segs[7].x2; segs[3].y2 = segs[6].y2; } /*- * */ #if 0 static void OldOctRotated( kaleidstruct * kp, int pn, XSegment * segs ) { double angle, radius; double oangle, oradius; double rv, av; double perp, para; int i; /* * I know that the "bounce" is not really accurate, but I like the way * it looks. */ kp->pen[pn].cx = kp->pen[pn].cx + kp->pen[pn].xv; kp->pen[pn].cy = kp->pen[pn].cy + kp->pen[pn].yv; angle = CartToAngle(kp->pen[pn].cx, kp->pen[pn].cy); radius = CartToRadius(kp->pen[pn].cx, kp->pen[pn].cy); oangle = CartToAngle(kp->pen[pn].ox, kp->pen[pn].oy); oradius = CartToRadius(kp->pen[pn].ox, kp->pen[pn].oy); if (radius < 0.0) { if (kp->pen[pn].xv < 0.0) { kp->pen[pn].xv = -kp->pen[pn].xv; } } else if (radius > kp->radius) { if (kp->pen[pn].xv > 0.0) { kp->pen[pn].xv = -kp->pen[pn].xv; } } if (angle < 0.0) { if (oangle > 0.0) { kp->pen[pn].yv = -kp->pen[pn].yv; } } else if (angle > 45.0) { if (oangle < 45.0) { rv = CartToRadius(kp->pen[pn].xv, kp->pen[pn].yv); av = CartToAngle(kp->pen[pn].xv, kp->pen[pn].yv); para = PolarToCartX(rv, av - 45.0); perp = PolarToCartY(rv, av - 45.0); rv = CartToRadius(para, -perp); av = CartToAngle(para, -perp); kp->pen[pn].xv = PolarToCartX(rv, av + 45.0); kp->pen[pn].yv = PolarToCartY(rv, av + 45.0); } } for (i = 0; i < 8; i += 1) { segs[i].x1 = (int) (kp->widthby2 + PolarToCartX(oradius, oangle)); segs[i].y1 = (int) (kp->heightby2 - PolarToCartY(oradius, oangle)); segs[i].x2 = (int) (kp->widthby2 + PolarToCartX(radius, angle)); segs[i].y2 = (int) (kp->heightby2 - PolarToCartY(radius, angle)); oangle += 45.0; angle += 45.0; } } #endif /*- * */ static void OctRotated( kaleidstruct * kp, int pn, XSegment * segs ) { double xdiag; double ydiag; double radius, angle; double oradius, oangle; double ox, oy, cx, cy; double offset; int i; /* * I know that the "bounce" is not really accurate, but I like the way * it looks. */ xdiag = (kp->widthby2 * kp->pen[pn].oy) / kp->heightby2; kp->pen[pn].cx = kp->pen[pn].cx + kp->pen[pn].xv; if (kp->pen[pn].cx < xdiag) { kp->pen[pn].cx = xdiag + (xdiag - kp->pen[pn].cx); kp->pen[pn].xv = -kp->pen[pn].xv; } else if (kp->pen[pn].cx >= kp->widthby2) { kp->pen[pn].cx = (kp->widthby2 - 1.0) - (kp->pen[pn].cx - kp->widthby2); kp->pen[pn].xv = -kp->pen[pn].xv; } ydiag = (kp->heightby2 * kp->pen[pn].cx) / kp->widthby2; kp->pen[pn].cy = kp->pen[pn].cy + kp->pen[pn].yv; if (kp->pen[pn].cy < 0.0) { kp->pen[pn].cy = -kp->pen[pn].cy; kp->pen[pn].yv = -kp->pen[pn].yv; } else if (kp->pen[pn].cy > ydiag) { kp->pen[pn].cy = ydiag - (kp->pen[pn].cy - ydiag); kp->pen[pn].yv = -kp->pen[pn].yv; } offset = CartToRadius(kp->heightby2, kp->widthby2); ox = (kp->pen[pn].ox * offset) / kp->widthby2; oy = (kp->pen[pn].oy * offset) / kp->heightby2; cx = (kp->pen[pn].cx * offset) / kp->widthby2; cy = (kp->pen[pn].cy * offset) / kp->heightby2; angle = CartToAngle(cx - offset, offset - cy ) - 90.0; radius = CartToRadius(cx - offset, offset - cy ); oangle = CartToAngle(ox - offset, offset - oy ) - 90.0; oradius = CartToRadius(ox - offset, offset - oy ); for (i = 0; i < 8; i += 1) { segs[i].x1 = (int) (kp->widthby2 + PolarToCartX(oradius, oangle)); segs[i].y1 = (int) (kp->heightby2 - PolarToCartY(oradius, oangle)); segs[i].x2 = (int) (kp->widthby2 + PolarToCartX(radius, angle)); segs[i].y2 = (int) (kp->heightby2 - PolarToCartY(radius, angle)); oangle += 45.0; angle += 45.0; } } /*- * */ static void GeneralLinearMoveAndBounce( kaleidstruct * kp, int pn, double *angle, double *radius, double *oangle, double *oradius ) { double rv, av; double perp, para; kp->pen[pn].cx = kp->pen[pn].cx + kp->pen[pn].xv; kp->pen[pn].cy = kp->pen[pn].cy + kp->pen[pn].yv; *angle = CartToAngle(kp->pen[pn].cx, kp->pen[pn].cy); *radius = CartToRadius(kp->pen[pn].cx, kp->pen[pn].cy); *oangle = CartToAngle(kp->pen[pn].ox, kp->pen[pn].oy); *oradius = CartToRadius(kp->pen[pn].ox, kp->pen[pn].oy); if (*radius < 0.0) { kp->pen[pn].RadiusOut = True; if (*oradius > 0.0) { rv = CartToRadius(kp->pen[pn].xv, kp->pen[pn].yv); av = CartToAngle(kp->pen[pn].xv, kp->pen[pn].yv); para = PolarToCartX(rv, av - (*angle + 90.0)); perp = PolarToCartY(rv, av - (*angle + 90.0)); rv = CartToRadius(para, -perp); av = CartToAngle(para, -perp); kp->pen[pn].xv = PolarToCartX(rv, av + (*angle + 90.0)); kp->pen[pn].yv = PolarToCartY(rv, av + (*angle + 90.0)); } } else if (*radius > kp->radius) { kp->pen[pn].RadiusOut = True; if (*oradius < kp->radius) { rv = CartToRadius(kp->pen[pn].xv, kp->pen[pn].yv); av = CartToAngle(kp->pen[pn].xv, kp->pen[pn].yv); para = PolarToCartX(rv, av - (*angle + 90.0)); perp = PolarToCartY(rv, av - (*angle + 90.0)); rv = CartToRadius(para, -perp); av = CartToAngle(para, -perp); kp->pen[pn].xv = PolarToCartX(rv, av + (*angle + 90.0)); kp->pen[pn].yv = PolarToCartY(rv, av + (*angle + 90.0)); } } else { kp->pen[pn].RadiusOut = False; } if (*angle < 0.0) { if (kp->spiral) { kp->pen[pn].AngleOut = False; *angle = *angle + 360.0; } else { kp->pen[pn].AngleOut = True; if (*oangle > 0.0) { rv = CartToRadius(kp->pen[pn].xv, kp->pen[pn].yv); av = CartToAngle(kp->pen[pn].xv, kp->pen[pn].yv); para = PolarToCartX(rv, av); perp = PolarToCartY(rv, av); rv = CartToRadius(para, -perp); av = CartToAngle(para, -perp); kp->pen[pn].xv = PolarToCartX(rv, av); kp->pen[pn].yv = PolarToCartY(rv, av); } } } else if (kp->spiral) { if (*angle > 360.0) { kp->pen[pn].AngleOut = False; *angle = *angle - 360.0; } } else if (*angle > kp->slice) { kp->pen[pn].AngleOut = True; if (*oangle < kp->slice) { rv = CartToRadius(kp->pen[pn].xv, kp->pen[pn].yv); av = CartToAngle(kp->pen[pn].xv, kp->pen[pn].yv); para = PolarToCartX(rv, av - kp->slice); perp = PolarToCartY(rv, av - kp->slice); rv = CartToRadius(para, -perp); av = CartToAngle(para, -perp); kp->pen[pn].xv = PolarToCartX(rv, av + kp->slice); kp->pen[pn].yv = PolarToCartY(rv, av + kp->slice); } } else { kp->pen[pn].AngleOut = False; } } /*- * */ static void GeneralLinearRotated( kaleidstruct * kp, int pn, XSegment * segs ) { double angle, radius; double oangle, oradius; int i; GeneralLinearMoveAndBounce(kp, pn, &angle, &radius, &oangle, &oradius); for (i = 0; i < kp->modetype; i += 1) { if (! kp->spots) { segs[i].x1 = (int) (kp->widthby2 + PolarToCartX(oradius, oangle)); segs[i].y1 = (int) (kp->heightby2 - PolarToCartY(oradius, oangle)); } segs[i].x2 = (int) (kp->widthby2 + PolarToCartX(radius, angle)); segs[i].y2 = (int) (kp->heightby2 - PolarToCartY(radius, angle)); if (kp->spots) { segs[i].x1 = segs[i].x2; segs[i].y1 = segs[i].y2; } oangle += kp->slice; angle += kp->slice; } } /*- * */ static void GeneralLinearMirrored( kaleidstruct * kp, int pn, XSegment * segs ) { double hangle, angle, radius; double hoangle, oangle, oradius; int i; GeneralLinearMoveAndBounce(kp, pn, &angle, &radius, &oangle, &oradius); hoangle = oangle; hangle = angle; for (i = 0; i < kp->modetype; i += 2) { if (! kp->spots) { segs[i].x1 = (int) (kp->widthby2 + PolarToCartX(oradius, oangle)); segs[i].y1 = (int) (kp->heightby2 - PolarToCartY(oradius, oangle)); } segs[i].x2 = (int) (kp->widthby2 + PolarToCartX(radius, angle)); segs[i].y2 = (int) (kp->heightby2 - PolarToCartY(radius, angle)); if (kp->spots) { segs[i].x1 = segs[i].x2; segs[i].y1 = segs[i].y2; } oangle += 2.0 * kp->slice; angle += 2.0 * kp->slice; } oangle = kp->slice * 2.0; angle = kp->slice * 2.0; for (i = 1; i < kp->modetype; i += 2) { if (! kp->spots) { segs[i].x1 = (int) (kp->widthby2 + PolarToCartX(oradius, oangle - hoangle)); segs[i].y1 = (int) (kp->heightby2 - PolarToCartY(oradius, oangle - hoangle)); } segs[i].x2 = (int) (kp->widthby2 + PolarToCartX(radius, angle - hangle)); segs[i].y2 = (int) (kp->heightby2 - PolarToCartY(radius, angle - hangle)); if (kp->spots) { segs[i].x1 = segs[i].x2; segs[i].y1 = segs[i].y2; } oangle += 2.0 * kp->slice; angle += 2.0 * kp->slice; } } /*- * */ static void random_velocity(kaleidstruct * kp, int i) { int tMxRV, tMnRV; int tMxAV, tMnAV; int tMxV, tMnV; if (kp->spots) { tMxRV = MaxRadialVelocity * SpotsMult; tMnRV = MinRadialVelocity * SpotsMult; tMxAV = MaxAngularVelocity * SpotsMult; tMnAV = MinAngularVelocity * SpotsMult; tMxV = MaxVelocity * SpotsMult; tMnV = MinVelocity * SpotsMult; } else { tMxRV = MaxRadialVelocity; tMnRV = MinRadialVelocity; tMxAV = MaxAngularVelocity; tMnAV = MinAngularVelocity; tMxV = MaxVelocity; tMnV = MinVelocity; } if (kp->modetype > 0) { kp->pen[i].xv = INTRAND(-tMxRV, tMxRV); if (kp->pen[i].xv > 0.0) { kp->pen[i].xv += tMnRV; } else if (kp->pen[i].xv < 0.0) { kp->pen[i].xv -= tMnRV; } kp->pen[i].yv = INTRAND(-tMxAV, tMxAV); if (kp->pen[i].yv > 0.0) { kp->pen[i].yv += tMnAV; } else if (kp->pen[i].yv < 0.0) { kp->pen[i].yv -= tMnAV; } } else { kp->pen[i].xv = INTRAND(-tMxV, tMxV); if (kp->pen[i].xv > 0.0) { kp->pen[i].xv += tMnV; } else if (kp->pen[i].xv < 0.0) { kp->pen[i].xv -= tMnV; } kp->pen[i].yv = INTRAND(-tMxV, tMxV); if (kp->pen[i].yv > 0.0) { kp->pen[i].yv += tMnV; } else if (kp->pen[i].yv < 0.0) { kp->pen[i].yv -= tMnV; } } } static void random_position(kaleidstruct * kp, int i) { if (kp->modetype >= 0) { if (kp->linear) { double radius, angle; radius = (double) INTRAND(0, (int) (kp->radius - 1.0)); angle = (double) INTRAND(0, (int) (kp->slice - 1.0)); kp->pen[i].cx = PolarToCartX(radius, angle); kp->pen[i].cy = PolarToCartY(radius, angle); } else { kp->pen[i].cx = (double) INTRAND(0, (int) (kp->radius - 1.0)); kp->pen[i].cy = (double) INTRAND(0, (int) (kp->slice - 1.0)); } } else if (kp->modetype == OCT) { double radius, angle; radius = (double) INTRAND(0, (int) (kp->radius - 1.0)); angle = (double) INTRAND(0, 44); kp->pen[i].cx = PolarToCartX(radius, angle); kp->pen[i].cy = PolarToCartY(radius, angle); } else { kp->pen[i].cx = (double) INTRAND(0, (int) (kp->widthby2 - 1.0)); kp->pen[i].cy = (double) INTRAND(0, (int) ((kp->heightby2 * kp->pen[i].cx) / kp->widthby2)); } } /*- * */ void init_kaleid(ModeInfo * mi) { int i; kaleidstruct *kp; if (kaleids == NULL) { if ((kaleids = (kaleidstruct *) calloc(MI_NUM_SCREENS(mi), sizeof (kaleidstruct))) == NULL) return; } kp = &kaleids[MI_SCREEN(mi)]; kp->PenCount = MI_COUNT(mi); if (MI_IS_FULLRANDOM(mi)) { kp->alternate = (Bool) (LRAND() & 1); kp->disconnected = (Bool) (LRAND() & 1); kp->serial = (Bool) (LRAND() & 1); kp->linear = (Bool) (LRAND() & 1); kp->spiral = (Bool) (LRAND() & 1); kp->spots = (Bool) (LRAND() & 1); } else { kp->alternate = Alternate; kp->disconnected = Disconnected; kp->serial = Serial; kp->linear = Linear; kp->spiral = Spiral; kp->spots = Spots; } if (kp->PenCount < -MINPENS) { /* if kp->PenCount is random ... the size can change */ if (kp->pen != NULL) { free(kp->pen); kp->pen = (penstruct *) NULL; } kp->PenCount = NRAND(-kp->PenCount - MINPENS + 1) + MINPENS; } else if (kp->PenCount < MINPENS) kp->PenCount = MINPENS; if (kp->pen == NULL) { if ((kp->pen = (penstruct *) malloc(kp->PenCount * sizeof (penstruct))) == NULL) return; } if ((MI_SIZE(mi)) > MINSIZE) { kp->modetype = (!kp->alternate + 1) * MI_SIZE(mi); } else if ((MI_SIZE(mi)) < -MINSIZE) { kp->modetype = (!kp->alternate + 1) * (NRAND(-MI_SIZE(mi) + 1) + MINSIZE); } else { kp->modetype = (!kp->alternate + 1) * MINSIZE; } if (MI_IS_FULLRANDOM(mi)) { int tmp; tmp = NRAND(ABS(MI_SIZE(mi)) + 2); if (tmp == 0) kp->modetype = OCT; else if (tmp == 1) kp->modetype = QUAD; } else { if (Oct) kp->modetype = OCT; else if (Quad) kp->modetype = QUAD; } kp->PercentBlack = (int) MAX(0, MIN(MI_CYCLES(mi), 95)); /* set various size parameters */ kp->width = (double) MI_WIDTH(mi); kp->height = (double) MI_HEIGHT(mi); if (kp->width < 2.0) kp->width = 2.0; if (kp->height < 2.0) kp->height = 2.0; kp->radius = sqrt(((kp->width * kp->width) + (kp->height * kp->height) ) / 4.0 ); if (kp->modetype >= 0) { kp->bouncetype = INTRAND(0, 2); kp->slice = 360.0 / (double) kp->modetype; kp->widthby2 = kp->width / 2.0; kp->heightby2 = kp->height / 2.0; } else { kp->widthby2 = kp->width / 2.0; kp->heightby2 = kp->height / 2.0; } /* set the maximum pen width */ if (kp->modetype >= 0) { if ((kp->slice == 360.0) || (kp->slice == 180.0)) { kp->maxlwidth = (int) ((((double) MIN(kp->widthby2, kp->heightby2)) * (double) WidthPercent) / 100.0); } else { kp->maxlwidth = (int) (((sin(kp->slice * ToRadians) * MIN(kp->widthby2, kp->heightby2)) * (double) WidthPercent) / 100.0); } } else { kp->maxlwidth = (int) ((MIN(kp->widthby2, kp->heightby2 ) * (double) WidthPercent ) / 100.0 ); } if (kp->spots) { kp->maxlwidth = 2 * kp->maxlwidth; } if (kp->maxlwidth <= 0) { kp->maxlwidth = 1; } for (i = 0; i < kp->PenCount; i += 1) { if (MI_NPIXELS(mi) > 2) { kp->pen[i].pix = NRAND(MI_NPIXELS(mi)); kp->pen[i].White = 1; } else { kp->pen[i].White = 1; } kp->pen[i].curlwidth = INTRAND(1, kp->maxlwidth); kp->pen[i].RadiusOut = False; kp->pen[i].AngleOut = False; random_position(kp, i); kp->pen[i].ox = kp->pen[i].cx; kp->pen[i].oy = kp->pen[i].cy; kp->pen[i].DeferredChange = False; random_velocity(kp, i); } MI_CLEARWINDOW(mi); } /*- * */ static void set_pen_attributes(ModeInfo * mi, kaleidstruct * kp, int i) { Display *display = MI_DISPLAY(mi); GC gc = MI_GC(mi); if (kp->pen[i].White) { if (MI_NPIXELS(mi) > 2) XSetForeground(display, gc, MI_PIXEL(mi, kp->pen[i].pix)); else XSetForeground(display, gc, MI_WHITE_PIXEL(mi)); } else { XSetForeground(display, gc, MI_BLACK_PIXEL(mi)); } XSetLineAttributes(display, gc, kp->pen[i].curlwidth, LineSolid, CapRound, JoinRound); } /*- * */ static void change_pen(ModeInfo * mi, kaleidstruct * kp, int i) { if (INTRAND(0, 100) < kp->PercentBlack) { kp->pen[i].White = 0; } else { kp->pen[i].White = 1; if (kp->serial) { if (MI_NPIXELS(mi) > 2) { if (++kp->pen[i].pix >= MI_NPIXELS(mi)) kp->pen[i].pix = 0; } } else { if (MI_NPIXELS(mi) > 2) { kp->pen[i].pix = NRAND(MI_NPIXELS(mi)); } } } random_velocity(kp, i); kp->pen[i].curlwidth = INTRAND(1, kp->maxlwidth); if (kp->modetype >= 0) { kp->bouncetype = INTRAND(0, 2); } if (kp->disconnected) { random_position(kp, i); kp->pen[i].ox = kp->pen[i].cx; kp->pen[i].oy = kp->pen[i].cy; } } /*- * */ void draw_kaleid(ModeInfo * mi) { Display *display = MI_DISPLAY(mi); GC gc = MI_GC(mi); XSegment *segs; int NumberOfSegments; int i; kaleidstruct *kp; if (kaleids == NULL) return; kp = &kaleids[MI_SCREEN(mi)]; if (kp->pen == NULL) return; MI_IS_DRAWN(mi) = True; if (kp->modetype == QUAD) { NumberOfSegments = 4; } else if (kp->modetype == OCT) { NumberOfSegments = 8; } else { /* if (kp->modetype > 0) */ NumberOfSegments = kp->modetype; } if ((segs = (XSegment *) malloc(NumberOfSegments * sizeof (XSegment))) == NULL) { free(kp->pen); kp->pen = (penstruct *) NULL; return; } for (i = 0; i < kp->PenCount; i++) { set_pen_attributes(mi, kp, i); if (kp->modetype == QUAD) { if (kp->alternate) { QuadRotated(kp, i, segs); } else { QuadMirrored(kp, i, segs); } } else if (kp->modetype == OCT) { if (kp->alternate) { OctRotated(kp, i, segs); } else { OctMirrored(kp, i, segs); } } else { if (kp->alternate) { if (kp->linear) { GeneralLinearRotated(kp, i, segs); } else { GeneralRotated(kp, i, segs); } } else { if (kp->linear) { GeneralLinearMirrored(kp, i, segs); } else { GeneralMirrored(kp, i, segs); } } } XDrawSegments( display, MI_WINDOW(mi), gc, segs, NumberOfSegments ); kp->pen[i].ox = kp->pen[i].cx; kp->pen[i].oy = kp->pen[i].cy; if ((INTRAND(0, 100) < ChangeChance) || kp->pen[i].DeferredChange) { if (!kp->pen[i].AngleOut && !kp->pen[i].RadiusOut) { kp->pen[i].DeferredChange = False; change_pen(mi, kp, i); } else { kp->pen[i].DeferredChange = True; } } } XSetLineAttributes(display, gc, 1, LineSolid, CapRound, JoinRound); free(segs); } /*- * */ void release_kaleid(ModeInfo * mi) { if (kaleids != NULL) { int screen; for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) { kaleidstruct *kp = &kaleids[screen]; if (kp->pen != NULL) free(kp->pen); } free(kaleids); kaleids = (kaleidstruct *) NULL; } } /*- * */ void refresh_kaleid(ModeInfo * mi) { MI_CLEARWINDOW(mi); } #endif /* MODE_kaleid */