X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=hacks%2Fnerverot.c;h=3c058c6dd806d8020ba3ec8247386df7a9c68d5b;hb=8eb2873d7054e705c4e83f22d18c40946a9e2529;hp=dcacb5ae097d24ef1c0a2f1c5023f41bee8cd0ce;hpb=93f25dc6827112d98b8b855ea85c8f5eb8123086;p=xscreensaver diff --git a/hacks/nerverot.c b/hacks/nerverot.c index dcacb5ae..3c058c6d 100644 --- a/hacks/nerverot.c +++ b/hacks/nerverot.c @@ -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 @@ -13,28 +13,7 @@ * 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 : foreground color - * -bg : background color - * -delay : delay between frames - * -event-chance : chance, per iteration, that an interesting event - * will happen (range 0..1) - * -iter-amt : amount, per iteration, to move towards rotation and - * scale targets (range 0..1) - * -count : number of blots - * -colors : number of colors to use - * -lineWidth : width of lines (0 means an optimized thin line) - * -nervousness : amount of nervousness (range 0..1) - * -min-scale : 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 : 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 : minimum radius for drawing blots (range 1..100) - * -max-radius : maximum radius for drawing blots (range 1..100) - * -max-nerve-radius : maximum nervousness radius (range 0..1) + * See the included man page for more details. */ #include @@ -44,11 +23,11 @@ /* 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)