ftp://ftp.uni-heidelberg.de/pub/X11/contrib/applications/xscreensaver-2.07.tar.gz
[xscreensaver] / hacks / lmorph.c
diff --git a/hacks/lmorph.c b/hacks/lmorph.c
new file mode 100644 (file)
index 0000000..0c7571a
--- /dev/null
@@ -0,0 +1,498 @@
+
+/**************************************************************************
+ *
+ *  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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#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 },
+  { 0, 0, 0, 0 }
+};
+
+
+/**************************************************************************
+ *                                                                        *
+ *                   P R I V A T E    F U N C T I O N S                   *
+ *                                                                        *
+ **************************************************************************/
+
+static void *
+xmalloc(size_t size)
+{
+    void *ret;
+
+    if ((ret = malloc(size)) == NULL) {
+       fprintf(stderr, "lmorph: out of memory\n");
+       exit(1);
+    }
+    return ret;
+}
+
+
+
+static double frnd (void)
+{
+    /*
+     *  Hm. for some reason the second line (using RAND_MAX) didn't
+     *  work on some machines, so I always use the first.
+     */
+#ifndef dont_use_RAND_MAX
+    return (double) (random() & 0x7FFF) / 0x7FFF;
+#else  /* RAND_MAX */
+    return ((double) random()) / RAND_MAX;
+#endif /* RAND_MAX */
+}
+
+
+
+static void initPointArrays (void)
+{
+    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 (void)
+{
+    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 (void)
+{
+    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 (void)
+{
+    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 (void)
+{
+    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(Display *disp, Window win)
+{
+    dpy = disp;
+    window = win;
+    initLMorph();
+    for (;;) {
+       animateLMorph();
+       screenhack_usleep(delay);
+    }
+}