1 /* nerverot, nervous rotation of random thingies, v1.4
2 * by Dan Bornstein, danfuzz@milk.com
3 * Copyright (c) 2000-2001 Dan Bornstein. All rights reserved.
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that
8 * copyright notice and this permission notice appear in supporting
9 * documentation. No representations are made about the suitability of this
10 * software for any purpose. It is provided "as is" without express or
13 * The goal of this screensaver is to be interesting and compelling to
14 * watch, yet induce a state of nervous edginess in the viewer.
16 * See the included man page for more details.
20 #include "screenhack.h"
24 /* random float in the range (-1..1) */
25 #define RAND_FLOAT_PM1 \
26 (((FLOAT) ((random() >> 8) & 0xffff)) / ((FLOAT) 0x10000) * 2 - 1)
28 /* random float in the range (0..1) */
29 #define RAND_FLOAT_01 \
30 (((FLOAT) ((random() >> 8) & 0xffff)) / ((FLOAT) 0x10000))
33 /* structure of the model */
35 /* each point-like thingy to draw is represented as a blot */
38 FLOAT x; /* 3d x position (-1..1) */
39 FLOAT y; /* 3d y position (-1..1) */
40 FLOAT z; /* 3d z position (-1..1) */
41 FLOAT xoff[3][3]; /* display x offset per drawn point (-1..1) */
42 FLOAT yoff[3][3]; /* display x offset per drawn point (-1..1) */
45 /* each drawn line is represented as a LineSegment */
46 typedef struct linesegment_s
55 /* each blot draws as a simple 2d shape with each coordinate as an int
56 * in the range (-1..1); this is the base shape */
57 static const XPoint blotShape[] = { { 0, 0}, { 1, 0}, { 1, 1},
58 { 0, 1}, {-1, 1}, {-1, 0},
59 {-1,-1}, { 0,-1}, { 1,-1} };
60 static int blotShapeCount = sizeof (blotShape) / sizeof (XPoint);
70 int requestedBlotCount; /* number of blots */
71 int delay; /* delay (usec) between iterations */
72 int maxIters; /* max iterations per model */
73 FLOAT nervousness; /* variability of xoff/yoff per iteration (0..1) */
74 FLOAT maxNerveRadius; /* max nervousness radius (0..1) */
75 FLOAT eventChance; /* chance per iteration that an event will happen */
76 FLOAT iterAmt; /* fraction (0..1) towards rotation target or scale target to move each * iteration */
77 FLOAT minScale; /* min and max scale for drawing, as fraction of baseScale */
79 int minRadius; /* min and max radius of blot drawing */
81 int colorCount; /* the number of colors to use */
83 int lineWidth; /* width of lines */
85 Bool doubleBuffer; /* whether or not to do double-buffering */
88 int baseScale; /* base scale factor for drawing, calculated as max(screenWidth,screenHeight) */
91 int windowWidth; /* width and height of the window */
94 int centerX; /* center position of the window */
97 Drawable drawable; /* the thing to directly draw on */
98 GC *gcs; /* array of gcs, one per color used */
100 Blot *blots; /* array of the blots in the model */
103 int segCount; /* two arrays of line segments; one for the ones to erase, and one for the ones to draw */
105 LineSegment *segsToDraw;
106 LineSegment *segsToErase;
109 FLOAT xRot; /* current rotation values per axis, scale factor, and light position */
118 FLOAT xRotTarget; /* target rotation values per axis, scale factor, and light position */
127 int centerXOff; /* current absolute offsets from the center */
130 int itersTillNext; /* iterations until the model changes */
135 * generic blot setup and manipulation
138 /* initialize a blot with the given coordinates and random display offsets */
139 static void initBlot (Blot *b, FLOAT x, FLOAT y, FLOAT z)
147 for (i = 0; i < 3; i++)
149 for (j = 0; j < 3; j++)
151 b->xoff[i][j] = RAND_FLOAT_PM1;
152 b->yoff[i][j] = RAND_FLOAT_PM1;
157 /* scale the blots to have a max distance of 1 from the center */
158 static void scaleBlotsToRadius1 (struct state *st)
163 for (n = 0; n < st->blotCount; n++)
166 st->blots[n].x * st->blots[n].x +
167 st->blots[n].y * st->blots[n].y +
168 st->blots[n].z * st->blots[n].z;
169 if (distSquare > max)
182 for (n = 0; n < st->blotCount; n++)
184 st->blots[n].x /= max;
185 st->blots[n].y /= max;
186 st->blots[n].z /= max;
190 /* randomly reorder the blots */
191 static void randomlyReorderBlots (struct state *st)
195 for (n = 0; n < st->blotCount; n++)
197 int m = RAND_FLOAT_01 * (st->blotCount - n) + n;
198 Blot tmpBlot = st->blots[n];
199 st->blots[n] = st->blots[m];
200 st->blots[m] = tmpBlot;
204 /* randomly rotate the blots around the origin */
205 static void randomlyRotateBlots (struct state *st)
209 /* random amounts to rotate about each axis */
210 FLOAT xRot = RAND_FLOAT_PM1 * M_PI;
211 FLOAT yRot = RAND_FLOAT_PM1 * M_PI;
212 FLOAT zRot = RAND_FLOAT_PM1 * M_PI;
214 /* rotation factors */
215 FLOAT sinX = sin (xRot);
216 FLOAT cosX = cos (xRot);
217 FLOAT sinY = sin (yRot);
218 FLOAT cosY = cos (yRot);
219 FLOAT sinZ = sin (zRot);
220 FLOAT cosZ = cos (zRot);
222 for (n = 0; n < st->blotCount; n++)
224 FLOAT x1 = st->blots[n].x;
225 FLOAT y1 = st->blots[n].y;
226 FLOAT z1 = st->blots[n].z;
229 /* rotate on z axis */
230 x2 = x1 * cosZ - y1 * sinZ;
231 y2 = x1 * sinZ + y1 * cosZ;
234 /* rotate on x axis */
235 y1 = y2 * cosX - z2 * sinX;
236 z1 = y2 * sinX + z2 * cosX;
239 /* rotate on y axis */
240 z2 = z1 * cosY - x1 * sinY;
241 x2 = z1 * sinY + x1 * cosY;
253 * blot configurations
256 /* set up the initial array of blots to be a at the edge of a sphere */
257 static void setupBlotsSphere (struct state *st)
261 st->blotCount = st->requestedBlotCount;
262 st->blots = calloc (sizeof (Blot), st->blotCount);
264 for (n = 0; n < st->blotCount; n++)
266 /* pick a spot, but reject if its radius is < 0.2 or > 1 to
267 * avoid scaling problems */
268 FLOAT x, y, z, radius;
276 radius = sqrt (x * x + y * y + z * z);
277 if ((radius >= 0.2) && (radius <= 1.0))
287 initBlot (&st->blots[n], x, y, z);
291 /* set up the initial array of blots to be a simple cube */
292 static void setupBlotsCube (struct state *st)
296 /* derive blotsPerEdge from blotCount, but then do the reverse
297 * since roundoff may have changed blotCount */
298 int blotsPerEdge = ((st->requestedBlotCount - 8) / 12) + 2;
301 if (blotsPerEdge < 2)
306 distBetween = 2.0 / (blotsPerEdge - 1.0);
308 st->blotCount = 8 + (blotsPerEdge - 2) * 12;
309 st->blots = calloc (sizeof (Blot), st->blotCount);
312 /* define the corners */
313 for (i = -1; i < 2; i += 2)
315 for (j = -1; j < 2; j += 2)
317 for (k = -1; k < 2; k += 2)
319 initBlot (&st->blots[n], i, j, k);
325 /* define the edges */
326 for (i = 1; i < (blotsPerEdge - 1); i++)
328 FLOAT varEdge = distBetween * i - 1;
329 initBlot (&st->blots[n++], varEdge, -1, -1);
330 initBlot (&st->blots[n++], varEdge, 1, -1);
331 initBlot (&st->blots[n++], varEdge, -1, 1);
332 initBlot (&st->blots[n++], varEdge, 1, 1);
333 initBlot (&st->blots[n++], -1, varEdge, -1);
334 initBlot (&st->blots[n++], 1, varEdge, -1);
335 initBlot (&st->blots[n++], -1, varEdge, 1);
336 initBlot (&st->blots[n++], 1, varEdge, 1);
337 initBlot (&st->blots[n++], -1, -1, varEdge);
338 initBlot (&st->blots[n++], 1, -1, varEdge);
339 initBlot (&st->blots[n++], -1, 1, varEdge);
340 initBlot (&st->blots[n++], 1, 1, varEdge);
343 scaleBlotsToRadius1 (st);
344 randomlyReorderBlots (st);
345 randomlyRotateBlots (st);
348 /* set up the initial array of blots to be a cylinder */
349 static void setupBlotsCylinder (struct state *st)
354 /* derive blotsPerEdge and blotsPerRing from blotCount, but then do the
355 * reverse since roundoff may have changed blotCount */
356 FLOAT reqRoot = sqrt ((FLOAT) st->requestedBlotCount);
357 int blotsPerRing = ceil (RAND_FLOAT_PM1 * reqRoot) / 2 + reqRoot;
358 int blotsPerEdge = st->requestedBlotCount / blotsPerRing;
360 if (blotsPerRing < 2)
365 if (blotsPerEdge < 2)
370 distBetween = 2.0 / (blotsPerEdge - 1);
372 st->blotCount = blotsPerEdge * blotsPerRing;
373 st->blots = calloc (sizeof (Blot), st->blotCount);
376 /* define the edges */
377 for (i = 0; i < blotsPerRing; i++)
379 FLOAT x = sin (2 * M_PI / blotsPerRing * i);
380 FLOAT y = cos (2 * M_PI / blotsPerRing * i);
381 for (j = 0; j < blotsPerEdge; j++)
383 initBlot (&st->blots[n], x, y, j * distBetween - 1);
388 scaleBlotsToRadius1 (st);
389 randomlyReorderBlots (st);
390 randomlyRotateBlots (st);
393 /* set up the initial array of blots to be a squiggle */
394 static void setupBlotsSquiggle (struct state *st)
396 FLOAT x, y, z, xv, yv, zv, len;
397 int minCoor, maxCoor;
400 st->blotCount = st->requestedBlotCount;
401 st->blots = calloc (sizeof (Blot), st->blotCount);
403 maxCoor = (int) (RAND_FLOAT_01 * 5) + 1;
413 len = sqrt (xv * xv + yv * yv + zv * zv);
418 for (n = 0; n < st->blotCount; n++)
420 FLOAT newx, newy, newz;
421 initBlot (&st->blots[n], x, y, z);
425 xv += RAND_FLOAT_PM1 * 0.1;
426 yv += RAND_FLOAT_PM1 * 0.1;
427 zv += RAND_FLOAT_PM1 * 0.1;
428 len = sqrt (xv * xv + yv * yv + zv * zv);
437 if ( (newx >= minCoor) && (newx <= maxCoor)
438 && (newy >= minCoor) && (newy <= maxCoor)
439 && (newz >= minCoor) && (newz <= maxCoor))
450 scaleBlotsToRadius1 (st);
451 randomlyReorderBlots (st);
454 /* set up the initial array of blots to be near the corners of a
455 * cube, distributed slightly */
456 static void setupBlotsCubeCorners (struct state *st)
460 st->blotCount = st->requestedBlotCount;
461 st->blots = calloc (sizeof (Blot), st->blotCount);
463 for (n = 0; n < st->blotCount; n++)
465 FLOAT x = rint (RAND_FLOAT_01) * 2 - 1;
466 FLOAT y = rint (RAND_FLOAT_01) * 2 - 1;
467 FLOAT z = rint (RAND_FLOAT_01) * 2 - 1;
469 x += RAND_FLOAT_PM1 * 0.3;
470 y += RAND_FLOAT_PM1 * 0.3;
471 z += RAND_FLOAT_PM1 * 0.3;
473 initBlot (&st->blots[n], x, y, z);
476 scaleBlotsToRadius1 (st);
477 randomlyRotateBlots (st);
480 /* set up the initial array of blots to be randomly distributed
481 * on the surface of a tetrahedron */
482 static void setupBlotsTetrahedron (struct state *st)
484 /* table of corners of the tetrahedron */
485 static const FLOAT cor[4][3] = { { 0.0, 1.0, 0.0 },
486 { -0.75, -0.5, -0.433013 },
487 { 0.0, -0.5, 0.866025 },
488 { 0.75, -0.5, -0.433013 } };
492 /* derive blotsPerSurface from blotCount, but then do the reverse
493 * since roundoff may have changed blotCount */
494 int blotsPerSurface = st->requestedBlotCount / 4;
496 st->blotCount = blotsPerSurface * 4;
497 st->blots = calloc (sizeof (Blot), st->blotCount);
499 for (n = 0; n < st->blotCount; n += 4)
501 /* pick a random point on a unit right triangle */
502 FLOAT rawx = RAND_FLOAT_01;
503 FLOAT rawy = RAND_FLOAT_01;
505 if ((rawx + rawy) > 1)
507 /* swap coords into place */
508 FLOAT t = 1.0 - rawx;
513 /* translate the point to be on each of the surfaces */
514 for (c = 0; c < 4; c++)
518 int c1 = (c + 1) % 4;
519 int c2 = (c + 2) % 4;
521 x = (cor[c1][0] - cor[c][0]) * rawx +
522 (cor[c2][0] - cor[c][0]) * rawy +
525 y = (cor[c1][1] - cor[c][1]) * rawx +
526 (cor[c2][1] - cor[c][1]) * rawy +
529 z = (cor[c1][2] - cor[c][2]) * rawx +
530 (cor[c2][2] - cor[c][2]) * rawy +
533 initBlot (&st->blots[n + c], x, y, z);
537 randomlyRotateBlots (st);
540 /* set up the initial array of blots to be an almost-evenly-distributed
542 static void setupBlotsSheet (struct state *st)
546 int blotsPerDimension = floor (sqrt (st->requestedBlotCount));
549 if (blotsPerDimension < 2)
551 blotsPerDimension = 2;
554 spaceBetween = 2.0 / (blotsPerDimension - 1);
556 st->blotCount = blotsPerDimension * blotsPerDimension;
557 st->blots = calloc (sizeof (Blot), st->blotCount);
559 for (x = 0; x < blotsPerDimension; x++)
561 for (y = 0; y < blotsPerDimension; y++)
563 FLOAT x1 = x * spaceBetween - 1.0;
564 FLOAT y1 = y * spaceBetween - 1.0;
567 x1 += RAND_FLOAT_PM1 * spaceBetween / 3;
568 y1 += RAND_FLOAT_PM1 * spaceBetween / 3;
569 z1 += RAND_FLOAT_PM1 * spaceBetween / 2;
571 initBlot (&st->blots[x + y * blotsPerDimension], x1, y1, z1);
575 scaleBlotsToRadius1 (st);
576 randomlyReorderBlots (st);
577 randomlyRotateBlots (st);
580 /* set up the initial array of blots to be a swirlycone */
581 static void setupBlotsSwirlyCone (struct state *st)
583 FLOAT radSpace = 1.0 / (st->requestedBlotCount - 1);
584 FLOAT zSpace = radSpace * 2;
585 FLOAT rotAmt = RAND_FLOAT_PM1 * M_PI / 10;
590 st->blotCount = st->requestedBlotCount;
591 st->blots = calloc (sizeof (Blot), st->blotCount);
593 for (n = 0; n < st->blotCount; n++)
595 FLOAT radius = n * radSpace;
596 FLOAT x = cos (rot) * radius;
597 FLOAT y = sin (rot) * radius;
598 FLOAT z = n * zSpace - 1.0;
601 initBlot (&st->blots[n], x, y, z);
604 scaleBlotsToRadius1 (st);
605 randomlyReorderBlots (st);
606 randomlyRotateBlots (st);
609 /* forward declaration for recursive use immediately below */
610 static void setupBlots (struct state *st);
612 /* set up the blots to be two of the other choices, placed next to
614 static void setupBlotsDuo (struct state *st)
616 int origRequest = st->requestedBlotCount;
617 FLOAT tx, ty, tz, radius;
618 Blot *blots1, *blots2;
622 if (st->requestedBlotCount < 15)
624 /* special case bottom-out */
625 setupBlotsSphere (st);
632 radius = sqrt (tx * tx + ty * ty + tz * tz);
637 /* recursive call to setup set 1 */
638 st->requestedBlotCount = origRequest / 2;
641 if (st->blotCount >= origRequest)
643 /* return immediately if this satisfies the original count request */
644 st->requestedBlotCount = origRequest;
649 count1 = st->blotCount;
653 /* translate to new position */
654 for (n = 0; n < count1; n++)
661 /* recursive call to setup set 2 */
662 st->requestedBlotCount = origRequest - count1;
665 count2 = st->blotCount;
667 /* translate to new position */
668 for (n = 0; n < count2; n++)
675 /* combine the two arrays */
676 st->blotCount = count1 + count2;
677 st->blots = calloc (sizeof (Blot), st->blotCount);
678 memcpy (&st->blots[0], blots1, sizeof (Blot) * count1);
679 memcpy (&st->blots[count1], blots2, sizeof (Blot) * count2);
683 scaleBlotsToRadius1 (st);
684 randomlyReorderBlots (st);
686 /* restore the original requested count, for future iterations */
687 st->requestedBlotCount = origRequest;
696 /* free the blots, in preparation for a new shape */
697 static void freeBlots (struct state *st)
699 if (st->blots != NULL)
705 if (st->segsToErase != NULL)
707 free (st->segsToErase);
708 st->segsToErase = NULL;
711 if (st->segsToDraw != NULL)
713 free (st->segsToDraw);
714 st->segsToDraw = NULL;
718 /* set up the initial arrays of blots */
719 static void setupBlots (struct state *st)
721 int which = RAND_FLOAT_01 * 11;
731 setupBlotsSphere (st);
734 setupBlotsCylinder (st);
737 setupBlotsSquiggle (st);
740 setupBlotsCubeCorners (st);
743 setupBlotsTetrahedron (st);
746 setupBlotsSheet (st);
749 setupBlotsSwirlyCone (st);
759 /* set up the segments arrays */
760 static void setupSegs (struct state *st)
762 /* there are blotShapeCount - 1 line segments per blot */
763 st->segCount = st->blotCount * (blotShapeCount - 1);
764 st->segsToErase = calloc (sizeof (LineSegment), st->segCount);
765 st->segsToDraw = calloc (sizeof (LineSegment), st->segCount);
774 /* set up the colormap */
775 static void setupColormap (struct state *st, XWindowAttributes *xgwa)
779 XColor *colors = (XColor *) calloc (sizeof (XColor), st->colorCount + 1);
781 unsigned short r, g, b;
783 double s1, s2, v1, v2;
785 r = RAND_FLOAT_01 * 0x10000;
786 g = RAND_FLOAT_01 * 0x10000;
787 b = RAND_FLOAT_01 * 0x10000;
788 rgb_to_hsv (r, g, b, &h1, &s1, &v1);
792 r = RAND_FLOAT_01 * 0x10000;
793 g = RAND_FLOAT_01 * 0x10000;
794 b = RAND_FLOAT_01 * 0x10000;
795 rgb_to_hsv (r, g, b, &h2, &s2, &v2);
799 colors[0].pixel = get_pixel_resource (st->dpy, xgwa->colormap,
800 "background", "Background");
802 make_color_ramp (st->dpy, xgwa->colormap, h1, s1, v1, h2, s2, v2,
803 colors + 1, &st->colorCount, False, True, False);
805 if (st->colorCount < 1)
807 fprintf (stderr, "%s: couldn't allocate any colors\n", progname);
811 st->gcs = (GC *) calloc (sizeof (GC), st->colorCount + 1);
813 for (n = 0; n <= st->colorCount; n++)
815 gcv.foreground = colors[n].pixel;
816 gcv.line_width = st->lineWidth;
817 st->gcs[n] = XCreateGC (st->dpy, st->window, GCForeground | GCLineWidth, &gcv);
826 * overall setup stuff
829 /* set up the system */
830 static void setup (struct state *st)
832 XWindowAttributes xgwa;
834 XGetWindowAttributes (st->dpy, st->window, &xgwa);
836 st->windowWidth = xgwa.width;
837 st->windowHeight = xgwa.height;
838 st->centerX = st->windowWidth / 2;
839 st->centerY = st->windowHeight / 2;
840 st->baseScale = (xgwa.height < xgwa.width) ? xgwa.height : xgwa.width;
842 if (st->doubleBuffer)
844 st->drawable = XCreatePixmap (st->dpy, st->window, xgwa.width, xgwa.height,
849 st->drawable = st->window;
852 setupColormap (st, &xgwa);
856 /* set up the initial rotation, scale, and light values as random, but
857 * with the targets equal to where it is */
858 st->xRot = st->xRotTarget = RAND_FLOAT_01 * M_PI;
859 st->yRot = st->yRotTarget = RAND_FLOAT_01 * M_PI;
860 st->zRot = st->zRotTarget = RAND_FLOAT_01 * M_PI;
861 st->curScale = st->scaleTarget = RAND_FLOAT_01 * (st->maxScale - st->minScale) + st->minScale;
862 st->lightX = st->lightXTarget = RAND_FLOAT_PM1;
863 st->lightY = st->lightYTarget = RAND_FLOAT_PM1;
864 st->lightZ = st->lightZTarget = RAND_FLOAT_PM1;
866 st->itersTillNext = RAND_FLOAT_01 * st->maxIters;
875 /* "render" the blots into segsToDraw, with the current rotation factors */
876 static void renderSegs (struct state *st)
881 /* rotation factors */
882 FLOAT sinX = sin (st->xRot);
883 FLOAT cosX = cos (st->xRot);
884 FLOAT sinY = sin (st->yRot);
885 FLOAT cosY = cos (st->yRot);
886 FLOAT sinZ = sin (st->zRot);
887 FLOAT cosZ = cos (st->zRot);
889 for (n = 0; n < st->blotCount; n++)
891 Blot *b = &st->blots[n];
899 FLOAT x1 = st->blots[n].x;
900 FLOAT y1 = st->blots[n].y;
901 FLOAT z1 = st->blots[n].z;
904 /* rotate on z axis */
905 x2 = x1 * cosZ - y1 * sinZ;
906 y2 = x1 * sinZ + y1 * cosZ;
909 /* rotate on x axis */
910 y1 = y2 * cosX - z2 * sinX;
911 z1 = y2 * sinX + z2 * cosX;
914 /* rotate on y axis */
915 z2 = z1 * cosY - x1 * sinY;
916 x2 = z1 * sinY + x1 * cosY;
919 /* the color to draw is based on the distance from the light of
920 * the post-rotation blot */
921 x1 = x2 - st->lightX;
922 y1 = y2 - st->lightY;
923 z1 = z2 - st->lightZ;
924 color = 1 + (x1 * x1 + y1 * y1 + z1 * z1) / 4 * st->colorCount;
925 if (color > st->colorCount)
927 color = st->colorCount;
930 /* set up the base screen coordinates for drawing */
931 baseX = x2 / 2 * st->baseScale * st->curScale + st->centerX + st->centerXOff;
932 baseY = y2 / 2 * st->baseScale * st->curScale + st->centerY + st->centerYOff;
934 radius = (z2 + 1) / 2 * (st->maxRadius - st->minRadius) + st->minRadius;
936 for (i = 0; i < 3; i++)
938 for (j = 0; j < 3; j++)
941 ((i - 1) + (b->xoff[i][j] * st->maxNerveRadius)) * radius;
943 ((j - 1) + (b->yoff[i][j] * st->maxNerveRadius)) * radius;
947 for (i = 1; i < blotShapeCount; i++)
949 st->segsToDraw[m].gc = st->gcs[color];
950 st->segsToDraw[m].x1 = x[blotShape[i-1].x + 1][blotShape[i-1].y + 1];
951 st->segsToDraw[m].y1 = y[blotShape[i-1].x + 1][blotShape[i-1].y + 1];
952 st->segsToDraw[m].x2 = x[blotShape[i].x + 1][blotShape[i].y + 1];
953 st->segsToDraw[m].y2 = y[blotShape[i].x + 1][blotShape[i].y + 1];
959 /* update blots, adjusting the offsets and rotation factors. */
960 static void updateWithFeeling (struct state *st)
964 /* pick a new model if the time is right */
966 if (st->itersTillNext < 0)
968 st->itersTillNext = RAND_FLOAT_01 * st->maxIters;
974 /* update the rotation factors by moving them a bit toward the targets */
975 st->xRot = st->xRot + (st->xRotTarget - st->xRot) * st->iterAmt;
976 st->yRot = st->yRot + (st->yRotTarget - st->yRot) * st->iterAmt;
977 st->zRot = st->zRot + (st->zRotTarget - st->zRot) * st->iterAmt;
979 /* similarly the scale factor */
980 st->curScale = st->curScale + (st->scaleTarget - st->curScale) * st->iterAmt;
982 /* and similarly the light position */
983 st->lightX = st->lightX + (st->lightXTarget - st->lightX) * st->iterAmt;
984 st->lightY = st->lightY + (st->lightYTarget - st->lightY) * st->iterAmt;
985 st->lightZ = st->lightZ + (st->lightZTarget - st->lightZ) * st->iterAmt;
987 /* for each blot... */
988 for (n = 0; n < st->blotCount; n++)
990 /* add a bit of random jitter to xoff/yoff */
991 for (i = 0; i < 3; i++)
993 for (j = 0; j < 3; j++)
997 newOff = st->blots[n].xoff[i][j] + RAND_FLOAT_PM1 * st->nervousness;
998 if (newOff < -1) newOff = -(newOff + 1) - 1;
999 else if (newOff > 1) newOff = -(newOff - 1) + 1;
1000 st->blots[n].xoff[i][j] = newOff;
1002 newOff = st->blots[n].yoff[i][j] + RAND_FLOAT_PM1 * st->nervousness;
1003 if (newOff < -1) newOff = -(newOff + 1) - 1;
1004 else if (newOff > 1) newOff = -(newOff - 1) + 1;
1005 st->blots[n].yoff[i][j] = newOff;
1010 /* depending on random chance, update one or more factors */
1011 if (RAND_FLOAT_01 <= st->eventChance)
1013 int which = RAND_FLOAT_01 * 14;
1018 st->xRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
1023 st->yRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
1028 st->zRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
1033 st->xRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
1034 st->yRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
1039 st->xRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
1040 st->zRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
1045 st->yRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
1046 st->zRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
1051 st->xRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
1052 st->yRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
1053 st->zRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
1058 st->centerXOff = RAND_FLOAT_PM1 * st->maxRadius;
1063 st->centerYOff = RAND_FLOAT_PM1 * st->maxRadius;
1068 st->centerXOff = RAND_FLOAT_PM1 * st->maxRadius;
1069 st->centerYOff = RAND_FLOAT_PM1 * st->maxRadius;
1075 RAND_FLOAT_01 * (st->maxScale - st->minScale) + st->minScale;
1081 RAND_FLOAT_01 * (st->maxScale - st->minScale) + st->minScale;
1086 st->lightX = RAND_FLOAT_PM1;
1087 st->lightY = RAND_FLOAT_PM1;
1088 st->lightZ = RAND_FLOAT_PM1;
1093 st->lightXTarget = RAND_FLOAT_PM1;
1094 st->lightYTarget = RAND_FLOAT_PM1;
1095 st->lightZTarget = RAND_FLOAT_PM1;
1102 /* erase segsToErase and draw segsToDraw */
1103 static void eraseAndDraw (struct state *st)
1107 if (st->doubleBuffer)
1108 XFillRectangle (st->dpy, st->drawable, st->gcs[0], 0, 0,
1109 st->windowWidth, st->windowHeight);
1111 XClearWindow (st->dpy, st->drawable);
1113 for (n = 0; n < st->segCount; n++)
1115 LineSegment *seg = &st->segsToErase[n];
1116 #ifdef HAVE_COCOA /* Don't second-guess Quartz's double-buffering */
1117 XDrawLine (st->dpy, st->drawable, st->gcs[0],
1118 seg->x1, seg->y1, seg->x2, seg->y2);
1120 seg = &st->segsToDraw[n];
1121 XDrawLine (st->dpy, st->drawable, seg->gc,
1122 seg->x1, seg->y1, seg->x2, seg->y2);
1125 if (st->doubleBuffer)
1127 XCopyArea (st->dpy, st->drawable, st->window, st->gcs[0], 0, 0,
1128 st->windowWidth, st->windowHeight, 0, 0);
1132 /* do one iteration */
1133 static unsigned long
1134 nerverot_draw (Display *dpy, Window win, void *closure)
1136 struct state *st = (struct state *) closure;
1137 /* switch segsToErase and segsToDraw */
1138 LineSegment *temp = st->segsToDraw;
1139 st->segsToDraw = st->segsToErase;
1140 st->segsToErase = temp;
1142 /* update the model */
1143 updateWithFeeling (st);
1145 /* render new segments */
1148 /* erase old segments and draw new ones */
1154 /* initialize the user-specifiable params */
1155 static void initParams (struct state *st)
1159 st->delay = get_integer_resource (st->dpy, "delay", "Delay");
1162 fprintf (stderr, "error: delay must be at least 0\n");
1166 st->maxIters = get_integer_resource (st->dpy, "maxIters", "Integer");
1167 if (st->maxIters < 0)
1169 fprintf (stderr, "error: maxIters must be at least 0\n");
1173 st->doubleBuffer = get_boolean_resource (st->dpy, "doubleBuffer", "Boolean");
1175 # ifdef HAVE_COCOA /* Don't second-guess Quartz's double-buffering */
1176 st->doubleBuffer = False;
1179 st->requestedBlotCount = get_integer_resource (st->dpy, "count", "Count");
1180 if (st->requestedBlotCount <= 0)
1182 fprintf (stderr, "error: count must be at least 0\n");
1186 st->colorCount = get_integer_resource (st->dpy, "colors", "Colors");
1187 if (st->colorCount <= 0)
1189 fprintf (stderr, "error: colors must be at least 1\n");
1193 st->lineWidth = get_integer_resource (st->dpy, "lineWidth", "LineWidth");
1194 if (st->lineWidth < 0)
1196 fprintf (stderr, "error: line width must be at least 0\n");
1200 st->nervousness = get_float_resource (st->dpy, "nervousness", "Float");
1201 if ((st->nervousness < 0) || (st->nervousness > 1))
1203 fprintf (stderr, "error: nervousness must be in the range 0..1\n");
1207 st->maxNerveRadius = get_float_resource (st->dpy, "maxNerveRadius", "Float");
1208 if ((st->maxNerveRadius < 0) || (st->maxNerveRadius > 1))
1210 fprintf (stderr, "error: maxNerveRadius must be in the range 0..1\n");
1214 st->eventChance = get_float_resource (st->dpy, "eventChance", "Float");
1215 if ((st->eventChance < 0) || (st->eventChance > 1))
1217 fprintf (stderr, "error: eventChance must be in the range 0..1\n");
1221 st->iterAmt = get_float_resource (st->dpy, "iterAmt", "Float");
1222 if ((st->iterAmt < 0) || (st->iterAmt > 1))
1224 fprintf (stderr, "error: iterAmt must be in the range 0..1\n");
1228 st->minScale = get_float_resource (st->dpy, "minScale", "Float");
1229 if ((st->minScale < 0) || (st->minScale > 10))
1231 fprintf (stderr, "error: minScale must be in the range 0..10\n");
1235 st->maxScale = get_float_resource (st->dpy, "maxScale", "Float");
1236 if ((st->maxScale < 0) || (st->maxScale > 10))
1238 fprintf (stderr, "error: maxScale must be in the range 0..10\n");
1242 if (st->maxScale < st->minScale)
1244 fprintf (stderr, "error: maxScale must be >= minScale\n");
1248 st->minRadius = get_integer_resource (st->dpy, "minRadius", "Integer");
1249 if ((st->minRadius < 1) || (st->minRadius > 100))
1251 fprintf (stderr, "error: minRadius must be in the range 1..100\n");
1255 st->maxRadius = get_integer_resource (st->dpy, "maxRadius", "Integer");
1256 if ((st->maxRadius < 1) || (st->maxRadius > 100))
1258 fprintf (stderr, "error: maxRadius must be in the range 1..100\n");
1262 if (st->maxRadius < st->minRadius)
1264 fprintf (stderr, "error: maxRadius must be >= minRadius\n");
1275 nerverot_init (Display *dpy, Window window)
1277 struct state *st = (struct state *) calloc (1, sizeof(*st));
1279 st->window = window;
1284 /* make a valid set to erase at first */
1290 nerverot_reshape (Display *dpy, Window window, void *closure,
1291 unsigned int w, unsigned int h)
1296 nerverot_event (Display *dpy, Window window, void *closure, XEvent *event)
1302 nerverot_free (Display *dpy, Window window, void *closure)
1304 struct state *st = (struct state *) closure;
1310 static const char *nerverot_defaults [] = {
1311 ".background: black",
1312 ".foreground: white",
1317 "*doubleBuffer: false",
1318 "*eventChance: 0.2",
1325 "*maxNerveRadius: 0.7",
1326 "*nervousness: 0.3",
1330 static XrmOptionDescRec nerverot_options [] = {
1331 { "-count", ".count", XrmoptionSepArg, 0 },
1332 { "-colors", ".colors", XrmoptionSepArg, 0 },
1333 { "-delay", ".delay", XrmoptionSepArg, 0 },
1334 { "-max-iters", ".maxIters", XrmoptionSepArg, 0 },
1335 { "-db", ".doubleBuffer", XrmoptionNoArg, "true" },
1336 { "-no-db", ".doubleBuffer", XrmoptionNoArg, "false" },
1337 { "-event-chance", ".eventChance", XrmoptionSepArg, 0 },
1338 { "-iter-amt", ".iterAmt", XrmoptionSepArg, 0 },
1339 { "-line-width", ".lineWidth", XrmoptionSepArg, 0 },
1340 { "-min-scale", ".minScale", XrmoptionSepArg, 0 },
1341 { "-max-scale", ".maxScale", XrmoptionSepArg, 0 },
1342 { "-min-radius", ".minRadius", XrmoptionSepArg, 0 },
1343 { "-max-radius", ".maxRadius", XrmoptionSepArg, 0 },
1344 { "-max-nerve-radius", ".maxNerveRadius", XrmoptionSepArg, 0 },
1345 { "-nervousness", ".nervousness", XrmoptionSepArg, 0 },
1350 XSCREENSAVER_MODULE ("NerveRot", nerverot)