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

Revision 20148, 18.2 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/* twang, twist around screen bits, v1.3
2 * by Dan Bornstein, danfuzz@milk.com
3 * Copyright (c) 2003 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 * See the included man page for more details.
14 */
15
16#include <math.h>
17#include "screenhack.h"
18#include <X11/Xutil.h>
19
20#ifdef HAVE_XSHM_EXTENSION
21#include "xshm.h"
22#endif
23
24#define FLOAT double
25
26/* random float in the range (-1..1) */
27#define RAND_FLOAT_PM1 \
28        (((FLOAT) ((random() >> 8) & 0xffff)) / ((FLOAT) 0x10000) * 2 - 1)
29
30/* random float in the range (0..1) */
31#define RAND_FLOAT_01 \
32        (((FLOAT) ((random() >> 8) & 0xffff)) / ((FLOAT) 0x10000))
33
34
35
36/* parameters that are user configurable */
37
38/* whether or not to use xshm */
39#ifdef HAVE_XSHM_EXTENSION
40static Bool useShm;
41#endif
42
43/* delay (usec) between iterations */
44static int delay;
45
46/* the maximum number of columns of tiles */
47static int maxColumns;
48
49/* the maximum number of rows of tiles */
50static int maxRows;
51
52/* the size (width and height) of a tile */
53static int tileSize;
54
55/* the width of the border around each tile */
56static int borderWidth;
57
58/* the chance, per iteration, of an interesting event happening */
59static FLOAT eventChance;
60
61/* friction: the fraction (0..1) by which velocity decreased per iteration */
62static FLOAT friction;
63
64/* springiness: the fraction (0..1) of the orientation that turns into
65 * velocity towards the center */
66static FLOAT springiness;
67
68/* transference: the fraction (0..1) of the orientations of orthogonal
69 * neighbors that turns into velocity (in the same direction as the
70 * orientation) */
71static FLOAT transference;
72
73
74
75/* non-user-modifiable immutable definitions */
76
77/* width and height of the window */
78static int windowWidth;
79static int windowHeight;
80
81static Display *display;        /* the display to draw on */
82static Window window;           /* the window to draw on */
83static Screen *screen;          /* the screen to draw on */
84
85static XImage *sourceImage;     /* image source of stuff to draw */
86static XImage *workImage;       /* work area image, used when rendering */
87static XImage *backgroundImage; /* image filled with background pixels */
88
89static GC backgroundGC;         /* GC for the background color */
90static GC foregroundGC;         /* GC for the foreground color */
91static GC borderGC;             /* GC for the border color */
92unsigned long backgroundPixel;  /* background color as a pixel value */
93unsigned long borderPixel;      /* border color as a pixel value */
94
95#ifdef HAVE_XSHM_EXTENSION
96XShmSegmentInfo shmInfo;
97#endif
98
99
100
101/* the model */
102
103typedef struct
104{
105    int x;        /* x coordinate of the center of the tile */
106    int y;        /* y coordinate of the center of the tile */
107    FLOAT angle;  /* angle of the tile (-pi..pi) */
108    FLOAT zoom;   /* log of the zoom of the tile (-1..1) */
109    FLOAT vAngle; /* angular velocity (-pi/4..pi/4) */
110    FLOAT vZoom;  /* zoomular velocity (-0.25..0.25) */
111}
112Tile;
113
114static Tile *tiles;  /* array of tiles (left->right, top->bottom, row major) */
115static int rows;     /* number of rows of tiles */
116static int columns;  /* number of columns of tiles */
117
118static Tile **sortedTiles; /* array of tile pointers, sorted by zoom */
119static int tileCount;     /* total number of tiles */
120
121#define TILE_AT(col,row) (&tiles[(row) * columns + (col)])
122
123#define MAX_VANGLE (M_PI / 4.0)
124#define MAX_VZOOM 0.25
125
126#define RAND_ANGLE (RAND_FLOAT_PM1 * M_PI)
127#define RAND_ZOOM (RAND_FLOAT_PM1)
128#define RAND_VANGLE (RAND_FLOAT_PM1 * MAX_VANGLE)
129#define RAND_VZOOM (RAND_FLOAT_PM1 * MAX_VZOOM)
130
131
132
133/*
134 * overall setup stuff
135 */
136
137/* grab the source image */
138static void grabImage (XWindowAttributes *xwa)
139{
140    XFillRectangle (display, window, backgroundGC, 0, 0,
141                    windowWidth, windowHeight);
142    backgroundImage =
143        XGetImage (display, window, 0, 0, windowWidth, windowHeight,
144                   ~0L, ZPixmap);
145
146    load_random_image (screen, window, window, NULL);
147    sourceImage = XGetImage (display, window, 0, 0, windowWidth, windowHeight,
148                             ~0L, ZPixmap);
149
150#ifdef HAVE_XSHM_EXTENSION
151    workImage = NULL;
152    if (useShm)
153    {
154        workImage = create_xshm_image (display, xwa->visual, xwa->depth,
155                                       ZPixmap, 0, &shmInfo,
156                                       windowWidth, windowHeight);
157        if (!workImage)
158        {
159            useShm = False;
160            fprintf (stderr, "create_xshm_image failed\n");
161        }
162    }
163
164    if (workImage == NULL)
165#endif /* HAVE_XSHM_EXTENSION */
166
167        /* just use XSubImage to acquire the right visual, depth, etc;
168         * easier than the other alternatives */
169        workImage = XSubImage (sourceImage, 0, 0, windowWidth, windowHeight);
170}
171
172/* set up the system */
173static void setup (void)
174{
175    XWindowAttributes xgwa;
176    XGCValues gcv;
177
178    XGetWindowAttributes (display, window, &xgwa);
179
180    screen = xgwa.screen;
181    windowWidth = xgwa.width;
182    windowHeight = xgwa.height;
183
184    gcv.line_width = borderWidth;
185    gcv.foreground = get_pixel_resource ("borderColor", "BorderColor",
186                                         display, xgwa.colormap);
187    borderPixel = gcv.foreground;
188    borderGC = XCreateGC (display, window, GCForeground | GCLineWidth,
189                          &gcv);
190
191    gcv.foreground = get_pixel_resource ("background", "Background",
192                                         display, xgwa.colormap);
193    backgroundPixel = gcv.foreground;
194    backgroundGC = XCreateGC (display, window, GCForeground, &gcv);
195
196    gcv.foreground = get_pixel_resource ("foreground", "Foreground",
197                                         display, xgwa.colormap);
198    foregroundGC = XCreateGC (display, window, GCForeground, &gcv);
199
200    grabImage (&xgwa);
201}
202
203
204
205/*
206 * the simulation
207 */
208
209/* event: randomize all the angular velocities */
210static void randomizeAllAngularVelocities (void)
211{
212    int c;
213    int r;
214
215    for (r = 0; r < rows; r++)
216    {
217        for (c = 0; c < columns; c++)
218        {
219            TILE_AT (c, r)->vAngle = RAND_VANGLE;
220        }
221    }
222}
223
224/* event: randomize all the zoomular velocities */
225static void randomizeAllZoomularVelocities (void)
226{
227    int c;
228    int r;
229
230    for (r = 0; r < rows; r++)
231    {
232        for (c = 0; c < columns; c++)
233        {
234            TILE_AT (c, r)->vZoom = RAND_VZOOM;
235        }
236    }
237}
238
239/* event: randomize all the velocities */
240static void randomizeAllVelocities (void)
241{
242    randomizeAllAngularVelocities ();
243    randomizeAllZoomularVelocities ();
244}
245
246/* event: randomize all the angular orientations */
247static void randomizeAllAngularOrientations (void)
248{
249    int c;
250    int r;
251
252    for (r = 0; r < rows; r++)
253    {
254        for (c = 0; c < columns; c++)
255        {
256            TILE_AT (c, r)->angle = RAND_ANGLE;
257        }
258    }
259}
260
261/* event: randomize all the zoomular orientations */
262static void randomizeAllZoomularOrientations (void)
263{
264    int c;
265    int r;
266
267    for (r = 0; r < rows; r++)
268    {
269        for (c = 0; c < columns; c++)
270        {
271            TILE_AT (c, r)->zoom = RAND_ZOOM;
272        }
273    }
274}
275
276/* event: randomize all the orientations */
277static void randomizeAllOrientations (void)
278{
279    randomizeAllAngularOrientations ();
280    randomizeAllZoomularOrientations ();
281}
282
283/* event: randomize everything */
284static void randomizeEverything (void)
285{
286    randomizeAllVelocities ();
287    randomizeAllOrientations ();
288}
289
290/* event: pick one tile and randomize all its stats */
291static void randomizeOneTile (void)
292{
293    int c = RAND_FLOAT_01 * columns;
294    int r = RAND_FLOAT_01 * rows;
295
296    Tile *t = TILE_AT (c, r);
297    t->angle = RAND_ANGLE;
298    t->zoom = RAND_ZOOM;
299    t->vAngle = RAND_VANGLE;
300    t->vZoom = RAND_VZOOM;
301}
302
303/* event: pick one row and randomize everything about each of its tiles */
304static void randomizeOneRow (void)
305{
306    int c;
307    int r = RAND_FLOAT_01 * rows;
308
309    for (c = 0; c < columns; c++)
310    {
311        Tile *t = TILE_AT (c, r);
312        t->angle = RAND_ANGLE;
313        t->zoom = RAND_ZOOM;
314        t->vAngle = RAND_VANGLE;
315        t->vZoom = RAND_VZOOM;
316    }
317}
318
319/* event: pick one column and randomize everything about each of its tiles */
320static void randomizeOneColumn (void)
321{
322    int c = RAND_FLOAT_01 * columns;
323    int r;
324
325    for (r = 0; r < rows; r++)
326    {
327        Tile *t = TILE_AT (c, r);
328        t->angle = RAND_ANGLE;
329        t->zoom = RAND_ZOOM;
330        t->vAngle = RAND_VANGLE;
331        t->vZoom = RAND_VZOOM;
332    }
333}
334
335/* do model event processing */
336static void modelEvents (void)
337{
338    int which;
339
340    if (RAND_FLOAT_01 > eventChance)
341    {
342        return;
343    }
344
345    which = RAND_FLOAT_01 * 10;
346
347    switch (which)
348    {
349        case 0: randomizeAllAngularVelocities ();    break;
350        case 1: randomizeAllZoomularVelocities ();   break;
351        case 2: randomizeAllVelocities ();           break;
352        case 3: randomizeAllAngularOrientations ();  break;
353        case 4: randomizeAllZoomularOrientations (); break;
354        case 5: randomizeAllOrientations ();         break;
355        case 6: randomizeEverything ();              break;
356        case 7: randomizeOneTile ();                 break;
357        case 8: randomizeOneColumn ();               break;
358        case 9: randomizeOneRow ();                  break;
359    }
360}
361
362/* update the model for one iteration */
363static void updateModel (void)
364{
365    int r;
366    int c;
367
368    /* for each tile, decrease its velocities according to the friction,
369     * and increase them based on its current orientation and the orientations
370     * of its orthogonal neighbors */
371    for (r = 0; r < rows; r++)
372    {
373        for (c = 0; c < columns; c++)
374        {
375            Tile *t = TILE_AT (c, r);
376            FLOAT a = t->angle;
377            FLOAT z = t->zoom;
378            FLOAT va = t->vAngle;
379            FLOAT vz = t->vZoom;
380
381            va -= t->angle * springiness;
382            vz -= t->zoom * springiness;
383
384            if (c > 0)
385            {
386                Tile *t2 = TILE_AT (c - 1, r);
387                va += (t2->angle - a) * transference;
388                vz += (t2->zoom - z) * transference;
389            }
390
391            if (c < (columns - 1))
392            {
393                Tile *t2 = TILE_AT (c + 1, r);
394                va += (t2->angle - a) * transference;
395                vz += (t2->zoom - z) * transference;
396            }
397
398            if (r > 0)
399            {
400                Tile *t2 = TILE_AT (c, r - 1);
401                va += (t2->angle - a) * transference;
402                vz += (t2->zoom - z) * transference;
403            }
404
405            if (r < (rows - 1))
406            {
407                Tile *t2 = TILE_AT (c, r + 1);
408                va += (t2->angle - a) * transference;
409                vz += (t2->zoom - z) * transference;
410            }
411
412            va *= (1.0 - friction);
413            vz *= (1.0 - friction);
414
415            if (va > MAX_VANGLE) va = MAX_VANGLE;
416            else if (va < -MAX_VANGLE) va = -MAX_VANGLE;
417            t->vAngle = va;
418
419            if (vz > MAX_VZOOM) vz = MAX_VZOOM;
420            else if (vz < -MAX_VZOOM) vz = -MAX_VZOOM;
421            t->vZoom = vz;
422        }
423    }
424
425    /* for each tile, update its orientation based on its velocities */
426    for (r = 0; r < rows; r++)
427    {
428        for (c = 0; c < columns; c++)
429        {
430            Tile *t = TILE_AT (c, r);
431            FLOAT a = t->angle + t->vAngle;
432            FLOAT z = t->zoom + t->vZoom;
433
434            if (a > M_PI) a = M_PI;
435            else if (a < -M_PI) a = -M_PI;
436            t->angle = a;
437
438            if (z > 1.0) z = 1.0;
439            else if (z < -1.0) z = -1.0;
440            t->zoom = z;
441        }
442    }
443}
444
445/* the comparator to us to sort the tiles (used immediately below); it'd
446 * sure be nice if C allowed inner functions (or jeebus-forbid *real
447 * closures*!) */
448static int sortTilesComparator (const void *v1, const void *v2)
449{
450    Tile *t1 = *(Tile **) v1;
451    Tile *t2 = *(Tile **) v2;
452   
453    if (t1->zoom < t2->zoom)
454    {
455        return -1;
456    }
457
458    if (t1->zoom > t2->zoom)
459    {
460        return 1;
461    }
462
463    return 0;
464}
465
466/* sort the tiles in sortedTiles by zoom */
467static void sortTiles (void)
468{
469    qsort (sortedTiles, tileCount, sizeof (Tile *), sortTilesComparator);
470}
471
472/* render the given tile */
473static void renderTile (Tile *t)
474{
475    /* note: the zoom as stored per tile is log-based (centered on 0, with
476     * 0 being no zoom, but the range for zoom-as-drawn is 0.4..2.5,
477     * hence the alteration of t->zoom, below */
478
479    int x, y;
480
481    int tx = t->x;
482    int ty = t->y;
483
484    FLOAT zoom = pow (2.5, t->zoom);
485    FLOAT ang = -t->angle;
486    FLOAT sinAng = sin (ang);
487    FLOAT cosAng = cos (ang);
488
489    FLOAT innerBorder = (tileSize - borderWidth) / 2.0;
490    FLOAT outerBorder = innerBorder + borderWidth;
491
492    int maxCoord = outerBorder * zoom * (fabs (sinAng) + fabs (cosAng));
493    int minX = tx - maxCoord;
494    int maxX = tx + maxCoord;
495    int minY = ty - maxCoord;
496    int maxY = ty + maxCoord;
497
498    FLOAT prey;
499
500    if (minX < 0) minX = 0;
501    if (maxX > windowWidth) maxX = windowWidth;
502    if (minY < 0) minY = 0;
503    if (maxY > windowHeight) maxY = windowHeight;
504
505    sinAng /= zoom;
506    cosAng /= zoom;
507
508    for (y = minY, prey = y - ty; y < maxY; y++, prey++)
509    {
510        FLOAT prex = minX - tx;
511        FLOAT srcx = prex * cosAng - prey * sinAng;
512        FLOAT srcy = prex * sinAng + prey * cosAng;
513
514        for (x = minX;
515             x < maxX;
516             x++, srcx += cosAng, srcy += sinAng)
517        {
518            if ((srcx < -innerBorder) || (srcx >= innerBorder) ||
519                (srcy < -innerBorder) || (srcy >= innerBorder))
520            {
521                if ((srcx < -outerBorder) || (srcx >= outerBorder) ||
522                    (srcy < -outerBorder) || (srcy >= outerBorder))
523                {
524                    continue;
525                }
526                XPutPixel (workImage, x, y, borderPixel);
527            }
528            else
529            {
530                unsigned long p =
531                    XGetPixel (sourceImage, srcx + tx, srcy + ty);
532                XPutPixel (workImage, x, y, p);
533            }
534        }
535    }
536}
537
538/* render and display the current model */
539static void renderFrame (void)
540{
541    int n;
542
543    memcpy (workImage->data, backgroundImage->data,
544            workImage->bytes_per_line * workImage->height);
545
546    sortTiles ();
547
548    for (n = 0; n < tileCount; n++)
549    {
550        renderTile (sortedTiles[n]);
551    }
552
553#ifdef HAVE_XSHM_EXTENSION
554    if (useShm)
555        XShmPutImage (display, window, backgroundGC, workImage, 0, 0, 0, 0,
556                      windowWidth, windowHeight, False);
557    else
558#endif /* HAVE_XSHM_EXTENSION */
559        XPutImage (display, window, backgroundGC, workImage,
560                   0, 0, 0, 0, windowWidth, windowHeight);
561}
562
563/* set up the model */
564static void setupModel (void)
565{
566    int c;
567    int r;
568
569    int leftX; /* x of the center of the top-left tile */
570    int topY;  /* y of the center of the top-left tile */
571
572    if (tileSize > (windowWidth / 2))
573    {
574        tileSize = windowWidth / 2;
575    }
576
577    if (tileSize > (windowHeight / 2))
578    {
579        tileSize = windowHeight / 2;
580    }
581
582    columns = windowWidth / tileSize;
583    rows = windowHeight / tileSize;
584
585    if ((maxColumns != 0) && (columns > maxColumns))
586    {
587        columns = maxColumns;
588    }
589
590    if ((maxRows != 0) && (rows > maxRows))
591    {
592        rows = maxRows;
593    }
594
595    tileCount = rows * columns;
596
597    leftX = (windowWidth - (columns * tileSize) + tileSize) / 2;
598    topY = (windowHeight - (rows * tileSize) + tileSize) / 2;
599
600    tiles = calloc (tileCount, sizeof (Tile));
601    sortedTiles = calloc (tileCount, sizeof (Tile *));
602
603    for (r = 0; r < rows; r++)
604    {
605        for (c = 0; c < columns; c++)
606        {
607            Tile *t = TILE_AT (c, r);
608            t->x = leftX + c * tileSize;
609            t->y = topY + r * tileSize;
610            sortedTiles[c + r * columns] = t;
611        }
612    }
613
614    randomizeEverything ();
615}
616
617/* do one iteration */
618static void oneIteration (void)
619{
620    modelEvents ();
621    updateModel ();
622    renderFrame ();
623}
624
625
626
627/* main and options and stuff */
628
629char *progclass = "Twang";
630
631char *defaults [] = {
632    ".background:       black",
633    ".foreground:       white",
634    "*borderColor:      blue",
635    "*borderWidth:      3",
636    "*delay:            10000",
637    "*eventChance:      0.01",
638    "*friction:         0.05",
639    "*maxColumns:       0",
640    "*maxRows:          0",
641    "*springiness:      0.1",
642    "*tileSize:         120",
643    "*transference:     0.025",
644#ifdef HAVE_XSHM_EXTENSION
645    "*useSHM: True",
646#endif
647    0
648};
649
650XrmOptionDescRec options [] = {
651  { "-border-color",     ".borderColor",    XrmoptionSepArg, 0 },
652  { "-border-width",     ".borderWidth",    XrmoptionSepArg, 0 },
653  { "-delay",            ".delay",          XrmoptionSepArg, 0 },
654  { "-event-chance",     ".eventChance",    XrmoptionSepArg, 0 },
655  { "-friction",         ".friction",       XrmoptionSepArg, 0 },
656  { "-max-columns",      ".maxColumns",     XrmoptionSepArg, 0 },
657  { "-max-rows",         ".maxRows",        XrmoptionSepArg, 0 },
658  { "-springiness",      ".springiness",    XrmoptionSepArg, 0 },
659  { "-tile-size",        ".tileSize",       XrmoptionSepArg, 0 },
660  { "-transference",     ".transference",   XrmoptionSepArg, 0 },
661#ifdef HAVE_XSHM_EXTENSION
662  { "-shm",              ".useSHM",         XrmoptionNoArg, "True" },
663  { "-no-shm",           ".useSHM",         XrmoptionNoArg, "False" },
664#endif
665  { 0, 0, 0, 0 }
666};
667
668/* initialize the user-specifiable params */
669static void initParams (void)
670{
671    int problems = 0;
672
673    borderWidth = get_integer_resource ("borderWidth", "Integer");
674    if (borderWidth < 0)
675    {
676        fprintf (stderr, "error: border width must be at least 0\n");
677        problems = 1;
678    }
679
680    delay = get_integer_resource ("delay", "Delay");
681    if (delay < 0)
682    {
683        fprintf (stderr, "error: delay must be at least 0\n");
684        problems = 1;
685    }
686
687    eventChance = get_float_resource ("eventChance", "Double");
688    if ((eventChance < 0.0) || (eventChance > 1.0))
689    {
690        fprintf (stderr, "error: eventChance must be in the range 0..1\n");
691        problems = 1;
692    }
693
694    friction = get_float_resource ("friction", "Double");
695    if ((friction < 0.0) || (friction > 1.0))
696    {
697        fprintf (stderr, "error: friction must be in the range 0..1\n");
698        problems = 1;
699    }
700
701    maxColumns = get_integer_resource ("maxColumns", "Integer");
702    if (maxColumns < 0)
703    {
704        fprintf (stderr, "error: max columns must be at least 0\n");
705        problems = 1;
706    }
707
708    maxRows = get_integer_resource ("maxRows", "Integer");
709    if (maxRows < 0)
710    {
711        fprintf (stderr, "error: max rows must be at least 0\n");
712        problems = 1;
713    }
714
715    springiness = get_float_resource ("springiness", "Double");
716    if ((springiness < 0.0) || (springiness > 1.0))
717    {
718        fprintf (stderr, "error: springiness must be in the range 0..1\n");
719        problems = 1;
720    }
721
722    tileSize = get_integer_resource ("tileSize", "Integer");
723    if (tileSize < 1)
724    {
725        fprintf (stderr, "error: tile size must be at least 1\n");
726        problems = 1;
727    }
728   
729    transference = get_float_resource ("transference", "Double");
730    if ((transference < 0.0) || (transference > 1.0))
731    {
732        fprintf (stderr, "error: transference must be in the range 0..1\n");
733        problems = 1;
734    }
735
736#ifdef HAVE_XSHM_EXTENSION
737    useShm = get_boolean_resource ("useSHM", "Boolean");
738#endif
739
740    if (problems)
741    {
742        exit (1);
743    }
744}
745
746/* main function */
747void screenhack (Display *dpy, Window win)
748{
749    display = dpy;
750    window = win;
751
752    initParams ();
753    setup ();
754    setupModel ();
755
756    for (;;)
757    {
758        oneIteration ();
759        XSync (dpy, False);
760        screenhack_handle_events (dpy);
761        usleep (delay);
762    }
763}
Note: See TracBrowser for help on using the repository browser.