http://ftp.x.org/contrib/applications/xscreensaver-3.25.tar.gz
[xscreensaver] / hacks / nerverot.c
1 /* nerverot, nervous rotation of random thingies, v1.0
2  * by Dan Bornstein, danfuzz@milk.com
3  * Copyright (c) 2000 Dan Bornstein.
4  *
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 
11  * implied warranty.
12  *
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.
15  *
16  * Brief description of options/resources:
17  *
18  *   -fg <color>: foreground color
19  *   -bg <color>: background color
20  *   -delay <usec>: delay between frames
21  *   -event-chance <frac>: chance, per iteration, that an interesting event
22  *     will happen (range 0..1)
23  *   -iter-amt <frac>: amount, per iteration, to move towards rotation and
24  *     scale targets (range 0..1)
25  *   -count <n>: number of blots
26  *   -colors <n>: number of colors to use
27  *   -lineWidth <n>: width of lines (0 means an optimized thin line)
28  *   -nervousness <frac>: amount of nervousness (range 0..1)
29  *   -min-scale <frac>: minimum scale of drawing as fraction of base scale
30  *     (which is the minumum of the width or height of the screen) (range
31  *     0..10)
32  *   -max-scale <frac>: maximum scale of drawing as fraction of base scale
33  *     (which is the minumum of the width or height of the screen) (range
34  *     0..10)
35  *   -min-radius <n>: minimum radius for drawing blots (range 1..100)
36  *   -max-radius <n>: maximum radius for drawing blots (range 1..100)
37  *   -max-nerve-radius <frac>: maximum nervousness radius (range 0..1)
38  */
39
40 #include <math.h>
41 #include "screenhack.h"
42
43 #define FLOAT double
44
45 /* random float in the range (-1..1) */
46 #define RAND_FLOAT_PM1 \
47         (((FLOAT) (random() & 0xffff)) / ((FLOAT) 0x10000) * 2 - 1)
48
49 /* random float in the range (0..1) */
50 #define RAND_FLOAT_01 \
51         (((FLOAT) (random() & 0xffff)) / ((FLOAT) 0x10000))
52
53
54
55 /* parameters that are user configurable */
56
57 /* number of blots */
58 static int requestedBlotCount;
59
60 /* delay (usec) between iterations */
61 int delay;
62
63 /* variability of xoff/yoff per iteration (0..1) */
64 static FLOAT nervousness;
65
66 /* max nervousness radius (0..1) */
67 static FLOAT maxNerveRadius;
68
69 /* chance per iteration that an event will happen */
70 static FLOAT eventChance;
71
72 /* fraction (0..1) towards rotation target or scale target to move each
73  * iteration */
74 static FLOAT iterAmt;
75
76 /* min and max scale for drawing, as fraction of baseScale */
77 static FLOAT minScale;
78 static FLOAT maxScale;
79
80 /* min and max radius of blot drawing */
81 static int minRadius;
82 static int maxRadius;
83
84 /* the number of colors to use */
85 static int colorCount;
86
87 /* width of lines */
88 static int lineWidth;
89
90
91
92 /* non-user-modifiable immutable definitions */
93
94 /* base scale factor for drawing, calculated as
95  * max(screenWidth,screenHeight) */
96 static int baseScale;
97
98 /* width and height of the window */
99 static int windowWidth;
100 static int windowHeight;
101
102 /* center position of the window */
103 static int centerX;
104 static int centerY;
105
106 static Display *display; /* the display to draw on */
107 static Window window;    /* the window to draw on */
108 static GC *gcs;          /* array of gcs, one per color used */
109
110
111
112 /* structure of the model */
113
114 /* each point-like thingy to draw is represented as a blot */
115 typedef struct blot_s
116 {
117     FLOAT x;           /* 3d x position (-1..1) */
118     FLOAT y;           /* 3d y position (-1..1) */
119     FLOAT z;           /* 3d z position (-1..1) */
120     FLOAT xoff[3][3];  /* display x offset per drawn point (-1..1) */
121     FLOAT yoff[3][3];  /* display x offset per drawn point (-1..1) */
122 } Blot;
123
124 /* each drawn line is represented as a LineSegment */
125 typedef struct linesegment_s
126 {
127     GC gc;
128     int x1;
129     int y1;
130     int x2;
131     int y2;
132 } LineSegment;
133
134 /* array of the blots in the model */
135 static Blot *blots = NULL;
136 static int blotCount;
137
138 /* each blot draws as a simple 2d shape with each coordinate as an int
139  * in the range (-1..1); this is the base shape */
140 static XPoint blotShape[] = { { 0, 0}, { 1, 0}, { 1, 1}, 
141                               { 0, 1}, {-1, 1}, {-1, 0}, 
142                               {-1,-1}, { 0,-1}, { 1,-1} };
143 static int blotShapeCount = sizeof (blotShape) / sizeof (XPoint);
144
145 /* two arrays of line segments; one for the ones to erase, and one for the
146  * ones to draw */
147 static int segCount;
148 static LineSegment *segsToDraw = NULL;
149 static LineSegment *segsToErase = NULL;
150
151 /* current rotation values per axis, scale factor, and light position */
152 static FLOAT xRot;
153 static FLOAT yRot;
154 static FLOAT zRot;
155 static FLOAT curScale;
156 static FLOAT lightX;
157 static FLOAT lightY;
158 static FLOAT lightZ;
159
160 /* target rotation values per axis, scale factor, and light position */
161 static FLOAT xRotTarget;
162 static FLOAT yRotTarget;
163 static FLOAT zRotTarget;
164 static FLOAT scaleTarget;
165 static FLOAT lightXTarget;
166 static FLOAT lightYTarget;
167 static FLOAT lightZTarget;
168
169 /* current absolute offsets from the center */
170 static int centerXOff = 0;
171 static int centerYOff = 0;
172
173 /* iterations until the model changes */
174 static int itersTillNext;
175
176
177
178 /*
179  * blot setup stuff
180  */
181
182 /* initialize a blot with the given coordinates and random display offsets */
183 static void initBlot (Blot *b, FLOAT x, FLOAT y, FLOAT z)
184 {
185     int i, j;
186
187     b->x = x;
188     b->y = y;
189     b->z = z;
190
191     for (i = 0; i < 3; i++)
192     {
193         for (j = 0; j < 3; j++)
194         {
195             b->xoff[i][j] = RAND_FLOAT_PM1;
196             b->yoff[i][j] = RAND_FLOAT_PM1;
197         }
198     }
199 }
200
201 /* scale the blots to have a max distance of 1 from the center */
202 static void scaleBlotsToRadius1 (void)
203 {
204     FLOAT max = 0.0;
205     int n;
206
207     for (n = 0; n < blotCount; n++)
208     {
209         FLOAT distSquare = 
210             blots[n].x * blots[n].x +
211             blots[n].y * blots[n].y +
212             blots[n].z * blots[n].z;
213         if (distSquare > max)
214         {
215             max = distSquare;
216         }
217     }
218
219     if (max == 0.0)
220     {
221         return;
222     }
223
224     max = sqrt (max);
225
226     for (n = 0; n < blotCount; n++)
227     {
228         blots[n].x /= max;
229         blots[n].y /= max;
230         blots[n].z /= max;
231     }
232 }
233
234 /* randomly reorder the blots */
235 static void randomlyReorderBlots (void)
236 {
237     int n;
238
239     for (n = 0; n < blotCount; n++)
240     {
241         int m = RAND_FLOAT_01 * (blotCount - n) + n;
242         Blot tmpBlot = blots[n];
243         blots[n] = blots[m];
244         blots[m] = tmpBlot;
245     }
246 }
247
248 /* set up the initial array of blots to be a at the edge of a sphere */
249 static void setupBlotsSphere (void)
250 {
251     int n;
252
253     blotCount = requestedBlotCount;
254     blots = calloc (sizeof (Blot), blotCount);
255
256     for (n = 0; n < blotCount; n++)
257     {
258         /* pick a spot, but reject if its radius is < 0.2 or > 1 to
259          * avoid scaling problems */
260         FLOAT x, y, z, radius;
261
262         for (;;)
263         {
264             x = RAND_FLOAT_PM1;
265             y = RAND_FLOAT_PM1;
266             z = RAND_FLOAT_PM1;
267
268             radius = sqrt (x * x + y * y + z * z);
269             if ((radius >= 0.2) && (radius <= 1.0))
270             {
271                 break;
272             }
273         }
274
275         x /= radius;
276         y /= radius;
277         z /= radius;
278
279         initBlot (&blots[n], x, y, z);
280     }
281
282 }
283
284 /* set up the initial array of blots to be a simple cube */
285 static void setupBlotsCube (void)
286 {
287     int i, j, k, n;
288
289     /* derive blotsPerEdge from blotCount, but then do the reverse
290      * since roundoff may have changed blotCount */
291     int blotsPerEdge = ((requestedBlotCount - 8) / 12) + 2;
292     FLOAT distBetween;
293
294     if (blotsPerEdge < 2)
295     {
296         blotsPerEdge = 2;
297     }
298
299     distBetween = 2.0 / (blotsPerEdge - 1.0);
300
301     blotCount = 8 + (blotsPerEdge - 2) * 12;
302     blots = calloc (sizeof (Blot), blotCount);
303     n = 0;
304
305     /* define the corners */
306     for (i = -1; i < 2; i += 2)
307     {
308         for (j = -1; j < 2; j += 2)
309         {
310             for (k = -1; k < 2; k += 2)
311             {
312                 initBlot (&blots[n], i, j, k);
313                 n++;
314             } 
315         }
316     }
317
318     /* define the edges */
319     for (i = 1; i < (blotsPerEdge - 1); i++)
320     {
321         FLOAT varEdge = distBetween * i - 1;
322         initBlot (&blots[n++], varEdge, -1, -1);
323         initBlot (&blots[n++], varEdge,  1, -1);
324         initBlot (&blots[n++], varEdge, -1,  1);
325         initBlot (&blots[n++], varEdge,  1,  1);
326         initBlot (&blots[n++], -1, varEdge, -1);
327         initBlot (&blots[n++],  1, varEdge, -1);
328         initBlot (&blots[n++], -1, varEdge,  1);
329         initBlot (&blots[n++],  1, varEdge,  1);
330         initBlot (&blots[n++], -1, -1, varEdge);
331         initBlot (&blots[n++],  1, -1, varEdge);
332         initBlot (&blots[n++], -1,  1, varEdge);
333         initBlot (&blots[n++],  1,  1, varEdge);
334     }
335
336     scaleBlotsToRadius1 ();
337     randomlyReorderBlots ();
338 }
339
340
341 /* set up the initial array of blots to be a cylinder */
342 static void setupBlotsCylinder (void)
343 {
344     int i, j, n;
345
346     /* derive blotsPerEdge from blotCount, but then do the reverse
347      * since roundoff may have changed blotCount */
348     int blotsPerEdge = requestedBlotCount / 32;
349     FLOAT distBetween;
350
351     if (blotsPerEdge < 2)
352     {
353         blotsPerEdge = 2;
354     }
355
356     distBetween = 2.0 / (blotsPerEdge - 1);
357
358     blotCount = blotsPerEdge * 32;
359     blots = calloc (sizeof (Blot), blotCount);
360     n = 0;
361
362     /* define the edges */
363     for (i = 0; i < 32; i++)
364     {
365         FLOAT x = sin (2 * M_PI / 32 * i);
366         FLOAT y = cos (2 * M_PI / 32 * i);
367         for (j = 0; j < blotsPerEdge; j++)
368         {
369             initBlot (&blots[n], x, y, j * distBetween - 1);
370             n++;
371         }
372     }
373
374     scaleBlotsToRadius1 ();
375     randomlyReorderBlots ();
376 }
377
378
379
380 /* set up the initial array of blots to be a squiggle */
381 static void setupBlotsSquiggle (void)
382 {
383     FLOAT x, y, z, xv, yv, zv, len;
384     int n;
385
386     blotCount = requestedBlotCount;
387     blots = calloc (sizeof (Blot), blotCount);
388
389     x = RAND_FLOAT_PM1;
390     y = RAND_FLOAT_PM1;
391     z = RAND_FLOAT_PM1;
392
393     xv = RAND_FLOAT_PM1;
394     yv = RAND_FLOAT_PM1;
395     zv = RAND_FLOAT_PM1;
396     len = sqrt (xv * xv + yv * yv + zv * zv);
397     xv /= len;
398     yv /= len;
399     zv /= len;
400     
401     for (n = 0; n < blotCount; n++)
402     {
403         FLOAT newx, newy, newz;
404         initBlot (&blots[n], x, y, z);
405
406         for (;;)
407         {
408             xv += RAND_FLOAT_PM1 * 0.1;
409             yv += RAND_FLOAT_PM1 * 0.1;
410             zv += RAND_FLOAT_PM1 * 0.1;
411             len = sqrt (xv * xv + yv * yv + zv * zv);
412             xv /= len;
413             yv /= len;
414             zv /= len;
415
416             newx = x + xv * 0.1;
417             newy = y + yv * 0.1;
418             newz = z + zv * 0.1;
419
420             if (   (newx >= -1) && (newx <= 1)
421                 && (newy >= -1) && (newy <= 1)
422                 && (newz >= -1) && (newz <= 1))
423             {
424                 break;
425             }
426         }
427
428         x = newx;
429         y = newy;
430         z = newz;
431     }
432
433     scaleBlotsToRadius1 ();
434     randomlyReorderBlots ();
435 }
436
437
438
439 /* free the blots, in preparation for a new shape */
440 static void freeBlots (void)
441 {
442     if (blots != NULL)
443     {
444         free (blots);
445         blots = NULL;
446     }
447
448     if (segsToErase != NULL)
449     {
450         free (segsToErase);
451         segsToErase = NULL;
452     }
453
454     if (segsToDraw != NULL)
455     {
456         free (segsToDraw);
457         segsToDraw = NULL;
458     }
459 }
460
461
462
463 /* set up the initial arrays of blots */
464 static void setupBlots (void)
465 {
466     int which = RAND_FLOAT_01 * 4;
467
468     freeBlots ();
469
470     switch (which)
471     {
472         case 0:
473             setupBlotsCube ();
474             break;
475         case 1:
476             setupBlotsSphere ();
477             break;
478         case 2:
479             setupBlotsCylinder ();
480             break;
481         case 3:
482             setupBlotsSquiggle ();
483             break;
484     }
485
486     /* there are blotShapeCount - 1 line segments per blot */
487     segCount = blotCount * (blotShapeCount - 1);
488     segsToErase = calloc (sizeof (LineSegment), segCount);
489     segsToDraw = calloc (sizeof (LineSegment), segCount);
490
491     /* erase the world */
492     XFillRectangle (display, window, gcs[0], 0, 0, windowWidth, windowHeight);
493 }
494
495
496
497 /*
498  * color setup stuff
499  */
500
501 /* set up the colormap */
502 static void setupColormap (XWindowAttributes *xgwa)
503 {
504     int n;
505     XGCValues gcv;
506     XColor *colors = (XColor *) calloc (sizeof (XColor), colorCount + 1);
507
508     unsigned short r, g, b;
509     int h1, h2;
510     double s1, s2, v1, v2;
511
512     r = RAND_FLOAT_01 * 0x10000;
513     g = RAND_FLOAT_01 * 0x10000;
514     b = RAND_FLOAT_01 * 0x10000;
515     rgb_to_hsv (r, g, b, &h1, &s1, &v1);
516     v1 = 1.0;
517     s1 = 1.0;
518
519     r = RAND_FLOAT_01 * 0x10000;
520     g = RAND_FLOAT_01 * 0x10000;
521     b = RAND_FLOAT_01 * 0x10000;
522     rgb_to_hsv (r, g, b, &h2, &s2, &v2);
523     s2 = 0.7;
524     v2 = 0.7;
525     
526     colors[0].pixel = get_pixel_resource ("background", "Background",
527                                           display, xgwa->colormap);
528     
529     make_color_ramp (display, xgwa->colormap, h1, s1, v1, h2, s2, v2,
530                      colors + 1, &colorCount, False, True, False);
531
532     if (colorCount < 1)
533     {
534         fprintf (stderr, "%s: couldn't allocate any colors\n", progname);
535         exit (-1);
536     }
537     
538     gcs = (GC *) calloc (sizeof (GC), colorCount + 1);
539
540     for (n = 0; n <= colorCount; n++) 
541     {
542         gcv.foreground = colors[n].pixel;
543         gcv.line_width = lineWidth;
544         gcs[n] = XCreateGC (display, window, GCForeground | GCLineWidth, &gcv);
545     }
546
547     free (colors);
548 }
549
550
551
552 /*
553  * overall setup stuff
554  */
555
556 /* set up the system */
557 static void setup (void)
558 {
559     XWindowAttributes xgwa;
560
561     XGetWindowAttributes (display, window, &xgwa);
562
563     windowWidth = xgwa.width;
564     windowHeight = xgwa.height;
565     centerX = windowWidth / 2;
566     centerY = windowHeight / 2;
567     baseScale = (xgwa.height < xgwa.width) ? xgwa.height : xgwa.width;
568
569     setupColormap (&xgwa);
570     setupBlots ();
571
572     /* set up the initial rotation, scale, and light values as random, but
573      * with the targets equal to where it is */
574     xRot = xRotTarget = RAND_FLOAT_01 * M_PI;
575     yRot = yRotTarget = RAND_FLOAT_01 * M_PI;
576     zRot = zRotTarget = RAND_FLOAT_01 * M_PI;
577     curScale = scaleTarget = RAND_FLOAT_01 * (maxScale - minScale) + minScale;
578     lightX = lightXTarget = RAND_FLOAT_PM1;
579     lightY = lightYTarget = RAND_FLOAT_PM1;
580     lightZ = lightZTarget = RAND_FLOAT_PM1;
581
582     itersTillNext = RAND_FLOAT_01 * 1234;
583 }
584
585
586
587 /*
588  * the simulation
589  */
590
591 /* "render" the blots into segsToDraw, with the current rotation factors */
592 static void renderSegs (void)
593 {
594     int n;
595     int m = 0;
596
597     /* rotation factors */
598     FLOAT sinX = sin (xRot);
599     FLOAT cosX = cos (xRot);
600     FLOAT sinY = sin (yRot);
601     FLOAT cosY = cos (yRot);
602     FLOAT sinZ = sin (zRot);
603     FLOAT cosZ = cos (zRot);
604
605     for (n = 0; n < blotCount; n++)
606     {
607         Blot *b = &blots[n];
608         int i, j;
609         int baseX, baseY;
610         FLOAT radius;
611         int x[3][3];
612         int y[3][3];
613         int color;
614
615         FLOAT x1 = blots[n].x;
616         FLOAT y1 = blots[n].y;
617         FLOAT z1 = blots[n].z;
618         FLOAT x2, y2, z2;
619
620         /* rotate on z axis */
621         x2 = x1 * cosZ - y1 * sinZ;
622         y2 = x1 * sinZ + y1 * cosZ;
623         z2 = z1;
624
625         /* rotate on x axis */
626         y1 = y2 * cosX - z2 * sinX;
627         z1 = y2 * sinX + z2 * cosX;
628         x1 = x2;
629
630         /* rotate on y axis */
631         z2 = z1 * cosY - x1 * sinY;
632         x2 = z1 * sinY + x1 * cosY;
633         y2 = y1;
634
635         /* the color to draw is based on the distance from the light of
636          * the post-rotation blot */
637         x1 = x2 - lightX;
638         y1 = y2 - lightY;
639         z1 = z2 - lightZ;
640         color = 1 + (x1 * x1 + y1 * y1 + z1 * z1) / 4 * colorCount;
641         if (color > colorCount)
642         {
643             color = colorCount;
644         }
645
646         /* set up the base screen coordinates for drawing */
647         baseX = x2 / 2 * baseScale * curScale + centerX + centerXOff;
648         baseY = y2 / 2 * baseScale * curScale + centerY + centerYOff;
649         
650         radius = (z2 + 1) / 2 * (maxRadius - minRadius) + minRadius;
651
652         for (i = 0; i < 3; i++)
653         {
654             for (j = 0; j < 3; j++)
655             {
656                 x[i][j] = baseX + 
657                     ((i - 1) + (b->xoff[i][j] * maxNerveRadius)) * radius;
658                 y[i][j] = baseY + 
659                     ((j - 1) + (b->yoff[i][j] * maxNerveRadius)) * radius;
660             }
661         }
662
663         for (i = 1; i < blotShapeCount; i++)
664         {
665             segsToDraw[m].gc = gcs[color];
666             segsToDraw[m].x1 = x[blotShape[i-1].x + 1][blotShape[i-1].y + 1];
667             segsToDraw[m].y1 = y[blotShape[i-1].x + 1][blotShape[i-1].y + 1];
668             segsToDraw[m].x2 = x[blotShape[i].x   + 1][blotShape[i].y   + 1];
669             segsToDraw[m].y2 = y[blotShape[i].x   + 1][blotShape[i].y   + 1];
670             m++;
671         }
672     }
673 }
674
675 /* update blots, adjusting the offsets and rotation factors. */
676 static void updateWithFeeling (void)
677 {
678     int n, i, j;
679
680     /* pick a new model if the time is right */
681     itersTillNext--;
682     if (itersTillNext < 0)
683     {
684         itersTillNext = RAND_FLOAT_01 * 1234;
685         setupBlots ();
686         renderSegs ();
687     }
688
689     /* update the rotation factors by moving them a bit toward the targets */
690     xRot = xRot + (xRotTarget - xRot) * iterAmt; 
691     yRot = yRot + (yRotTarget - yRot) * iterAmt;
692     zRot = zRot + (zRotTarget - zRot) * iterAmt;
693
694     /* similarly the scale factor */
695     curScale = curScale + (scaleTarget - curScale) * iterAmt;
696
697     /* and similarly the light position */
698     lightX = lightX + (lightXTarget - lightX) * iterAmt; 
699     lightY = lightY + (lightYTarget - lightY) * iterAmt; 
700     lightZ = lightZ + (lightZTarget - lightZ) * iterAmt; 
701
702     /* for each blot... */
703     for (n = 0; n < blotCount; n++)
704     {
705         /* add a bit of random jitter to xoff/yoff */
706         for (i = 0; i < 3; i++)
707         {
708             for (j = 0; j < 3; j++)
709             {
710                 FLOAT newOff;
711
712                 newOff = blots[n].xoff[i][j] + RAND_FLOAT_PM1 * nervousness;
713                 if (newOff < -1) newOff = -(newOff + 1) - 1;
714                 else if (newOff > 1) newOff = -(newOff - 1) + 1;
715                 blots[n].xoff[i][j] = newOff;
716
717                 newOff = blots[n].yoff[i][j] + RAND_FLOAT_PM1 * nervousness;
718                 if (newOff < -1) newOff = -(newOff + 1) - 1;
719                 else if (newOff > 1) newOff = -(newOff - 1) + 1;
720                 blots[n].yoff[i][j] = newOff;
721             }
722         }
723     }
724
725     /* depending on random chance, update one or more factors */
726     if (RAND_FLOAT_01 <= eventChance)
727     {
728         int which = RAND_FLOAT_01 * 14;
729         switch (which)
730         {
731             case 0:
732             {
733                 xRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
734                 break;
735             }
736             case 1:
737             {
738                 yRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
739                 break;
740             }
741             case 2:
742             {
743                 zRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
744                 break;
745             }
746             case 3:
747             {
748                 xRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
749                 yRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
750                 break;
751             }
752             case 4:
753             {
754                 xRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
755                 zRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
756                 break;
757             }
758             case 5:
759             {
760                 yRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
761                 zRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
762                 break;
763             }
764             case 6:
765             {
766                 xRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
767                 yRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
768                 zRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
769                 break;
770             }
771             case 7:
772             {
773                 centerXOff = RAND_FLOAT_PM1 * maxRadius / 2;
774                 break;
775             }
776             case 8:
777             {
778                 centerYOff = RAND_FLOAT_PM1 * maxRadius / 2;
779                 break;
780             }
781             case 9:
782             {
783                 centerXOff = RAND_FLOAT_PM1 * maxRadius / 2;
784                 centerYOff = RAND_FLOAT_PM1 * maxRadius / 2;
785                 break;
786             }
787             case 10:
788             {
789                 scaleTarget = 
790                     RAND_FLOAT_01 * (maxScale - minScale) + minScale;
791                 break;
792             }
793             case 11:
794             {
795                 curScale = 
796                     RAND_FLOAT_01 * (maxScale - minScale) + minScale;
797                 break;
798             }
799             case 12:
800             {
801                 lightX = RAND_FLOAT_PM1;
802                 lightY = RAND_FLOAT_PM1;
803                 lightZ = RAND_FLOAT_PM1;
804                 break;
805             }
806             case 13:
807             {
808                 lightXTarget = RAND_FLOAT_PM1;
809                 lightYTarget = RAND_FLOAT_PM1;
810                 lightZTarget = RAND_FLOAT_PM1;
811                 break;
812             }
813         }
814     }
815 }
816
817 /* erase segsToErase and draw segsToDraw */
818 static void eraseAndDraw (void)
819 {
820     int n;
821
822     for (n = 0; n < segCount; n++)
823     {
824         LineSegment *seg = &segsToErase[n];
825         XDrawLine (display, window, gcs[0], 
826                    seg->x1, seg->y1, seg->x2, seg->y2);
827         seg = &segsToDraw[n];
828         XDrawLine (display, window, seg->gc,
829                    seg->x1, seg->y1, seg->x2, seg->y2);
830     }
831 }
832
833 /* do one iteration */
834 static void oneIteration (void)
835 {
836     /* switch segsToErase and segsToDraw */
837     LineSegment *temp = segsToDraw;
838     segsToDraw = segsToErase;
839     segsToErase = temp;
840
841     /* update the model */
842     updateWithFeeling ();
843
844     /* render new segments */
845     renderSegs ();
846
847     /* erase old segments and draw new ones */
848     eraseAndDraw ();
849 }
850
851 char *progclass = "NerveRot";
852
853 char *defaults [] = {
854     ".background:       black",
855     ".foreground:       white",
856     "*count:            250",
857     "*colors:           4",
858     "*delay:            10000",
859     "*eventChance:      0.2",
860     "*iterAmt:          0.01",
861     "*lineWidth:        0",
862     "*minScale:         0.6",
863     "*maxScale:         1.75",
864     "*minRadius:        3",
865     "*maxRadius:        25",
866     "*maxNerveRadius:   0.7",
867     "*nervousness:      0.3",
868     0
869 };
870
871 XrmOptionDescRec options [] = {
872   { "-count",            ".count",          XrmoptionSepArg, 0 },
873   { "-colors",           ".colors",         XrmoptionSepArg, 0 },
874   { "-cube",             ".cube",           XrmoptionNoArg,  "true" },
875   { "-delay",            ".delay",          XrmoptionSepArg, 0 },
876   { "-event-chance",     ".eventChance",    XrmoptionSepArg, 0 },
877   { "-iter-amt",         ".iterAmt",        XrmoptionSepArg, 0 },
878   { "-line-width",       ".lineWidth",      XrmoptionSepArg, 0 },
879   { "-min-scale",        ".minScale",       XrmoptionSepArg, 0 },
880   { "-max-scale",        ".maxScale",       XrmoptionSepArg, 0 },
881   { "-min-radius",       ".minRadius",      XrmoptionSepArg, 0 },
882   { "-max-radius",       ".maxRadius",      XrmoptionSepArg, 0 },
883   { "-max-nerve-radius", ".maxNerveRadius", XrmoptionSepArg, 0 },
884   { "-nervousness",      ".nervousness",    XrmoptionSepArg, 0 },
885   { 0, 0, 0, 0 }
886 };
887
888 /* initialize the user-specifiable params */
889 static void initParams (void)
890 {
891     int problems = 0;
892
893     delay = get_integer_resource ("delay", "Delay");
894     if (delay < 0)
895     {
896         fprintf (stderr, "error: delay must be at least 0\n");
897         problems = 1;
898     }
899
900     requestedBlotCount = get_integer_resource ("count", "Count");
901     if (requestedBlotCount <= 0)
902     {
903         fprintf (stderr, "error: count must be at least 0\n");
904         problems = 1;
905     }
906
907     colorCount = get_integer_resource ("colors", "Colors");
908     if (colorCount <= 0)
909     {
910         fprintf (stderr, "error: colors must be at least 1\n");
911         problems = 1;
912     }
913
914     lineWidth = get_integer_resource ("lineWidth", "LineWidth");
915     if (lineWidth < 0)
916     {
917         fprintf (stderr, "error: line width must be at least 0\n");
918         problems = 1;
919     }
920
921     nervousness = get_float_resource ("nervousness", "Float");
922     if ((nervousness < 0) || (nervousness > 1))
923     {
924         fprintf (stderr, "error: nervousness must be in the range 0..1\n");
925         problems = 1;
926     }
927
928     maxNerveRadius = get_float_resource ("maxNerveRadius", "Float");
929     if ((maxNerveRadius < 0) || (maxNerveRadius > 1))
930     {
931         fprintf (stderr, "error: maxNerveRadius must be in the range 0..1\n");
932         problems = 1;
933     }
934
935     eventChance = get_float_resource ("eventChance", "Float");
936     if ((eventChance < 0) || (eventChance > 1))
937     {
938         fprintf (stderr, "error: eventChance must be in the range 0..1\n");
939         problems = 1;
940     }
941
942     iterAmt = get_float_resource ("iterAmt", "Float");
943     if ((iterAmt < 0) || (iterAmt > 1))
944     {
945         fprintf (stderr, "error: iterAmt must be in the range 0..1\n");
946         problems = 1;
947     }
948
949     minScale = get_float_resource ("minScale", "Float");
950     if ((minScale < 0) || (minScale > 10))
951     {
952         fprintf (stderr, "error: minScale must be in the range 0..10\n");
953         problems = 1;
954     }
955
956     maxScale = get_float_resource ("maxScale", "Float");
957     if ((maxScale < 0) || (maxScale > 10))
958     {
959         fprintf (stderr, "error: maxScale must be in the range 0..10\n");
960         problems = 1;
961     }
962
963     if (maxScale < minScale)
964     {
965         fprintf (stderr, "error: maxScale must be >= minScale\n");
966         problems = 1;
967     }   
968
969     minRadius = get_integer_resource ("minRadius", "Integer");
970     if ((minRadius < 1) || (minRadius > 100))
971     {
972         fprintf (stderr, "error: minRadius must be in the range 1..100\n");
973         problems = 1;
974     }
975
976     maxRadius = get_integer_resource ("maxRadius", "Integer");
977     if ((maxRadius < 1) || (maxRadius > 100))
978     {
979         fprintf (stderr, "error: maxRadius must be in the range 1..100\n");
980         problems = 1;
981     }
982
983     if (maxRadius < minRadius)
984     {
985         fprintf (stderr, "error: maxRadius must be >= minRadius\n");
986         problems = 1;
987     }   
988
989     if (problems)
990     {
991         exit (1);
992     }
993 }
994
995 /* main function */
996 void screenhack (Display *dpy, Window win)
997 {
998     display = dpy;
999     window = win;
1000
1001     initParams ();
1002     setup ();
1003
1004     /* make a valid set to erase at first */
1005     renderSegs ();
1006     
1007     for (;;) 
1008     {
1009         oneIteration ();
1010         XSync (dpy, False);
1011         screenhack_handle_events (dpy);
1012         usleep (delay);
1013     }
1014 }