/************************************************************************** * * FILE lmorph.c * MODULE OF xscreensaver * * DESCRIPTION Bilinear interpolation for morphing line shapes. * * WRITTEN BY Sverre H. Huseby Glenn T. Lines * Maridalsvn. 122, leil 101 Frysjavn. 3, 5. etg. * N-0461 Oslo N-0883 Oslo * Norway Norway * * Phone: +47 22 71 99 08 Phone: +47 22 23 71 99 * E-mail: sverrehu@ifi.uio.no E-mail: gtl@si.sintef.no * * The original idea, and the bilinear interpolation * mathematics used, emerged in the head of the wise * Glenn Terje Lines. * * MODIFICATIONS march 1995 * * Converted from an MS-Windows program to X Window. * **************************************************************************/ #include #include #include #include #include "screenhack.h" /************************************************************************** * * * P R I V A T E D A T A * * * **************************************************************************/ /* Define MARGINS to make some space around the figure */ #define MARGINS /**/ #define MAXFIGS 20 #define TWO_PI (2.0 * M_PI) #define RND(x) (random() % (x)) static int cFig = 0, /* Number of figure arrays. */ cPoint, /* Number of points in each array. */ nWork, /* Current work array number. */ nFrom, /* Current from array number. */ nTo; /* Current to array number. */ static long delay; /* usecs to wait between updates. */ static XPoint *aWork[2], /* Working arrays. */ *a[MAXFIGS], /* The figure arrays. */ *aTmp, /* Used as source when interrupting morph */ *aPrev, /* Previous points displayed. */ *aCurr, /* The current points displayed. */ *aFrom, /* Figure converting from. */ *aTo; /* Figure converting to. */ static double gam, maxGamma = 1.0, delta_gam; static GC gcDraw, gcClear; static Display *dpy; static Window window; /************************************************************************** * * * P U B L I C D A T A * * * **************************************************************************/ char *progclass = "LMorph"; char *defaults [] = { "LMorph.background: black", "LMorph.foreground: green", "*points: 150", "*steps: 0", "*delay: 50000", 0 }; XrmOptionDescRec options [] = { { "-points", ".points", XrmoptionSepArg, 0 }, { "-steps", ".steps", XrmoptionSepArg, 0 }, { "-delay", ".delay", XrmoptionSepArg, 0 }, }; int options_size = (sizeof (options) / sizeof (options[0])); /************************************************************************** * * * P R I V A T E F U N C T I O N S * * * **************************************************************************/ static void *xmalloc(size) size_t size; { void *ret; if ((ret = malloc(size)) == NULL) { fprintf(stderr, "lmorph: out of memory\n"); exit(1); } return ret; } static double frnd() { /* * Hm. for some reason the second line (using RAND_MAX) didn't * work on some machines, so I always use the first. */ #undef RAND_MAX #ifndef RAND_MAX return (double) (random() & 0x7FFF) / 0x7FFF; #else return ((double) random()) / RAND_MAX; #endif } static void initPointArrays() { XWindowAttributes wa; int q, w, mx, my, /* Max screen coordinates. */ mp, /* Max point number. */ s, rx, ry, marginx, marginy; double scalex, scaley; XGetWindowAttributes(dpy, window, &wa); mx = wa.width - 1; my = wa.height - 1; mp = cPoint - 1; aWork[0] = (XPoint *) xmalloc(cPoint * sizeof(XPoint)); aWork[1] = (XPoint *) xmalloc(cPoint * sizeof(XPoint)); aTmp = (XPoint *) xmalloc(cPoint * sizeof(XPoint)); /* * Figure 0 */ a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint)); s = cPoint / 4; for (q = 0; q < s; q++) { a[cFig][q].x = ((double) q / s) * mx; a[cFig][q].y = 0; a[cFig][s + q].x = mx; a[cFig][s + q].y = ((double) q / s) * my; a[cFig][2 * s + q].x = mx - ((double) q / s) * mx; a[cFig][2 * s + q].y = my; a[cFig][3 * s + q].x = 0; a[cFig][3 * s + q].y = my - ((double) q / s) * my; } for (q = 4 * s; q < cPoint; q++) a[cFig][q].x = a[cFig][q].y = 0; a[cFig][mp].x = a[cFig][0].x; a[cFig][mp].y = a[cFig][0].y; ++cFig; /* * Figure 1 */ a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint)); for (q = 0; q < cPoint; q++) { a[cFig][q].x = ((double) q / cPoint) * mx; a[cFig][q].y = (1.0 - sin(((double) q / mp) * TWO_PI)) * my / 2.0; } ++cFig; /* * Figure 2 */ a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint)); rx = mx / 2; ry = my / 2; for (q = 0; q < cPoint; q++) { a[cFig][q].x = mx / 2 + rx * sin(1 * TWO_PI * (double) q / mp); a[cFig][q].y = my / 2 + ry * cos(3 * TWO_PI * (double) q / mp); } a[cFig][mp].x = a[cFig][0].x; a[cFig][mp].y = a[cFig][0].y; ++cFig; /* * Figure 3 */ a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint)); rx = mx / 2; ry = my / 2; for (q = 0; q < cPoint; q++) { a[cFig][q].x = mx / 2 + ry * sin(3 * TWO_PI * (double) q / mp); a[cFig][q].y = my / 2 + ry * cos(1 * TWO_PI * (double) q / mp); } a[cFig][mp].x = a[cFig][0].x; a[cFig][mp].y = a[cFig][0].y; ++cFig; /* * Figure 4 */ a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint)); rx = mx / 2; ry = my / 2; for (q = 0; q < cPoint; q++) { a[cFig][q].x = mx / 2 + ry * (1 - 0.1 * frnd()) * sin(TWO_PI * (double) q / mp); a[cFig][q].y = my / 2 + ry * (1 - 0.1 * frnd()) * cos(TWO_PI * (double) q / mp); } a[cFig][mp].x = a[cFig][0].x; a[cFig][mp].y = a[cFig][0].y; ++cFig; /* * Figure 5 */ a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint)); rx = mx / 2; ry = my / 2; for (q = 0; q < cPoint; q++) { a[cFig][q].x = mx / 2 + ry * (0.8 - 0.2 * sin(30 * TWO_PI * q / mp)) * sin(TWO_PI * (double) q / mp); a[cFig][q].y = my / 2 + ry * (0.8 - 0.2 * sin(30 * TWO_PI * q / mp)) * cos(TWO_PI * (double) q / mp); } a[cFig][mp].x = a[cFig][0].x; a[cFig][mp].y = a[cFig][0].y; ++cFig; /* * Figure 6 */ a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint)); rx = mx / 2; ry = my / 2; for (q = 0; q < cPoint; q++) { a[cFig][q].x = mx / 2 + ry * sin(TWO_PI * (double) q / mp); a[cFig][q].y = my / 2 + ry * cos(TWO_PI * (double) q / mp); } a[cFig][mp].x = a[cFig][0].x; a[cFig][mp].y = a[cFig][0].y; ++cFig; /* * Figure 7 */ a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint)); rx = mx / 2; ry = my / 2; for (q = 0; q < cPoint; q++) { a[cFig][q].x = mx / 2 + rx * cos(TWO_PI * (double) q / mp); a[cFig][q].y = my / 2 + ry * sin(TWO_PI * (double) q / mp); } a[cFig][mp].x = a[cFig][0].x; a[cFig][mp].y = a[cFig][0].y; ++cFig; /* * Figure 8 */ a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint)); for (q = 0; q < cPoint; q++) { a[cFig][q].x = ((double) q / mp) * mx; a[cFig][q].y = (1.0 - cos(((double) q / mp) * 3 * TWO_PI)) * my / 2.0; } ++cFig; /* * Figure 9 */ a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint)); rx = mx / 2; ry = my / 2; for (q = 0; q < cPoint; q++) { a[cFig][q].x = mx / 2 + rx * sin(2 * TWO_PI * (double) q / mp); a[cFig][q].y = my / 2 + ry * cos(3 * TWO_PI * (double) q / mp); } a[cFig][mp].x = a[cFig][0].x; a[cFig][mp].y = a[cFig][0].y; ++cFig; /* * Figure 10 */ a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint)); rx = mx / 2; ry = my / 2; for (q = 0; q < cPoint; q++) { a[cFig][q].x = mx / 2 + ry * sin(5 * TWO_PI * (double) q / mp) * ((double) q / mp); a[cFig][q].y = my / 2 + ry * cos(5 * TWO_PI * (double) q / mp) * ((double) q / mp); } ++cFig; /* * Figure 11 */ a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint)); rx = mx / 2; ry = my / 2; for (q = 0; q < cPoint; q++) { a[cFig][q].x = mx / 2 + ry * sin(6 * TWO_PI * (double) q / mp) * ((double) q / mp); a[cFig][q].y = my / 2 - ry * cos(6 * TWO_PI * (double) q / mp) * ((double) q / mp); } ++cFig; /* * Figure 12 */ a[cFig] = (XPoint *) xmalloc(cPoint * sizeof(XPoint)); for (q = 0; q < cPoint; q++) { a[cFig][q].x = ((double) q / mp) * mx; a[cFig][q].y = (1.0 - sin(((double) q / mp) * 5 * TWO_PI)) * my / 2.0; } ++cFig; #ifdef MARGINS /* * Make some space around the figures. */ marginx = (mx + 1) / 10; marginy = (my + 1) / 10; scalex = (double) ((mx + 1) - 2.0 * marginx) / (mx + 1.0); scaley = (double) ((my + 1) - 2.0 * marginy) / (my + 1.0); for (q = 0; q < cFig; q++) for (w = 0; w < cPoint; w++) { a[q][w].x = marginx + a[q][w].x * scalex; a[q][w].y = marginy + a[q][w].y * scaley; } #endif } static void createPoints() { int q; XPoint *pa = aCurr, *pa1 = aFrom, *pa2 = aTo; long lg, l1g; lg = 8192L * gam, l1g = 8192L * (1.0 - gam); for (q = 0; q < cPoint; q++) { pa->x = (short) ((l1g * pa1->x + lg * pa2->x) / 8192L); pa->y = (short) ((l1g * pa1->y + lg * pa2->y) / 8192L); ++pa; ++pa1; ++pa2; } } static void drawImage() { register int q; XPoint *old0, *old1, *new0, *new1; /* * Problem: update the window without too much flickering. I do * this by handling each linesegment separately. First remove a * line, then draw the new line. The problem is that this leaves * small black pixels on the figure. To fix this, I draw the * entire figure using XDrawLines() afterwards. */ if (aPrev) { old0 = aPrev; old1 = aPrev + 1; new0 = aCurr; new1 = aCurr + 1; for (q = cPoint - 1; q; q--) { XDrawLine(dpy, window, gcClear, old0->x, old0->y, old1->x, old1->y); XDrawLine(dpy, window, gcDraw, new0->x, new0->y, new1->x, new1->y); ++old0; ++old1; ++new0; ++new1; } } XDrawLines(dpy, window, gcDraw, aCurr, cPoint, CoordModeOrigin); XFlush(dpy); } static void initLMorph() { int steps; XGCValues gcv; XWindowAttributes wa; Colormap cmap; cPoint = get_integer_resource("points", "Integer"); steps = get_integer_resource("steps", "Integer"); delay = get_integer_resource("delay", "Integer"); if (steps <= 0) steps = (random() % 400) + 100; delta_gam = 1.0 / steps; XGetWindowAttributes(dpy, window, &wa); cmap = wa.colormap; gcv.foreground = get_pixel_resource("foreground", "Foreground", dpy, cmap); gcDraw = XCreateGC(dpy, window, GCForeground, &gcv); XSetForeground(dpy, gcDraw, gcv.foreground); gcv.foreground = get_pixel_resource("background", "Background", dpy, cmap); gcClear = XCreateGC(dpy, window, GCForeground, &gcv); XClearWindow(dpy, window); srandom(time(NULL)); initPointArrays(); aCurr = aWork[nWork = 0]; aPrev = NULL; gam = 2.0; nTo = RND(cFig); } static void animateLMorph() { if (gam > maxGamma) { gam = 0.0; if (maxGamma == 1.0) { nFrom = nTo; aFrom = a[nFrom]; } else { memcpy(aTmp, aCurr, cPoint * sizeof(XPoint)); aFrom = aTmp; nFrom = -1; } do { nTo = RND(cFig); } while (nTo == nFrom); aTo = a[nTo]; if (RND(2)) { /* * Reverse the array to get more variation. */ int i1, i2; XPoint p; for (i1 = 0, i2 = cPoint - 1; i1 < cPoint / 2; i1++, i2--) { p = aTo[i1]; aTo[i1] = aTo[i2]; aTo[i2] = p; } } /* * It may be nice to interrupt the next run. */ if (RND(3) > 0) maxGamma = 0.1 + 0.7 * (RND(1001) / 1000.0); else maxGamma = 1.0; } createPoints(); drawImage(); aPrev = aCurr; aCurr = aWork[nWork ^= 1]; gam += delta_gam; } /************************************************************************** * * * P U B L I C F U N C T I O N S * * * **************************************************************************/ void screenhack(disp, win) Display *disp; Window win; { dpy = disp; window = win; initLMorph(); for (;;) { animateLMorph(); screenhack_usleep(delay); } }