source: trunk/third/xscreensaver/hacks/nerverot.c @ 20148

Revision 20148, 30.4 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r20147, which included commits to RCS files with non-trunk default branches.
Line 
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.
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 * See the included man page for more details.
17 */
18
19#include <math.h>
20#include "screenhack.h"
21
22#define FLOAT double
23
24/* random float in the range (-1..1) */
25#define RAND_FLOAT_PM1 \
26        (((FLOAT) ((random() >> 8) & 0xffff)) / ((FLOAT) 0x10000) * 2 - 1)
27
28/* random float in the range (0..1) */
29#define RAND_FLOAT_01 \
30        (((FLOAT) ((random() >> 8) & 0xffff)) / ((FLOAT) 0x10000))
31
32
33
34/* parameters that are user configurable */
35
36/* number of blots */
37static int requestedBlotCount;
38
39/* delay (usec) between iterations */
40int delay;
41
42/* max iterations per model */
43int maxIters;
44
45/* variability of xoff/yoff per iteration (0..1) */
46static FLOAT nervousness;
47
48/* max nervousness radius (0..1) */
49static FLOAT maxNerveRadius;
50
51/* chance per iteration that an event will happen */
52static FLOAT eventChance;
53
54/* fraction (0..1) towards rotation target or scale target to move each
55 * iteration */
56static FLOAT iterAmt;
57
58/* min and max scale for drawing, as fraction of baseScale */
59static FLOAT minScale;
60static FLOAT maxScale;
61
62/* min and max radius of blot drawing */
63static int minRadius;
64static int maxRadius;
65
66/* the number of colors to use */
67static int colorCount;
68
69/* width of lines */
70static int lineWidth;
71
72/* whether or not to do double-buffering */
73static Bool doubleBuffer;
74
75
76
77/* non-user-modifiable immutable definitions */
78
79/* base scale factor for drawing, calculated as
80 * max(screenWidth,screenHeight) */
81static int baseScale;
82
83/* width and height of the window */
84static int windowWidth;
85static int windowHeight;
86
87/* center position of the window */
88static int centerX;
89static int centerY;
90
91static Display *display;  /* the display to draw on */
92static Window window;     /* the window to draw on */
93static Drawable drawable; /* the thing to directly draw on */
94static GC *gcs;           /* array of gcs, one per color used */
95
96
97
98/* structure of the model */
99
100/* each point-like thingy to draw is represented as a blot */
101typedef struct blot_s
102{
103    FLOAT x;           /* 3d x position (-1..1) */
104    FLOAT y;           /* 3d y position (-1..1) */
105    FLOAT z;           /* 3d z position (-1..1) */
106    FLOAT xoff[3][3];  /* display x offset per drawn point (-1..1) */
107    FLOAT yoff[3][3];  /* display x offset per drawn point (-1..1) */
108} Blot;
109
110/* each drawn line is represented as a LineSegment */
111typedef struct linesegment_s
112{
113    GC gc;
114    int x1;
115    int y1;
116    int x2;
117    int y2;
118} LineSegment;
119
120/* array of the blots in the model */
121static Blot *blots = NULL;
122static int blotCount;
123
124/* each blot draws as a simple 2d shape with each coordinate as an int
125 * in the range (-1..1); this is the base shape */
126static XPoint blotShape[] = { { 0, 0}, { 1, 0}, { 1, 1},
127                              { 0, 1}, {-1, 1}, {-1, 0},
128                              {-1,-1}, { 0,-1}, { 1,-1} };
129static int blotShapeCount = sizeof (blotShape) / sizeof (XPoint);
130
131/* two arrays of line segments; one for the ones to erase, and one for the
132 * ones to draw */
133static int segCount;
134static LineSegment *segsToDraw = NULL;
135static LineSegment *segsToErase = NULL;
136
137/* current rotation values per axis, scale factor, and light position */
138static FLOAT xRot;
139static FLOAT yRot;
140static FLOAT zRot;
141static FLOAT curScale;
142static FLOAT lightX;
143static FLOAT lightY;
144static FLOAT lightZ;
145
146/* target rotation values per axis, scale factor, and light position */
147static FLOAT xRotTarget;
148static FLOAT yRotTarget;
149static FLOAT zRotTarget;
150static FLOAT scaleTarget;
151static FLOAT lightXTarget;
152static FLOAT lightYTarget;
153static FLOAT lightZTarget;
154
155/* current absolute offsets from the center */
156static int centerXOff = 0;
157static int centerYOff = 0;
158
159/* iterations until the model changes */
160static int itersTillNext;
161
162
163
164/*
165 * generic blot setup and manipulation
166 */
167
168/* initialize a blot with the given coordinates and random display offsets */
169static void initBlot (Blot *b, FLOAT x, FLOAT y, FLOAT z)
170{
171    int i, j;
172
173    b->x = x;
174    b->y = y;
175    b->z = z;
176
177    for (i = 0; i < 3; i++)
178    {
179        for (j = 0; j < 3; j++)
180        {
181            b->xoff[i][j] = RAND_FLOAT_PM1;
182            b->yoff[i][j] = RAND_FLOAT_PM1;
183        }
184    }
185}
186
187/* scale the blots to have a max distance of 1 from the center */
188static void scaleBlotsToRadius1 (void)
189{
190    FLOAT max = 0.0;
191    int n;
192
193    for (n = 0; n < blotCount; n++)
194    {
195        FLOAT distSquare =
196            blots[n].x * blots[n].x +
197            blots[n].y * blots[n].y +
198            blots[n].z * blots[n].z;
199        if (distSquare > max)
200        {
201            max = distSquare;
202        }
203    }
204
205    if (max == 0.0)
206    {
207        return;
208    }
209
210    max = sqrt (max);
211
212    for (n = 0; n < blotCount; n++)
213    {
214        blots[n].x /= max;
215        blots[n].y /= max;
216        blots[n].z /= max;
217    }
218}
219
220/* randomly reorder the blots */
221static void randomlyReorderBlots (void)
222{
223    int n;
224
225    for (n = 0; n < blotCount; n++)
226    {
227        int m = RAND_FLOAT_01 * (blotCount - n) + n;
228        Blot tmpBlot = blots[n];
229        blots[n] = blots[m];
230        blots[m] = tmpBlot;
231    }
232}
233
234/* randomly rotate the blots around the origin */
235static void randomlyRotateBlots (void)
236{
237    int n;
238
239    /* random amounts to rotate about each axis */
240    FLOAT xRot = RAND_FLOAT_PM1 * M_PI;
241    FLOAT yRot = RAND_FLOAT_PM1 * M_PI;
242    FLOAT zRot = RAND_FLOAT_PM1 * M_PI;
243
244    /* rotation factors */
245    FLOAT sinX = sin (xRot);
246    FLOAT cosX = cos (xRot);
247    FLOAT sinY = sin (yRot);
248    FLOAT cosY = cos (yRot);
249    FLOAT sinZ = sin (zRot);
250    FLOAT cosZ = cos (zRot);
251
252    for (n = 0; n < blotCount; n++)
253    {
254        FLOAT x1 = blots[n].x;
255        FLOAT y1 = blots[n].y;
256        FLOAT z1 = blots[n].z;
257        FLOAT x2, y2, z2;
258
259        /* rotate on z axis */
260        x2 = x1 * cosZ - y1 * sinZ;
261        y2 = x1 * sinZ + y1 * cosZ;
262        z2 = z1;
263
264        /* rotate on x axis */
265        y1 = y2 * cosX - z2 * sinX;
266        z1 = y2 * sinX + z2 * cosX;
267        x1 = x2;
268
269        /* rotate on y axis */
270        z2 = z1 * cosY - x1 * sinY;
271        x2 = z1 * sinY + x1 * cosY;
272        y2 = y1;
273
274        blots[n].x = x2;
275        blots[n].y = y2;
276        blots[n].z = z2;
277    }
278}
279
280
281
282/*
283 * blot configurations
284 */
285
286/* set up the initial array of blots to be a at the edge of a sphere */
287static void setupBlotsSphere (void)
288{
289    int n;
290
291    blotCount = requestedBlotCount;
292    blots = calloc (sizeof (Blot), blotCount);
293
294    for (n = 0; n < blotCount; n++)
295    {
296        /* pick a spot, but reject if its radius is < 0.2 or > 1 to
297         * avoid scaling problems */
298        FLOAT x, y, z, radius;
299
300        for (;;)
301        {
302            x = RAND_FLOAT_PM1;
303            y = RAND_FLOAT_PM1;
304            z = RAND_FLOAT_PM1;
305
306            radius = sqrt (x * x + y * y + z * z);
307            if ((radius >= 0.2) && (radius <= 1.0))
308            {
309                break;
310            }
311        }
312
313        x /= radius;
314        y /= radius;
315        z /= radius;
316
317        initBlot (&blots[n], x, y, z);
318    }
319}
320
321/* set up the initial array of blots to be a simple cube */
322static void setupBlotsCube (void)
323{
324    int i, j, k, n;
325
326    /* derive blotsPerEdge from blotCount, but then do the reverse
327     * since roundoff may have changed blotCount */
328    int blotsPerEdge = ((requestedBlotCount - 8) / 12) + 2;
329    FLOAT distBetween;
330
331    if (blotsPerEdge < 2)
332    {
333        blotsPerEdge = 2;
334    }
335
336    distBetween = 2.0 / (blotsPerEdge - 1.0);
337
338    blotCount = 8 + (blotsPerEdge - 2) * 12;
339    blots = calloc (sizeof (Blot), blotCount);
340    n = 0;
341
342    /* define the corners */
343    for (i = -1; i < 2; i += 2)
344    {
345        for (j = -1; j < 2; j += 2)
346        {
347            for (k = -1; k < 2; k += 2)
348            {
349                initBlot (&blots[n], i, j, k);
350                n++;
351            }
352        }
353    }
354
355    /* define the edges */
356    for (i = 1; i < (blotsPerEdge - 1); i++)
357    {
358        FLOAT varEdge = distBetween * i - 1;
359        initBlot (&blots[n++], varEdge, -1, -1);
360        initBlot (&blots[n++], varEdge,  1, -1);
361        initBlot (&blots[n++], varEdge, -1,  1);
362        initBlot (&blots[n++], varEdge,  1,  1);
363        initBlot (&blots[n++], -1, varEdge, -1);
364        initBlot (&blots[n++],  1, varEdge, -1);
365        initBlot (&blots[n++], -1, varEdge,  1);
366        initBlot (&blots[n++],  1, varEdge,  1);
367        initBlot (&blots[n++], -1, -1, varEdge);
368        initBlot (&blots[n++],  1, -1, varEdge);
369        initBlot (&blots[n++], -1,  1, varEdge);
370        initBlot (&blots[n++],  1,  1, varEdge);
371    }
372
373    scaleBlotsToRadius1 ();
374    randomlyReorderBlots ();
375    randomlyRotateBlots ();
376}
377
378/* set up the initial array of blots to be a cylinder */
379static void setupBlotsCylinder (void)
380{
381    int i, j, n;
382    FLOAT distBetween;
383
384    /* derive blotsPerEdge and blotsPerRing from blotCount, but then do the
385     * reverse since roundoff may have changed blotCount */
386    FLOAT reqRoot = sqrt ((FLOAT) requestedBlotCount);
387    int blotsPerRing = ceil (RAND_FLOAT_PM1 * reqRoot) / 2 + reqRoot;
388    int blotsPerEdge = requestedBlotCount / blotsPerRing;
389
390    if (blotsPerRing < 2)
391    {
392        blotsPerRing = 2;
393    }
394
395    if (blotsPerEdge < 2)
396    {
397        blotsPerEdge = 2;
398    }
399
400    distBetween = 2.0 / (blotsPerEdge - 1);
401
402    blotCount = blotsPerEdge * blotsPerRing;
403    blots = calloc (sizeof (Blot), blotCount);
404    n = 0;
405
406    /* define the edges */
407    for (i = 0; i < blotsPerRing; i++)
408    {
409        FLOAT x = sin (2 * M_PI / blotsPerRing * i);
410        FLOAT y = cos (2 * M_PI / blotsPerRing * i);
411        for (j = 0; j < blotsPerEdge; j++)
412        {
413            initBlot (&blots[n], x, y, j * distBetween - 1);
414            n++;
415        }
416    }
417
418    scaleBlotsToRadius1 ();
419    randomlyReorderBlots ();
420    randomlyRotateBlots ();
421}
422
423/* set up the initial array of blots to be a squiggle */
424static void setupBlotsSquiggle (void)
425{
426    FLOAT x, y, z, xv, yv, zv, len;
427    int minCoor, maxCoor;
428    int n;
429
430    blotCount = requestedBlotCount;
431    blots = calloc (sizeof (Blot), blotCount);
432
433    maxCoor = (int) (RAND_FLOAT_01 * 5) + 1;
434    minCoor = -maxCoor;
435
436    x = RAND_FLOAT_PM1;
437    y = RAND_FLOAT_PM1;
438    z = RAND_FLOAT_PM1;
439
440    xv = RAND_FLOAT_PM1;
441    yv = RAND_FLOAT_PM1;
442    zv = RAND_FLOAT_PM1;
443    len = sqrt (xv * xv + yv * yv + zv * zv);
444    xv /= len;
445    yv /= len;
446    zv /= len;
447   
448    for (n = 0; n < blotCount; n++)
449    {
450        FLOAT newx, newy, newz;
451        initBlot (&blots[n], x, y, z);
452
453        for (;;)
454        {
455            xv += RAND_FLOAT_PM1 * 0.1;
456            yv += RAND_FLOAT_PM1 * 0.1;
457            zv += RAND_FLOAT_PM1 * 0.1;
458            len = sqrt (xv * xv + yv * yv + zv * zv);
459            xv /= len;
460            yv /= len;
461            zv /= len;
462
463            newx = x + xv * 0.1;
464            newy = y + yv * 0.1;
465            newz = z + zv * 0.1;
466
467            if (   (newx >= minCoor) && (newx <= maxCoor)
468                && (newy >= minCoor) && (newy <= maxCoor)
469                && (newz >= minCoor) && (newz <= maxCoor))
470            {
471                break;
472            }
473        }
474
475        x = newx;
476        y = newy;
477        z = newz;
478    }
479
480    scaleBlotsToRadius1 ();
481    randomlyReorderBlots ();
482}
483
484/* set up the initial array of blots to be near the corners of a
485 * cube, distributed slightly */
486static void setupBlotsCubeCorners (void)
487{
488    int n;
489
490    blotCount = requestedBlotCount;
491    blots = calloc (sizeof (Blot), blotCount);
492
493    for (n = 0; n < blotCount; n++)
494    {
495        FLOAT x = rint (RAND_FLOAT_01) * 2 - 1;
496        FLOAT y = rint (RAND_FLOAT_01) * 2 - 1;
497        FLOAT z = rint (RAND_FLOAT_01) * 2 - 1;
498
499        x += RAND_FLOAT_PM1 * 0.3;
500        y += RAND_FLOAT_PM1 * 0.3;
501        z += RAND_FLOAT_PM1 * 0.3;
502
503        initBlot (&blots[n], x, y, z);
504    }
505
506    scaleBlotsToRadius1 ();
507    randomlyRotateBlots ();
508}
509
510/* set up the initial array of blots to be randomly distributed
511 * on the surface of a tetrahedron */
512static void setupBlotsTetrahedron (void)
513{
514    /* table of corners of the tetrahedron */
515    static FLOAT cor[4][3] = { {  0.0,   1.0,  0.0 },
516                               { -0.75, -0.5, -0.433013 },
517                               {  0.0,  -0.5,  0.866025 },
518                               {  0.75, -0.5, -0.433013 } };
519
520    int n, c;
521
522    /* derive blotsPerSurface from blotCount, but then do the reverse
523     * since roundoff may have changed blotCount */
524    int blotsPerSurface = requestedBlotCount / 4;
525
526    blotCount = blotsPerSurface * 4;
527    blots = calloc (sizeof (Blot), blotCount);
528
529    for (n = 0; n < blotCount; n += 4)
530    {
531        /* pick a random point on a unit right triangle */
532        FLOAT rawx = RAND_FLOAT_01;
533        FLOAT rawy = RAND_FLOAT_01;
534
535        if ((rawx + rawy) > 1)
536        {
537            /* swap coords into place */
538            FLOAT t = 1.0 - rawx;
539            rawx = 1.0 - rawy;
540            rawy = t;
541        }
542
543        /* translate the point to be on each of the surfaces */
544        for (c = 0; c < 4; c++)
545        {
546            FLOAT x, y, z;
547           
548            int c1 = (c + 1) % 4;
549            int c2 = (c + 2) % 4;
550           
551            x = (cor[c1][0] - cor[c][0]) * rawx +
552                (cor[c2][0] - cor[c][0]) * rawy +
553                cor[c][0];
554
555            y = (cor[c1][1] - cor[c][1]) * rawx +
556                (cor[c2][1] - cor[c][1]) * rawy +
557                cor[c][1];
558
559            z = (cor[c1][2] - cor[c][2]) * rawx +
560                (cor[c2][2] - cor[c][2]) * rawy +
561                cor[c][2];
562
563            initBlot (&blots[n + c], x, y, z);
564        }
565    }
566
567    randomlyRotateBlots ();
568}
569
570/* set up the initial array of blots to be an almost-evenly-distributed
571 * square sheet */
572static void setupBlotsSheet (void)
573{
574    int x, y;
575
576    int blotsPerDimension = floor (sqrt (requestedBlotCount));
577    FLOAT spaceBetween;
578
579    if (blotsPerDimension < 2)
580    {
581        blotsPerDimension = 2;
582    }
583
584    spaceBetween = 2.0 / (blotsPerDimension - 1);
585
586    blotCount = blotsPerDimension * blotsPerDimension;
587    blots = calloc (sizeof (Blot), blotCount);
588
589    for (x = 0; x < blotsPerDimension; x++)
590    {
591        for (y = 0; y < blotsPerDimension; y++)
592        {
593            FLOAT x1 = x * spaceBetween - 1.0;
594            FLOAT y1 = y * spaceBetween - 1.0;
595            FLOAT z1 = 0.0;
596
597            x1 += RAND_FLOAT_PM1 * spaceBetween / 3;
598            y1 += RAND_FLOAT_PM1 * spaceBetween / 3;
599            z1 += RAND_FLOAT_PM1 * spaceBetween / 2;
600
601            initBlot (&blots[x + y * blotsPerDimension], x1, y1, z1);
602        }
603    }
604
605    scaleBlotsToRadius1 ();
606    randomlyReorderBlots ();
607    randomlyRotateBlots ();
608}
609
610/* set up the initial array of blots to be a swirlycone */
611static void setupBlotsSwirlyCone (void)
612{
613    FLOAT radSpace = 1.0 / (requestedBlotCount - 1);
614    FLOAT zSpace = radSpace * 2;
615    FLOAT rotAmt = RAND_FLOAT_PM1 * M_PI / 10;
616
617    int n;
618    FLOAT rot = 0.0;
619
620    blotCount = requestedBlotCount;
621    blots = calloc (sizeof (Blot), blotCount);
622
623    for (n = 0; n < blotCount; n++)
624    {
625        FLOAT radius = n * radSpace;
626        FLOAT x = cos (rot) * radius;
627        FLOAT y = sin (rot) * radius;
628        FLOAT z = n * zSpace - 1.0;
629
630        rot += rotAmt;
631        initBlot (&blots[n], x, y, z);
632    }
633
634    scaleBlotsToRadius1 ();
635    randomlyReorderBlots ();
636    randomlyRotateBlots ();
637}
638
639/* forward declaration for recursive use immediately below */
640static void setupBlots (void);
641
642/* set up the blots to be two of the other choices, placed next to
643 * each other */
644static void setupBlotsDuo (void)
645{
646    int origRequest = requestedBlotCount;
647    FLOAT tx, ty, tz, radius;
648    Blot *blots1, *blots2;
649    int count1, count2;
650    int n;
651
652    if (requestedBlotCount < 15)
653    {
654        /* special case bottom-out */
655        setupBlotsSphere ();
656        return;
657    }
658
659    tx = RAND_FLOAT_PM1;
660    ty = RAND_FLOAT_PM1;
661    tz = RAND_FLOAT_PM1;
662    radius = sqrt (tx * tx + ty * ty + tz * tz);
663    tx /= radius;
664    ty /= radius;
665    tz /= radius;
666
667    /* recursive call to setup set 1 */
668    requestedBlotCount = origRequest / 2;
669    setupBlots ();
670
671    if (blotCount >= origRequest)
672    {
673        /* return immediately if this satisfies the original count request */
674        requestedBlotCount = origRequest;
675        return;
676    }
677
678    blots1 = blots;
679    count1 = blotCount;
680    blots = NULL;
681    blotCount = 0;
682   
683    /* translate to new position */
684    for (n = 0; n < count1; n++)
685    {
686        blots1[n].x += tx;
687        blots1[n].y += ty;
688        blots1[n].z += tz;
689    }
690
691    /* recursive call to setup set 2 */
692    requestedBlotCount = origRequest - count1;
693    setupBlots ();
694    blots2 = blots;
695    count2 = blotCount;
696
697    /* translate to new position */
698    for (n = 0; n < count2; n++)
699    {
700        blots2[n].x -= tx;
701        blots2[n].y -= ty;
702        blots2[n].z -= tz;
703    }
704
705    /* combine the two arrays */
706    blotCount = count1 + count2;
707    blots = calloc (sizeof (Blot), blotCount);
708    memcpy (&blots[0],      blots1, sizeof (Blot) * count1);
709    memcpy (&blots[count1], blots2, sizeof (Blot) * count2);
710    free (blots1);
711    free (blots2);
712
713    scaleBlotsToRadius1 ();
714    randomlyReorderBlots ();
715
716    /* restore the original requested count, for future iterations */
717    requestedBlotCount = origRequest;
718}
719
720
721
722/*
723 * main blot setup
724 */
725
726/* free the blots, in preparation for a new shape */
727static void freeBlots (void)
728{
729    if (blots != NULL)
730    {
731        free (blots);
732        blots = NULL;
733    }
734
735    if (segsToErase != NULL)
736    {
737        free (segsToErase);
738        segsToErase = NULL;
739    }
740
741    if (segsToDraw != NULL)
742    {
743        free (segsToDraw);
744        segsToDraw = NULL;
745    }
746}
747
748/* set up the initial arrays of blots */
749static void setupBlots (void)
750{
751    int which = RAND_FLOAT_01 * 11;
752
753    freeBlots ();
754
755    switch (which)
756    {
757        case 0:
758            setupBlotsCube ();
759            break;
760        case 1:
761            setupBlotsSphere ();
762            break;
763        case 2:
764            setupBlotsCylinder ();
765            break;
766        case 3:
767            setupBlotsSquiggle ();
768            break;
769        case 4:
770            setupBlotsCubeCorners ();
771            break;
772        case 5:
773            setupBlotsTetrahedron ();
774            break;
775        case 6:
776            setupBlotsSheet ();
777            break;
778        case 7:
779            setupBlotsSwirlyCone ();
780            break;
781        case 8:
782        case 9:
783        case 10:
784            setupBlotsDuo ();
785            break;
786    }
787}
788
789/* set up the segments arrays */
790static void setupSegs (void)
791{
792    /* there are blotShapeCount - 1 line segments per blot */
793    segCount = blotCount * (blotShapeCount - 1);
794    segsToErase = calloc (sizeof (LineSegment), segCount);
795    segsToDraw = calloc (sizeof (LineSegment), segCount);
796
797    /* erase the world */
798    XFillRectangle (display, drawable, gcs[0], 0, 0,
799                    windowWidth, windowHeight);
800}
801
802
803
804/*
805 * color setup stuff
806 */
807
808/* set up the colormap */
809static void setupColormap (XWindowAttributes *xgwa)
810{
811    int n;
812    XGCValues gcv;
813    XColor *colors = (XColor *) calloc (sizeof (XColor), colorCount + 1);
814
815    unsigned short r, g, b;
816    int h1, h2;
817    double s1, s2, v1, v2;
818
819    r = RAND_FLOAT_01 * 0x10000;
820    g = RAND_FLOAT_01 * 0x10000;
821    b = RAND_FLOAT_01 * 0x10000;
822    rgb_to_hsv (r, g, b, &h1, &s1, &v1);
823    v1 = 1.0;
824    s1 = 1.0;
825
826    r = RAND_FLOAT_01 * 0x10000;
827    g = RAND_FLOAT_01 * 0x10000;
828    b = RAND_FLOAT_01 * 0x10000;
829    rgb_to_hsv (r, g, b, &h2, &s2, &v2);
830    s2 = 0.7;
831    v2 = 0.7;
832   
833    colors[0].pixel = get_pixel_resource ("background", "Background",
834                                          display, xgwa->colormap);
835   
836    make_color_ramp (display, xgwa->colormap, h1, s1, v1, h2, s2, v2,
837                     colors + 1, &colorCount, False, True, False);
838
839    if (colorCount < 1)
840    {
841        fprintf (stderr, "%s: couldn't allocate any colors\n", progname);
842        exit (-1);
843    }
844   
845    gcs = (GC *) calloc (sizeof (GC), colorCount + 1);
846
847    for (n = 0; n <= colorCount; n++)
848    {
849        gcv.foreground = colors[n].pixel;
850        gcv.line_width = lineWidth;
851        gcs[n] = XCreateGC (display, window, GCForeground | GCLineWidth, &gcv);
852    }
853
854    free (colors);
855}
856
857
858
859/*
860 * overall setup stuff
861 */
862
863/* set up the system */
864static void setup (void)
865{
866    XWindowAttributes xgwa;
867
868    XGetWindowAttributes (display, window, &xgwa);
869
870    windowWidth = xgwa.width;
871    windowHeight = xgwa.height;
872    centerX = windowWidth / 2;
873    centerY = windowHeight / 2;
874    baseScale = (xgwa.height < xgwa.width) ? xgwa.height : xgwa.width;
875
876    if (doubleBuffer)
877    {
878        drawable = XCreatePixmap (display, window, xgwa.width, xgwa.height,
879                                  xgwa.depth);
880    }
881    else
882    {
883        drawable = window;
884    }
885
886    setupColormap (&xgwa);
887    setupBlots ();
888    setupSegs ();
889
890    /* set up the initial rotation, scale, and light values as random, but
891     * with the targets equal to where it is */
892    xRot = xRotTarget = RAND_FLOAT_01 * M_PI;
893    yRot = yRotTarget = RAND_FLOAT_01 * M_PI;
894    zRot = zRotTarget = RAND_FLOAT_01 * M_PI;
895    curScale = scaleTarget = RAND_FLOAT_01 * (maxScale - minScale) + minScale;
896    lightX = lightXTarget = RAND_FLOAT_PM1;
897    lightY = lightYTarget = RAND_FLOAT_PM1;
898    lightZ = lightZTarget = RAND_FLOAT_PM1;
899
900    itersTillNext = RAND_FLOAT_01 * maxIters;
901}
902
903
904
905/*
906 * the simulation
907 */
908
909/* "render" the blots into segsToDraw, with the current rotation factors */
910static void renderSegs (void)
911{
912    int n;
913    int m = 0;
914
915    /* rotation factors */
916    FLOAT sinX = sin (xRot);
917    FLOAT cosX = cos (xRot);
918    FLOAT sinY = sin (yRot);
919    FLOAT cosY = cos (yRot);
920    FLOAT sinZ = sin (zRot);
921    FLOAT cosZ = cos (zRot);
922
923    for (n = 0; n < blotCount; n++)
924    {
925        Blot *b = &blots[n];
926        int i, j;
927        int baseX, baseY;
928        FLOAT radius;
929        int x[3][3];
930        int y[3][3];
931        int color;
932
933        FLOAT x1 = blots[n].x;
934        FLOAT y1 = blots[n].y;
935        FLOAT z1 = blots[n].z;
936        FLOAT x2, y2, z2;
937
938        /* rotate on z axis */
939        x2 = x1 * cosZ - y1 * sinZ;
940        y2 = x1 * sinZ + y1 * cosZ;
941        z2 = z1;
942
943        /* rotate on x axis */
944        y1 = y2 * cosX - z2 * sinX;
945        z1 = y2 * sinX + z2 * cosX;
946        x1 = x2;
947
948        /* rotate on y axis */
949        z2 = z1 * cosY - x1 * sinY;
950        x2 = z1 * sinY + x1 * cosY;
951        y2 = y1;
952
953        /* the color to draw is based on the distance from the light of
954         * the post-rotation blot */
955        x1 = x2 - lightX;
956        y1 = y2 - lightY;
957        z1 = z2 - lightZ;
958        color = 1 + (x1 * x1 + y1 * y1 + z1 * z1) / 4 * colorCount;
959        if (color > colorCount)
960        {
961            color = colorCount;
962        }
963
964        /* set up the base screen coordinates for drawing */
965        baseX = x2 / 2 * baseScale * curScale + centerX + centerXOff;
966        baseY = y2 / 2 * baseScale * curScale + centerY + centerYOff;
967       
968        radius = (z2 + 1) / 2 * (maxRadius - minRadius) + minRadius;
969
970        for (i = 0; i < 3; i++)
971        {
972            for (j = 0; j < 3; j++)
973            {
974                x[i][j] = baseX +
975                    ((i - 1) + (b->xoff[i][j] * maxNerveRadius)) * radius;
976                y[i][j] = baseY +
977                    ((j - 1) + (b->yoff[i][j] * maxNerveRadius)) * radius;
978            }
979        }
980
981        for (i = 1; i < blotShapeCount; i++)
982        {
983            segsToDraw[m].gc = gcs[color];
984            segsToDraw[m].x1 = x[blotShape[i-1].x + 1][blotShape[i-1].y + 1];
985            segsToDraw[m].y1 = y[blotShape[i-1].x + 1][blotShape[i-1].y + 1];
986            segsToDraw[m].x2 = x[blotShape[i].x   + 1][blotShape[i].y   + 1];
987            segsToDraw[m].y2 = y[blotShape[i].x   + 1][blotShape[i].y   + 1];
988            m++;
989        }
990    }
991}
992
993/* update blots, adjusting the offsets and rotation factors. */
994static void updateWithFeeling (void)
995{
996    int n, i, j;
997
998    /* pick a new model if the time is right */
999    itersTillNext--;
1000    if (itersTillNext < 0)
1001    {
1002        itersTillNext = RAND_FLOAT_01 * maxIters;
1003        setupBlots ();
1004        setupSegs ();
1005        renderSegs ();
1006    }
1007
1008    /* update the rotation factors by moving them a bit toward the targets */
1009    xRot = xRot + (xRotTarget - xRot) * iterAmt;
1010    yRot = yRot + (yRotTarget - yRot) * iterAmt;
1011    zRot = zRot + (zRotTarget - zRot) * iterAmt;
1012
1013    /* similarly the scale factor */
1014    curScale = curScale + (scaleTarget - curScale) * iterAmt;
1015
1016    /* and similarly the light position */
1017    lightX = lightX + (lightXTarget - lightX) * iterAmt;
1018    lightY = lightY + (lightYTarget - lightY) * iterAmt;
1019    lightZ = lightZ + (lightZTarget - lightZ) * iterAmt;
1020
1021    /* for each blot... */
1022    for (n = 0; n < blotCount; n++)
1023    {
1024        /* add a bit of random jitter to xoff/yoff */
1025        for (i = 0; i < 3; i++)
1026        {
1027            for (j = 0; j < 3; j++)
1028            {
1029                FLOAT newOff;
1030
1031                newOff = blots[n].xoff[i][j] + RAND_FLOAT_PM1 * nervousness;
1032                if (newOff < -1) newOff = -(newOff + 1) - 1;
1033                else if (newOff > 1) newOff = -(newOff - 1) + 1;
1034                blots[n].xoff[i][j] = newOff;
1035
1036                newOff = blots[n].yoff[i][j] + RAND_FLOAT_PM1 * nervousness;
1037                if (newOff < -1) newOff = -(newOff + 1) - 1;
1038                else if (newOff > 1) newOff = -(newOff - 1) + 1;
1039                blots[n].yoff[i][j] = newOff;
1040            }
1041        }
1042    }
1043
1044    /* depending on random chance, update one or more factors */
1045    if (RAND_FLOAT_01 <= eventChance)
1046    {
1047        int which = RAND_FLOAT_01 * 14;
1048        switch (which)
1049        {
1050            case 0:
1051            {
1052                xRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
1053                break;
1054            }
1055            case 1:
1056            {
1057                yRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
1058                break;
1059            }
1060            case 2:
1061            {
1062                zRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
1063                break;
1064            }
1065            case 3:
1066            {
1067                xRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
1068                yRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
1069                break;
1070            }
1071            case 4:
1072            {
1073                xRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
1074                zRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
1075                break;
1076            }
1077            case 5:
1078            {
1079                yRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
1080                zRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
1081                break;
1082            }
1083            case 6:
1084            {
1085                xRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
1086                yRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
1087                zRotTarget = RAND_FLOAT_PM1 * M_PI * 2;
1088                break;
1089            }
1090            case 7:
1091            {
1092                centerXOff = RAND_FLOAT_PM1 * maxRadius;
1093                break;
1094            }
1095            case 8:
1096            {
1097                centerYOff = RAND_FLOAT_PM1 * maxRadius;
1098                break;
1099            }
1100            case 9:
1101            {
1102                centerXOff = RAND_FLOAT_PM1 * maxRadius;
1103                centerYOff = RAND_FLOAT_PM1 * maxRadius;
1104                break;
1105            }
1106            case 10:
1107            {
1108                scaleTarget =
1109                    RAND_FLOAT_01 * (maxScale - minScale) + minScale;
1110                break;
1111            }
1112            case 11:
1113            {
1114                curScale =
1115                    RAND_FLOAT_01 * (maxScale - minScale) + minScale;
1116                break;
1117            }
1118            case 12:
1119            {
1120                lightX = RAND_FLOAT_PM1;
1121                lightY = RAND_FLOAT_PM1;
1122                lightZ = RAND_FLOAT_PM1;
1123                break;
1124            }
1125            case 13:
1126            {
1127                lightXTarget = RAND_FLOAT_PM1;
1128                lightYTarget = RAND_FLOAT_PM1;
1129                lightZTarget = RAND_FLOAT_PM1;
1130                break;
1131            }
1132        }
1133    }
1134}
1135
1136/* erase segsToErase and draw segsToDraw */
1137static void eraseAndDraw (void)
1138{
1139    int n;
1140
1141    for (n = 0; n < segCount; n++)
1142    {
1143        LineSegment *seg = &segsToErase[n];
1144        XDrawLine (display, drawable, gcs[0],
1145                   seg->x1, seg->y1, seg->x2, seg->y2);
1146        seg = &segsToDraw[n];
1147        XDrawLine (display, drawable, seg->gc,
1148                   seg->x1, seg->y1, seg->x2, seg->y2);
1149    }
1150
1151    if (doubleBuffer)
1152    {
1153        XCopyArea (display, drawable, window, gcs[0], 0, 0,
1154                   windowWidth, windowHeight, 0, 0);
1155    }
1156}
1157
1158/* do one iteration */
1159static void oneIteration (void)
1160{
1161    /* switch segsToErase and segsToDraw */
1162    LineSegment *temp = segsToDraw;
1163    segsToDraw = segsToErase;
1164    segsToErase = temp;
1165
1166    /* update the model */
1167    updateWithFeeling ();
1168
1169    /* render new segments */
1170    renderSegs ();
1171
1172    /* erase old segments and draw new ones */
1173    eraseAndDraw ();
1174}
1175
1176char *progclass = "NerveRot";
1177
1178char *defaults [] = {
1179    ".background:       black",
1180    ".foreground:       white",
1181    "*count:            250",
1182    "*colors:           4",
1183    "*delay:            10000",
1184    "*maxIters:         1200",
1185    "*doubleBuffer:     false",
1186    "*eventChance:      0.2",
1187    "*iterAmt:          0.01",
1188    "*lineWidth:        0",
1189    "*minScale:         0.6",
1190    "*maxScale:         1.75",
1191    "*minRadius:        3",
1192    "*maxRadius:        25",
1193    "*maxNerveRadius:   0.7",
1194    "*nervousness:      0.3",
1195    0
1196};
1197
1198XrmOptionDescRec options [] = {
1199  { "-count",            ".count",          XrmoptionSepArg, 0 },
1200  { "-colors",           ".colors",         XrmoptionSepArg, 0 },
1201  { "-delay",            ".delay",          XrmoptionSepArg, 0 },
1202  { "-max-iters",        ".maxIters",       XrmoptionSepArg, 0 },
1203  { "-db",               ".doubleBuffer",   XrmoptionNoArg,  "true" },
1204  { "-no-db",            ".doubleBuffer",   XrmoptionNoArg,  "false" },
1205  { "-event-chance",     ".eventChance",    XrmoptionSepArg, 0 },
1206  { "-iter-amt",         ".iterAmt",        XrmoptionSepArg, 0 },
1207  { "-line-width",       ".lineWidth",      XrmoptionSepArg, 0 },
1208  { "-min-scale",        ".minScale",       XrmoptionSepArg, 0 },
1209  { "-max-scale",        ".maxScale",       XrmoptionSepArg, 0 },
1210  { "-min-radius",       ".minRadius",      XrmoptionSepArg, 0 },
1211  { "-max-radius",       ".maxRadius",      XrmoptionSepArg, 0 },
1212  { "-max-nerve-radius", ".maxNerveRadius", XrmoptionSepArg, 0 },
1213  { "-nervousness",      ".nervousness",    XrmoptionSepArg, 0 },
1214  { 0, 0, 0, 0 }
1215};
1216
1217/* initialize the user-specifiable params */
1218static void initParams (void)
1219{
1220    int problems = 0;
1221
1222    delay = get_integer_resource ("delay", "Delay");
1223    if (delay < 0)
1224    {
1225        fprintf (stderr, "error: delay must be at least 0\n");
1226        problems = 1;
1227    }
1228   
1229    maxIters = get_integer_resource ("maxIters", "Integer");
1230    if (maxIters < 0)
1231    {
1232        fprintf (stderr, "error: maxIters must be at least 0\n");
1233        problems = 1;
1234    }
1235   
1236    doubleBuffer = get_boolean_resource ("doubleBuffer", "Boolean");
1237
1238    requestedBlotCount = get_integer_resource ("count", "Count");
1239    if (requestedBlotCount <= 0)
1240    {
1241        fprintf (stderr, "error: count must be at least 0\n");
1242        problems = 1;
1243    }
1244
1245    colorCount = get_integer_resource ("colors", "Colors");
1246    if (colorCount <= 0)
1247    {
1248        fprintf (stderr, "error: colors must be at least 1\n");
1249        problems = 1;
1250    }
1251
1252    lineWidth = get_integer_resource ("lineWidth", "LineWidth");
1253    if (lineWidth < 0)
1254    {
1255        fprintf (stderr, "error: line width must be at least 0\n");
1256        problems = 1;
1257    }
1258
1259    nervousness = get_float_resource ("nervousness", "Float");
1260    if ((nervousness < 0) || (nervousness > 1))
1261    {
1262        fprintf (stderr, "error: nervousness must be in the range 0..1\n");
1263        problems = 1;
1264    }
1265
1266    maxNerveRadius = get_float_resource ("maxNerveRadius", "Float");
1267    if ((maxNerveRadius < 0) || (maxNerveRadius > 1))
1268    {
1269        fprintf (stderr, "error: maxNerveRadius must be in the range 0..1\n");
1270        problems = 1;
1271    }
1272
1273    eventChance = get_float_resource ("eventChance", "Float");
1274    if ((eventChance < 0) || (eventChance > 1))
1275    {
1276        fprintf (stderr, "error: eventChance must be in the range 0..1\n");
1277        problems = 1;
1278    }
1279
1280    iterAmt = get_float_resource ("iterAmt", "Float");
1281    if ((iterAmt < 0) || (iterAmt > 1))
1282    {
1283        fprintf (stderr, "error: iterAmt must be in the range 0..1\n");
1284        problems = 1;
1285    }
1286
1287    minScale = get_float_resource ("minScale", "Float");
1288    if ((minScale < 0) || (minScale > 10))
1289    {
1290        fprintf (stderr, "error: minScale must be in the range 0..10\n");
1291        problems = 1;
1292    }
1293
1294    maxScale = get_float_resource ("maxScale", "Float");
1295    if ((maxScale < 0) || (maxScale > 10))
1296    {
1297        fprintf (stderr, "error: maxScale must be in the range 0..10\n");
1298        problems = 1;
1299    }
1300
1301    if (maxScale < minScale)
1302    {
1303        fprintf (stderr, "error: maxScale must be >= minScale\n");
1304        problems = 1;
1305    }   
1306
1307    minRadius = get_integer_resource ("minRadius", "Integer");
1308    if ((minRadius < 1) || (minRadius > 100))
1309    {
1310        fprintf (stderr, "error: minRadius must be in the range 1..100\n");
1311        problems = 1;
1312    }
1313
1314    maxRadius = get_integer_resource ("maxRadius", "Integer");
1315    if ((maxRadius < 1) || (maxRadius > 100))
1316    {
1317        fprintf (stderr, "error: maxRadius must be in the range 1..100\n");
1318        problems = 1;
1319    }
1320
1321    if (maxRadius < minRadius)
1322    {
1323        fprintf (stderr, "error: maxRadius must be >= minRadius\n");
1324        problems = 1;
1325    }   
1326
1327    if (problems)
1328    {
1329        exit (1);
1330    }
1331}
1332
1333/* main function */
1334void screenhack (Display *dpy, Window win)
1335{
1336    display = dpy;
1337    window = win;
1338
1339    initParams ();
1340    setup ();
1341
1342    /* make a valid set to erase at first */
1343    renderSegs ();
1344   
1345    for (;;)
1346    {
1347        oneIteration ();
1348        XSync (dpy, False);
1349        screenhack_handle_events (dpy);
1350        usleep (delay);
1351    }
1352}
Note: See TracBrowser for help on using the repository browser.