http://packetstormsecurity.org/UNIX/admin/xscreensaver-4.00.tar.gz
[xscreensaver] / hacks / nerverot.c
index dcacb5ae097d24ef1c0a2f1c5023f41bee8cd0ce..3c058c6dd806d8020ba3ec8247386df7a9c68d5b 100644 (file)
@@ -1,6 +1,6 @@
-/* nerverot, nervous rotation of random thingies, v1.0
+/* nerverot, nervous rotation of random thingies, v1.4
  * by Dan Bornstein, danfuzz@milk.com
- * Copyright (c) 2000 Dan Bornstein.
+ * Copyright (c) 2000-2001 Dan Bornstein. All rights reserved.
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
  * The goal of this screensaver is to be interesting and compelling to
  * watch, yet induce a state of nervous edginess in the viewer.
  *
- * Brief description of options/resources:
- *
- *   -fg <color>: foreground color
- *   -bg <color>: background color
- *   -delay <usec>: delay between frames
- *   -event-chance <frac>: chance, per iteration, that an interesting event
- *     will happen (range 0..1)
- *   -iter-amt <frac>: amount, per iteration, to move towards rotation and
- *     scale targets (range 0..1)
- *   -count <n>: number of blots
- *   -colors <n>: number of colors to use
- *   -lineWidth <n>: width of lines (0 means an optimized thin line)
- *   -nervousness <frac>: amount of nervousness (range 0..1)
- *   -min-scale <frac>: minimum scale of drawing as fraction of base scale
- *     (which is the minumum of the width or height of the screen) (range
- *     0..10)
- *   -max-scale <frac>: maximum scale of drawing as fraction of base scale
- *     (which is the minumum of the width or height of the screen) (range
- *     0..10)
- *   -min-radius <n>: minimum radius for drawing blots (range 1..100)
- *   -max-radius <n>: maximum radius for drawing blots (range 1..100)
- *   -max-nerve-radius <frac>: maximum nervousness radius (range 0..1)
+ * See the included man page for more details.
  */
 
 #include <math.h>
 
 /* random float in the range (-1..1) */
 #define RAND_FLOAT_PM1 \
-        (((FLOAT) (random() & 0xffff)) / ((FLOAT) 0x10000) * 2 - 1)
+        (((FLOAT) ((random() >> 8) & 0xffff)) / ((FLOAT) 0x10000) * 2 - 1)
 
 /* random float in the range (0..1) */
 #define RAND_FLOAT_01 \
-        (((FLOAT) (random() & 0xffff)) / ((FLOAT) 0x10000))
+        (((FLOAT) ((random() >> 8) & 0xffff)) / ((FLOAT) 0x10000))
 
 
 
@@ -60,6 +39,9 @@ static int requestedBlotCount;
 /* delay (usec) between iterations */
 int delay;
 
+/* max iterations per model */
+int maxIters;
+
 /* variability of xoff/yoff per iteration (0..1) */
 static FLOAT nervousness;
 
@@ -87,6 +69,9 @@ static int colorCount;
 /* width of lines */
 static int lineWidth;
 
+/* whether or not to do double-buffering */
+static Bool doubleBuffer;
+
 
 
 /* non-user-modifiable immutable definitions */
@@ -103,9 +88,10 @@ static int windowHeight;
 static int centerX;
 static int centerY;
 
-static Display *display; /* the display to draw on */
-static Window window;    /* the window to draw on */
-static GC *gcs;          /* array of gcs, one per color used */
+static Display *display;  /* the display to draw on */
+static Window window;     /* the window to draw on */
+static Drawable drawable; /* the thing to directly draw on */
+static GC *gcs;           /* array of gcs, one per color used */
 
 
 
@@ -176,7 +162,7 @@ static int itersTillNext;
 
 
 /*
- * blot setup stuff
+ * generic blot setup and manipulation
  */
 
 /* initialize a blot with the given coordinates and random display offsets */
@@ -245,6 +231,58 @@ static void randomlyReorderBlots (void)
     }
 }
 
+/* randomly rotate the blots around the origin */
+static void randomlyRotateBlots (void)
+{
+    int n;
+
+    /* random amounts to rotate about each axis */
+    FLOAT xRot = RAND_FLOAT_PM1 * M_PI;
+    FLOAT yRot = RAND_FLOAT_PM1 * M_PI;
+    FLOAT zRot = RAND_FLOAT_PM1 * M_PI;
+
+    /* rotation factors */
+    FLOAT sinX = sin (xRot);
+    FLOAT cosX = cos (xRot);
+    FLOAT sinY = sin (yRot);
+    FLOAT cosY = cos (yRot);
+    FLOAT sinZ = sin (zRot);
+    FLOAT cosZ = cos (zRot);
+
+    for (n = 0; n < blotCount; n++)
+    {
+       FLOAT x1 = blots[n].x;
+       FLOAT y1 = blots[n].y;
+       FLOAT z1 = blots[n].z;
+       FLOAT x2, y2, z2;
+
+       /* rotate on z axis */
+       x2 = x1 * cosZ - y1 * sinZ;
+       y2 = x1 * sinZ + y1 * cosZ;
+       z2 = z1;
+
+       /* rotate on x axis */
+       y1 = y2 * cosX - z2 * sinX;
+       z1 = y2 * sinX + z2 * cosX;
+       x1 = x2;
+
+       /* rotate on y axis */
+       z2 = z1 * cosY - x1 * sinY;
+       x2 = z1 * sinY + x1 * cosY;
+       y2 = y1;
+
+       blots[n].x = x2;
+       blots[n].y = y2;
+       blots[n].z = z2;
+    }
+}
+
+
+
+/*
+ * blot configurations
+ */
+
 /* set up the initial array of blots to be a at the edge of a sphere */
 static void setupBlotsSphere (void)
 {
@@ -278,7 +316,6 @@ static void setupBlotsSphere (void)
 
        initBlot (&blots[n], x, y, z);
     }
-
 }
 
 /* set up the initial array of blots to be a simple cube */
@@ -335,19 +372,26 @@ static void setupBlotsCube (void)
 
     scaleBlotsToRadius1 ();
     randomlyReorderBlots ();
+    randomlyRotateBlots ();
 }
 
-
 /* set up the initial array of blots to be a cylinder */
 static void setupBlotsCylinder (void)
 {
     int i, j, n;
-
-    /* derive blotsPerEdge from blotCount, but then do the reverse
-     * since roundoff may have changed blotCount */
-    int blotsPerEdge = requestedBlotCount / 32;
     FLOAT distBetween;
 
+    /* derive blotsPerEdge and blotsPerRing from blotCount, but then do the
+     * reverse since roundoff may have changed blotCount */
+    FLOAT reqRoot = sqrt ((FLOAT) requestedBlotCount);
+    int blotsPerRing = ceil (RAND_FLOAT_PM1 * reqRoot) / 2 + reqRoot;
+    int blotsPerEdge = requestedBlotCount / blotsPerRing;
+
+    if (blotsPerRing < 2)
+    {
+       blotsPerRing = 2;
+    }
+
     if (blotsPerEdge < 2)
     {
        blotsPerEdge = 2;
@@ -355,15 +399,15 @@ static void setupBlotsCylinder (void)
 
     distBetween = 2.0 / (blotsPerEdge - 1);
 
-    blotCount = blotsPerEdge * 32;
+    blotCount = blotsPerEdge * blotsPerRing;
     blots = calloc (sizeof (Blot), blotCount);
     n = 0;
 
     /* define the edges */
-    for (i = 0; i < 32; i++)
+    for (i = 0; i < blotsPerRing; i++)
     {
-       FLOAT x = sin (2 * M_PI / 32 * i);
-       FLOAT y = cos (2 * M_PI / 32 * i);
+       FLOAT x = sin (2 * M_PI / blotsPerRing * i);
+       FLOAT y = cos (2 * M_PI / blotsPerRing * i);
        for (j = 0; j < blotsPerEdge; j++)
        {
            initBlot (&blots[n], x, y, j * distBetween - 1);
@@ -373,19 +417,22 @@ static void setupBlotsCylinder (void)
 
     scaleBlotsToRadius1 ();
     randomlyReorderBlots ();
+    randomlyRotateBlots ();
 }
 
-
-
 /* set up the initial array of blots to be a squiggle */
 static void setupBlotsSquiggle (void)
 {
     FLOAT x, y, z, xv, yv, zv, len;
+    int minCoor, maxCoor;
     int n;
 
     blotCount = requestedBlotCount;
     blots = calloc (sizeof (Blot), blotCount);
 
+    maxCoor = (int) (RAND_FLOAT_01 * 5) + 1;
+    minCoor = -maxCoor;
+
     x = RAND_FLOAT_PM1;
     y = RAND_FLOAT_PM1;
     z = RAND_FLOAT_PM1;
@@ -417,9 +464,9 @@ static void setupBlotsSquiggle (void)
            newy = y + yv * 0.1;
            newz = z + zv * 0.1;
 
-           if (   (newx >= -1) && (newx <= 1)
-               && (newy >= -1) && (newy <= 1)
-               && (newz >= -1) && (newz <= 1))
+           if (   (newx >= minCoor) && (newx <= maxCoor)
+               && (newy >= minCoor) && (newy <= maxCoor)
+               && (newz >= minCoor) && (newz <= maxCoor))
            {
                break;
            }
@@ -434,8 +481,248 @@ static void setupBlotsSquiggle (void)
     randomlyReorderBlots ();
 }
 
+/* set up the initial array of blots to be near the corners of a
+ * cube, distributed slightly */
+static void setupBlotsCubeCorners (void)
+{
+    int n;
+
+    blotCount = requestedBlotCount;
+    blots = calloc (sizeof (Blot), blotCount);
+
+    for (n = 0; n < blotCount; n++)
+    {
+       FLOAT x = rint (RAND_FLOAT_01) * 2 - 1;
+       FLOAT y = rint (RAND_FLOAT_01) * 2 - 1;
+       FLOAT z = rint (RAND_FLOAT_01) * 2 - 1;
+
+       x += RAND_FLOAT_PM1 * 0.3;
+       y += RAND_FLOAT_PM1 * 0.3;
+       z += RAND_FLOAT_PM1 * 0.3;
+
+       initBlot (&blots[n], x, y, z);
+    }
+
+    scaleBlotsToRadius1 ();
+    randomlyRotateBlots ();
+}
+
+/* set up the initial array of blots to be randomly distributed
+ * on the surface of a tetrahedron */
+static void setupBlotsTetrahedron (void)
+{
+    /* table of corners of the tetrahedron */
+    static FLOAT cor[4][3] = { {  0.0,   1.0,  0.0 },
+                              { -0.75, -0.5, -0.433013 },
+                              {  0.0,  -0.5,  0.866025 },
+                              {  0.75, -0.5, -0.433013 } };
+
+    int n, c;
+
+    /* derive blotsPerSurface from blotCount, but then do the reverse
+     * since roundoff may have changed blotCount */
+    int blotsPerSurface = requestedBlotCount / 4;
+
+    blotCount = blotsPerSurface * 4;
+    blots = calloc (sizeof (Blot), blotCount);
+
+    for (n = 0; n < blotCount; n += 4)
+    {
+       /* pick a random point on a unit right triangle */
+       FLOAT rawx = RAND_FLOAT_01;
+       FLOAT rawy = RAND_FLOAT_01;
+
+       if ((rawx + rawy) > 1)
+       {
+           /* swap coords into place */
+           FLOAT t = 1.0 - rawx;
+           rawx = 1.0 - rawy;
+           rawy = t;
+       }
+
+       /* translate the point to be on each of the surfaces */
+       for (c = 0; c < 4; c++)
+       {
+           FLOAT x, y, z;
+           
+           int c1 = (c + 1) % 4;
+           int c2 = (c + 2) % 4;
+           
+           x = (cor[c1][0] - cor[c][0]) * rawx + 
+               (cor[c2][0] - cor[c][0]) * rawy + 
+               cor[c][0];
+
+           y = (cor[c1][1] - cor[c][1]) * rawx + 
+               (cor[c2][1] - cor[c][1]) * rawy + 
+               cor[c][1];
+
+           z = (cor[c1][2] - cor[c][2]) * rawx + 
+               (cor[c2][2] - cor[c][2]) * rawy + 
+               cor[c][2];
+
+           initBlot (&blots[n + c], x, y, z);
+       }
+    }
+
+    randomlyRotateBlots ();
+}
+
+/* set up the initial array of blots to be an almost-evenly-distributed
+ * square sheet */
+static void setupBlotsSheet (void)
+{
+    int x, y;
+
+    int blotsPerDimension = floor (sqrt (requestedBlotCount));
+    FLOAT spaceBetween;
+
+    if (blotsPerDimension < 2)
+    {
+       blotsPerDimension = 2;
+    }
+
+    spaceBetween = 2.0 / (blotsPerDimension - 1);
+
+    blotCount = blotsPerDimension * blotsPerDimension;
+    blots = calloc (sizeof (Blot), blotCount);
+
+    for (x = 0; x < blotsPerDimension; x++)
+    {
+       for (y = 0; y < blotsPerDimension; y++)
+       {
+           FLOAT x1 = x * spaceBetween - 1.0;
+           FLOAT y1 = y * spaceBetween - 1.0;
+           FLOAT z1 = 0.0;
+
+           x1 += RAND_FLOAT_PM1 * spaceBetween / 3;
+           y1 += RAND_FLOAT_PM1 * spaceBetween / 3;
+           z1 += RAND_FLOAT_PM1 * spaceBetween / 2;
+
+           initBlot (&blots[x + y * blotsPerDimension], x1, y1, z1);
+       }
+    }
+
+    scaleBlotsToRadius1 ();
+    randomlyReorderBlots ();
+    randomlyRotateBlots ();
+}
+
+/* set up the initial array of blots to be a swirlycone */
+static void setupBlotsSwirlyCone (void)
+{
+    FLOAT radSpace = 1.0 / (requestedBlotCount - 1);
+    FLOAT zSpace = radSpace * 2;
+    FLOAT rotAmt = RAND_FLOAT_PM1 * M_PI / 10;
+
+    int n;
+    FLOAT rot = 0.0;
+
+    blotCount = requestedBlotCount;
+    blots = calloc (sizeof (Blot), blotCount);
+
+    for (n = 0; n < blotCount; n++)
+    {
+       FLOAT radius = n * radSpace;
+       FLOAT x = cos (rot) * radius;
+       FLOAT y = sin (rot) * radius;
+       FLOAT z = n * zSpace - 1.0;
+
+       rot += rotAmt;
+       initBlot (&blots[n], x, y, z);
+    }
+
+    scaleBlotsToRadius1 ();
+    randomlyReorderBlots ();
+    randomlyRotateBlots ();
+}
+
+/* forward declaration for recursive use immediately below */
+static void setupBlots (void);
+
+/* set up the blots to be two of the other choices, placed next to
+ * each other */
+static void setupBlotsDuo (void)
+{
+    int origRequest = requestedBlotCount;
+    FLOAT tx, ty, tz, radius;
+    Blot *blots1, *blots2;
+    int count1, count2;
+    int n;
+
+    if (requestedBlotCount < 15)
+    {
+       /* special case bottom-out */
+       setupBlotsSphere ();
+       return;
+    }
+
+    tx = RAND_FLOAT_PM1;
+    ty = RAND_FLOAT_PM1;
+    tz = RAND_FLOAT_PM1;
+    radius = sqrt (tx * tx + ty * ty + tz * tz);
+    tx /= radius;
+    ty /= radius;
+    tz /= radius;
+
+    /* recursive call to setup set 1 */
+    requestedBlotCount = origRequest / 2;
+    setupBlots ();
+
+    if (blotCount >= origRequest)
+    {
+       /* return immediately if this satisfies the original count request */
+       requestedBlotCount = origRequest;
+       return;
+    }
+
+    blots1 = blots;
+    count1 = blotCount;
+    blots = NULL;
+    blotCount = 0;
+    
+    /* translate to new position */
+    for (n = 0; n < count1; n++)
+    {
+       blots1[n].x += tx;
+       blots1[n].y += ty;
+       blots1[n].z += tz;
+    }
+
+    /* recursive call to setup set 2 */
+    requestedBlotCount = origRequest - count1;
+    setupBlots ();
+    blots2 = blots;
+    count2 = blotCount;
+
+    /* translate to new position */
+    for (n = 0; n < count2; n++)
+    {
+       blots2[n].x -= tx;
+       blots2[n].y -= ty;
+       blots2[n].z -= tz;
+    }
+
+    /* combine the two arrays */
+    blotCount = count1 + count2;
+    blots = calloc (sizeof (Blot), blotCount);
+    memcpy (&blots[0],      blots1, sizeof (Blot) * count1);
+    memcpy (&blots[count1], blots2, sizeof (Blot) * count2);
+    free (blots1);
+    free (blots2);
+
+    scaleBlotsToRadius1 ();
+    randomlyReorderBlots ();
+
+    /* restore the original requested count, for future iterations */
+    requestedBlotCount = origRequest;
+}
+
 
 
+/*
+ * main blot setup
+ */
+
 /* free the blots, in preparation for a new shape */
 static void freeBlots (void)
 {
@@ -458,12 +745,10 @@ static void freeBlots (void)
     }
 }
 
-
-
 /* set up the initial arrays of blots */
 static void setupBlots (void)
 {
-    int which = RAND_FLOAT_01 * 4;
+    int which = RAND_FLOAT_01 * 11;
 
     freeBlots ();
 
@@ -481,15 +766,37 @@ static void setupBlots (void)
        case 3:
            setupBlotsSquiggle ();
            break;
+       case 4:
+           setupBlotsCubeCorners ();
+           break;
+       case 5:
+           setupBlotsTetrahedron ();
+           break;
+       case 6:
+           setupBlotsSheet ();
+           break;
+       case 7:
+           setupBlotsSwirlyCone ();
+           break;
+       case 8:
+       case 9:
+       case 10:
+           setupBlotsDuo ();
+           break;
     }
+}
 
+/* set up the segments arrays */
+static void setupSegs (void)
+{
     /* there are blotShapeCount - 1 line segments per blot */
     segCount = blotCount * (blotShapeCount - 1);
     segsToErase = calloc (sizeof (LineSegment), segCount);
     segsToDraw = calloc (sizeof (LineSegment), segCount);
 
     /* erase the world */
-    XFillRectangle (display, window, gcs[0], 0, 0, windowWidth, windowHeight);
+    XFillRectangle (display, drawable, gcs[0], 0, 0, 
+                   windowWidth, windowHeight);
 }
 
 
@@ -566,8 +873,19 @@ static void setup (void)
     centerY = windowHeight / 2;
     baseScale = (xgwa.height < xgwa.width) ? xgwa.height : xgwa.width;
 
+    if (doubleBuffer)
+    {
+       drawable = XCreatePixmap (display, window, xgwa.width, xgwa.height,
+                                 xgwa.depth);
+    }
+    else
+    {
+       drawable = window;
+    }
+
     setupColormap (&xgwa);
     setupBlots ();
+    setupSegs ();
 
     /* set up the initial rotation, scale, and light values as random, but
      * with the targets equal to where it is */
@@ -579,7 +897,7 @@ static void setup (void)
     lightY = lightYTarget = RAND_FLOAT_PM1;
     lightZ = lightZTarget = RAND_FLOAT_PM1;
 
-    itersTillNext = RAND_FLOAT_01 * 1234;
+    itersTillNext = RAND_FLOAT_01 * maxIters;
 }
 
 
@@ -681,8 +999,9 @@ static void updateWithFeeling (void)
     itersTillNext--;
     if (itersTillNext < 0)
     {
-       itersTillNext = RAND_FLOAT_01 * 1234;
+       itersTillNext = RAND_FLOAT_01 * maxIters;
        setupBlots ();
+       setupSegs ();
        renderSegs ();
     }
 
@@ -770,18 +1089,18 @@ static void updateWithFeeling (void)
            }
            case 7:
            {
-               centerXOff = RAND_FLOAT_PM1 * maxRadius / 2;
+               centerXOff = RAND_FLOAT_PM1 * maxRadius;
                break;
            }
            case 8:
            {
-               centerYOff = RAND_FLOAT_PM1 * maxRadius / 2;
+               centerYOff = RAND_FLOAT_PM1 * maxRadius;
                break;
            }
            case 9:
            {
-               centerXOff = RAND_FLOAT_PM1 * maxRadius / 2;
-               centerYOff = RAND_FLOAT_PM1 * maxRadius / 2;
+               centerXOff = RAND_FLOAT_PM1 * maxRadius;
+               centerYOff = RAND_FLOAT_PM1 * maxRadius;
                break;
            }
            case 10:
@@ -822,12 +1141,18 @@ static void eraseAndDraw (void)
     for (n = 0; n < segCount; n++)
     {
        LineSegment *seg = &segsToErase[n];
-       XDrawLine (display, window, gcs[0], 
+       XDrawLine (display, drawable, gcs[0], 
                   seg->x1, seg->y1, seg->x2, seg->y2);
        seg = &segsToDraw[n];
-       XDrawLine (display, window, seg->gc,
+       XDrawLine (display, drawable, seg->gc,
                   seg->x1, seg->y1, seg->x2, seg->y2);
     }
+
+    if (doubleBuffer)
+    {
+       XCopyArea (display, drawable, window, gcs[0], 0, 0, 
+                  windowWidth, windowHeight, 0, 0);
+    }
 }
 
 /* do one iteration */
@@ -856,6 +1181,8 @@ char *defaults [] = {
     "*count:           250",
     "*colors:          4",
     "*delay:           10000",
+    "*maxIters:                1200",
+    "*doubleBuffer:    false",
     "*eventChance:      0.2",
     "*iterAmt:          0.01",
     "*lineWidth:        0",
@@ -871,8 +1198,10 @@ char *defaults [] = {
 XrmOptionDescRec options [] = {
   { "-count",            ".count",          XrmoptionSepArg, 0 },
   { "-colors",           ".colors",         XrmoptionSepArg, 0 },
-  { "-cube",             ".cube",           XrmoptionNoArg,  "true" },
   { "-delay",            ".delay",          XrmoptionSepArg, 0 },
+  { "-max-iters",        ".maxIters",       XrmoptionSepArg, 0 },
+  { "-db",               ".doubleBuffer",   XrmoptionNoArg,  "true" },
+  { "-no-db",            ".doubleBuffer",   XrmoptionNoArg,  "false" },
   { "-event-chance",     ".eventChance",    XrmoptionSepArg, 0 },
   { "-iter-amt",         ".iterAmt",        XrmoptionSepArg, 0 },
   { "-line-width",       ".lineWidth",      XrmoptionSepArg, 0 },
@@ -896,6 +1225,15 @@ static void initParams (void)
        fprintf (stderr, "error: delay must be at least 0\n");
        problems = 1;
     }
+    
+    maxIters = get_integer_resource ("maxIters", "Integer");
+    if (maxIters < 0)
+    {
+       fprintf (stderr, "error: maxIters must be at least 0\n");
+       problems = 1;
+    }
+    
+    doubleBuffer = get_boolean_resource ("doubleBuffer", "Boolean");
 
     requestedBlotCount = get_integer_resource ("count", "Count");
     if (requestedBlotCount <= 0)