source: trunk/third/xscreensaver/hacks/petri.c @ 15683

Revision 15683, 17.6 KB checked in by ghudson, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r15682, which included commits to RCS files with non-trunk default branches.
Line 
1/* petri, simulate mold in a petri dish. v2.7
2 * by Dan Bornstein, danfuzz@milk.com
3 * with help from Jamie Zawinski, jwz@jwz.org
4 * Copyright (c) 1992-1999 Dan Bornstein.
5 *
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation.  No representations are made about the suitability of this
11 * software for any purpose.  It is provided "as is" without express or
12 * implied warranty.
13 *
14 *
15 * Brief description of options/resources:
16 *
17 * delay: the delay in microseconds between iterations
18 * size: the size of a cell in pixels
19 * count: the number of different kinds of mold (minimum: 2)
20 * diaglim: the age limit for diagonal growth as a multiplier of orthogonal
21 *   growth (minimum: 1, maximum 2). 1 means square growth, 1.414
22 *   (i.e., sqrt(2)) means approximately circular growth, 2 means diamond
23 *   growth.
24 * anychan: the chance (fraction, between 0 and 1) that at each iteration,
25 *   any new cell will be born
26 * minorchan: the chance (fraction, between 0 and 1) that, given that new
27 *   cells will be added, that only two will be added (a minor cell birth
28 *   event)
29 * instantdeathchan: the chance (fraction, between 0 and 1) that, given
30 *   that death and destruction will happen, that instead of using plague
31 *   cells, death will be instantaneous
32 * minlifespan: the minimum lifespan of a colony (before black death ensues)
33 * maxlifespan: the maximum lifespan of a colony (before black death ensues)
34 * minlifespeed: the minimum speed for living cells as a fraction of the
35 *   maximum possible speed (fraction, between 0 and 1)
36 * maxlifespeed: the maximum speed for living cells as a fraction of the
37 *   maximum possible speed (fraction, between 0 and 1)
38 * mindeathspeed: the minimum speed for black death cells as a fraction of the
39 *   maximum possible speed (fraction, between 0 and 1)
40 * maxdeathspeed: the maximum speed for black death cells as a fraction of the
41 *   maximum possible speed (fraction, between 0 and 1)
42 * originalcolors: if true, count must be 8 or less and the colors are a
43 *   fixed set of primary and secondary colors (the artist's original choices)
44 *
45 * Interesting settings:
46 *
47 *      petri -originalcolors -size 8
48 *      petri -size 2
49 *      petri -size 8 -diaglim 1.8
50 *      petri -diaglim 1.1
51 *
52 *      petri -count 4 -anychan 0.01 -minorchan 1 \
53 *              -minlifespan 2000 -maxlifespan 5000
54 *
55 *      petri -count 3 -anychan 1 -minlifespan 100000 \
56 *              -instantdeathchan 0
57 *
58 *      petri -minlifespeed 0.02 -maxlifespeed 0.03 -minlifespan 1 \
59 *              -maxlifespan 1 -instantdeathchan 0 -minorchan 0 \
60 *              -anychan 0.3 -delay 4000
61 */
62
63#include <math.h>
64#include "screenhack.h"
65#include "spline.h"
66
67#define FLOAT float
68#define RAND_FLOAT (((FLOAT) (random() & 0xffff)) / ((FLOAT) 0x10000))
69
70typedef struct cell_s
71{
72    unsigned char col;              /*  0      */
73    unsigned char isnext;           /*  1      */
74    unsigned char nextcol;          /*  2      */
75                                    /*  3      */
76    struct cell_s *next;            /*  4      */
77    struct cell_s *prev;            /*  8    - */
78    FLOAT speed;                    /* 12      */
79    FLOAT growth;                   /* 16 20 - */
80    FLOAT nextspeed;                /* 20 28   */
81                                    /* 24 36 - */
82} cell;
83
84static int arr_width;
85static int arr_height;
86static int count;
87
88static cell *arr;
89static cell *head;
90static cell *tail;
91static int blastcount;
92
93static Display *display;
94static Window window;
95static GC *coloredGCs;
96
97static int windowWidth;
98static int windowHeight;
99static int xOffset;
100static int yOffset;
101static int xSize;
102static int ySize;
103
104static FLOAT orthlim = 1.0;
105static FLOAT diaglim;
106static FLOAT anychan;
107static FLOAT minorchan;
108static FLOAT instantdeathchan;
109static int minlifespan;
110static int maxlifespan;
111static FLOAT minlifespeed;
112static FLOAT maxlifespeed;
113static FLOAT mindeathspeed;
114static FLOAT maxdeathspeed;
115static Bool originalcolors;
116
117#define cell_x(c) (((c) - arr) % arr_width)
118#define cell_y(c) (((c) - arr) / arr_width)
119
120
121static int random_life_value (void)
122{
123    return (int) ((RAND_FLOAT * (maxlifespan - minlifespan)) + minlifespan);
124}
125
126static void setup_random_colormap (XWindowAttributes *xgwa)
127{
128    XGCValues gcv;
129    int lose = 0;
130    int ncolors = count - 1;
131    int n;
132    XColor *colors = (XColor *) calloc (sizeof(*colors), count*2);
133   
134    colors[0].pixel = get_pixel_resource ("background", "Background",
135                                          display, xgwa->colormap);
136   
137    make_random_colormap (display, xgwa->visual, xgwa->colormap,
138                          colors+1, &ncolors, True, True, 0, True);
139    if (ncolors < 1)
140      {
141        fprintf (stderr, "%s: couldn't allocate any colors\n", progname);
142        exit (-1);
143      }
144   
145    ncolors++;
146    count = ncolors;
147   
148    memcpy (colors + count, colors, count * sizeof(*colors));
149    colors[count].pixel = get_pixel_resource ("foreground", "Foreground",
150                                              display, xgwa->colormap);
151   
152    for (n = 1; n < count; n++)
153    {
154        int m = n + count;
155        colors[n].red = colors[m].red / 2;
156        colors[n].green = colors[m].green / 2;
157        colors[n].blue = colors[m].blue / 2;
158       
159        if (!XAllocColor (display, xgwa->colormap, &colors[n]))
160        {
161            lose++;
162            colors[n] = colors[m];
163        }
164    }
165
166    if (lose)
167    {
168        fprintf (stderr,
169                 "%s: unable to allocate %d half-intensity colors.\n",
170                 progname, lose);
171    }
172   
173    for (n = 0; n < count*2; n++)
174    {
175        gcv.foreground = colors[n].pixel;
176        coloredGCs[n] = XCreateGC (display, window, GCForeground, &gcv);
177    }
178
179    free (colors);
180}
181
182static void setup_original_colormap (XWindowAttributes *xgwa)
183{
184    XGCValues gcv;
185    int lose = 0;
186    int n;
187    XColor *colors = (XColor *) calloc (sizeof(*colors), count*2);
188   
189    colors[0].pixel = get_pixel_resource ("background", "Background",
190                                          display, xgwa->colormap);
191
192    colors[count].pixel = get_pixel_resource ("foreground", "Foreground",
193                                              display, xgwa->colormap);
194
195    for (n = 1; n < count; n++)
196    {
197        int m = n + count;
198        colors[n].red =   ((n & 0x01) != 0) * 0x8000;
199        colors[n].green = ((n & 0x02) != 0) * 0x8000;
200        colors[n].blue =  ((n & 0x04) != 0) * 0x8000;
201
202        if (!XAllocColor (display, xgwa->colormap, &colors[n]))
203        {
204            lose++;
205            colors[n] = colors[0];
206        }
207
208        colors[m].red   = colors[n].red + 0x4000;
209        colors[m].green = colors[n].green + 0x4000;
210        colors[m].blue  = colors[n].blue + 0x4000;
211
212        if (!XAllocColor (display, xgwa->colormap, &colors[m]))
213        {
214            lose++;
215            colors[m] = colors[count];
216        }
217    }
218
219    if (lose)
220    {
221        fprintf (stderr,
222                 "%s: unable to allocate %d colors.\n",
223                 progname, lose);
224    }
225   
226    for (n = 0; n < count*2; n++)
227    {
228        gcv.foreground = colors[n].pixel;
229        coloredGCs[n] = XCreateGC (display, window, GCForeground, &gcv);
230    }
231
232    free (colors);
233}
234
235static void setup_display (void)
236{
237    XWindowAttributes xgwa;
238    Colormap cmap;
239
240    int cell_size = get_integer_resource ("size", "Integer");
241    int osize, alloc_size, oalloc;
242    int mem_throttle = 0;
243    char *s;
244
245    if (cell_size < 1) cell_size = 1;
246
247    osize = cell_size;
248
249    s = get_string_resource ("memThrottle", "MemThrottle");
250    if (s)
251      {
252        int n;
253        char c;
254        if (1 == sscanf (s, " %d M %c", &n, &c) ||
255            1 == sscanf (s, " %d m %c", &n, &c))
256          mem_throttle = n * (1 << 20);
257        else if (1 == sscanf (s, " %d K %c", &n, &c) ||
258                 1 == sscanf (s, " %d k %c", &n, &c))
259          mem_throttle = n * (1 << 10);
260        else if (1 == sscanf (s, " %d %c", &n, &c))
261          mem_throttle = n;
262        else
263          {
264            fprintf (stderr, "%s: invalid memThrottle \"%s\" (try \"10M\")\n",
265                     progname, s);
266            exit (1);
267          }
268       
269        free (s);
270      }
271
272    XGetWindowAttributes (display, window, &xgwa);
273
274    originalcolors = get_boolean_resource ("originalcolors", "Boolean");
275
276    count = get_integer_resource ("count", "Integer");
277    if (count < 2) count = 2;
278
279    /* number of colors can't be greater than the half depth of the screen. */
280    if (count > (1L << (xgwa.depth-1)))
281      count = (1L << (xgwa.depth-1));
282
283    /* Actually, since cell->col is of type char, this has to be small. */
284    if (count >= (1L << ((sizeof(arr[0].col) * 8) - 1)))
285      count = (1L << ((sizeof(arr[0].col) * 8) - 1));
286
287
288    if (originalcolors && (count > 8))
289    {
290        count = 8;
291    }
292
293    coloredGCs = (GC *) calloc (sizeof(GC), count * 2);
294
295    diaglim  = get_float_resource ("diaglim", "Float");
296    if (diaglim < 1.0)
297    {
298        diaglim = 1.0;
299    }
300    else if (diaglim > 2.0)
301    {
302        diaglim = 2.0;
303    }
304    diaglim *= orthlim;
305
306    anychan  = get_float_resource ("anychan", "Float");
307    if (anychan < 0.0)
308    {
309        anychan = 0.0;
310    }
311    else if (anychan > 1.0)
312    {
313        anychan = 1.0;
314    }
315   
316    minorchan = get_float_resource ("minorchan","Float");
317    if (minorchan < 0.0)
318    {
319        minorchan = 0.0;
320    }
321    else if (minorchan > 1.0)
322    {
323        minorchan = 1.0;
324    }
325   
326    instantdeathchan = get_float_resource ("instantdeathchan","Float");
327    if (instantdeathchan < 0.0)
328    {
329        instantdeathchan = 0.0;
330    }
331    else if (instantdeathchan > 1.0)
332    {
333        instantdeathchan = 1.0;
334    }
335
336    minlifespan = get_integer_resource ("minlifespan", "Integer");
337    if (minlifespan < 1)
338    {
339        minlifespan = 1;
340    }
341
342    maxlifespan = get_integer_resource ("maxlifespan", "Integer");
343    if (maxlifespan < minlifespan)
344    {
345        maxlifespan = minlifespan;
346    }
347
348    minlifespeed = get_float_resource ("minlifespeed", "Float");
349    if (minlifespeed < 0.0)
350    {
351        minlifespeed = 0.0;
352    }
353    else if (minlifespeed > 1.0)
354    {
355        minlifespeed = 1.0;
356    }
357
358    maxlifespeed = get_float_resource ("maxlifespeed", "Float");
359    if (maxlifespeed < minlifespeed)
360    {
361        maxlifespeed = minlifespeed;
362    }
363    else if (maxlifespeed > 1.0)
364    {
365        maxlifespeed = 1.0;
366    }
367
368    mindeathspeed = get_float_resource ("mindeathspeed", "Float");
369    if (mindeathspeed < 0.0)
370    {
371        mindeathspeed = 0.0;
372    }
373    else if (mindeathspeed > 1.0)
374    {
375        mindeathspeed = 1.0;
376    }
377
378    maxdeathspeed = get_float_resource ("maxdeathspeed", "Float");
379    if (maxdeathspeed < mindeathspeed)
380    {
381        maxdeathspeed = mindeathspeed;
382    }
383    else if (maxdeathspeed > 1.0)
384    {
385        maxdeathspeed = 1.0;
386    }
387
388    minlifespeed *= diaglim;
389    maxlifespeed *= diaglim;
390    mindeathspeed *= diaglim;
391    maxdeathspeed *= diaglim;
392
393    cmap = xgwa.colormap;
394   
395    windowWidth = xgwa.width;
396    windowHeight = xgwa.height;
397   
398    arr_width = windowWidth / cell_size;
399    arr_height = windowHeight / cell_size;
400
401    alloc_size = sizeof(cell) * arr_width * arr_height;
402    oalloc = alloc_size;
403
404    if (mem_throttle > 0)
405      while (cell_size < windowWidth/10 &&
406             cell_size < windowHeight/10 &&
407             alloc_size > mem_throttle)
408        {
409          cell_size++;
410          arr_width = windowWidth / cell_size;
411          arr_height = windowHeight / cell_size;
412          alloc_size = sizeof(cell) * arr_width * arr_height;
413        }
414
415    if (osize != cell_size)
416      {
417        static int warned = 0;
418        if (!warned)
419          {
420            fprintf (stderr,
421             "%s: throttling cell size from %d to %d because of %dM limit.\n",
422                     progname, osize, cell_size, mem_throttle / (1 << 20));
423            fprintf (stderr, "%s: %dx%dx%d = %.1fM, %dx%dx%d = %.1fM.\n",
424                     progname,
425                     windowWidth, windowHeight, osize,
426                     ((float) oalloc) / (1 << 20),
427                     windowWidth, windowHeight, cell_size,
428                     ((float) alloc_size) / (1 << 20));
429            warned = 1;
430          }
431      }
432
433    xSize = windowWidth / arr_width;
434    ySize = windowHeight / arr_height;
435    if (xSize > ySize)
436    {
437        xSize = ySize;
438    }
439    else
440    {
441        ySize = xSize;
442    }
443   
444    xOffset = (windowWidth - (arr_width * xSize)) / 2;
445    yOffset = (windowHeight - (arr_height * ySize)) / 2;
446
447    if (originalcolors)
448    {
449        setup_original_colormap (&xgwa);
450    }
451    else
452    {
453        setup_random_colormap (&xgwa);
454    }
455}
456
457static void drawblock (int x, int y, unsigned char c)
458{
459  if (xSize == 1 && ySize == 1)
460    XDrawPoint (display, window, coloredGCs[c], x + xOffset, y + yOffset);
461  else
462    XFillRectangle (display, window, coloredGCs[c],
463                    x * xSize + xOffset, y * ySize + yOffset,
464                    xSize, ySize);
465}
466
467static void setup_arr (void)
468{
469    int x, y;
470
471    if (arr != NULL)
472    {
473        free (arr);
474    }
475
476    XFillRectangle (display, window, coloredGCs[0], 0, 0,
477                    windowWidth, windowHeight);
478
479    arr = (cell *) calloc (sizeof(cell), arr_width * arr_height); 
480    if (!arr)
481      {
482        fprintf (stderr, "%s: out of memory allocating %dx%d grid\n",
483                 progname, arr_width, arr_height);
484        exit (1);
485      }
486
487    for (y = 0; y < arr_height; y++)
488    {
489      int row = y * arr_width;
490        for (x = 0; x < arr_width; x++)
491        {
492            arr[row+x].speed = 0.0;
493            arr[row+x].growth = 0.0;
494            arr[row+x].col = 0;
495            arr[row+x].isnext = 0;
496            arr[row+x].next = 0;
497            arr[row+x].prev = 0;
498        }
499    }
500
501    if (head == NULL)
502    {
503        head = (cell *) malloc (sizeof (cell));
504    }
505   
506    if (tail == NULL)
507    {
508        tail = (cell *) malloc (sizeof (cell));
509    }
510
511    head->next = tail;
512    head->prev = head;
513    tail->next = tail;
514    tail->prev = head;
515
516    blastcount = random_life_value ();
517}
518
519static void newcell (cell *c, unsigned char col, FLOAT sp)
520{
521    if (! c) return;
522   
523    if (c->col == col) return;
524   
525    c->nextcol = col;
526    c->nextspeed = sp;
527    c->isnext = 1;
528   
529    if (c->prev == 0) {
530        c->next = head->next;
531        c->prev = head;
532        head->next = c;
533        c->next->prev = c;
534    }
535}
536
537static void killcell (cell *c)
538{
539    c->prev->next = c->next;
540    c->next->prev = c->prev;
541    c->prev = 0;
542    c->speed = 0.0;
543    drawblock (cell_x(c), cell_y(c), c->col);
544}
545
546
547static void randblip (int doit)
548{
549    int n;
550    int b = 0;
551    if (!doit
552        && (blastcount-- >= 0)
553        && (RAND_FLOAT > anychan))
554    {
555        return;
556    }
557   
558    if (blastcount < 0)
559    {
560        b = 1;
561        n = 2;
562        blastcount = random_life_value ();
563        if (RAND_FLOAT < instantdeathchan)
564        {
565            /* clear everything every so often to keep from getting into a
566             * rut */
567            setup_arr ();
568            b = 0;
569        }
570    }
571    else if (RAND_FLOAT <= minorchan)
572    {
573        n = 2;
574    }
575    else
576    {
577        n = random () % 3 + 3;
578    }
579   
580    while (n--)
581    {
582        int x = random () % arr_width;
583        int y = random () % arr_height;
584        int c;
585        FLOAT s;
586        if (b)
587        {
588            c = 0;
589            s = RAND_FLOAT * (maxdeathspeed - mindeathspeed) + mindeathspeed;
590        }
591        else
592        {
593            c = (random () % (count-1)) + 1;
594            s = RAND_FLOAT * (maxlifespeed - minlifespeed) + minlifespeed;
595        }
596        newcell (&arr[y * arr_width + x], c, s);
597    }
598}
599
600static void update (void)
601{
602    cell *a;
603   
604    for (a = head->next; a != tail; a = a->next)
605    {
606        static XPoint all_coords[] = {{-1, -1}, {-1, 1}, {1, -1}, {1, 1},
607                                      {-1,  0}, { 1, 0}, {0, -1}, {0, 1},
608                                      {99, 99}};
609
610        XPoint *coords = 0;
611
612        if (a->speed == 0) continue;
613        a->growth += a->speed;
614
615        if (a->growth >= diaglim)
616        {
617            coords = all_coords;
618        }
619        else if (a->growth >= orthlim)
620        {
621            coords = &all_coords[4];
622        }
623        else
624        {
625            continue;
626        }
627
628        while (coords->x != 99)
629        {
630            int x = cell_x(a) + coords->x;
631            int y = cell_y(a) + coords->y;
632            coords++;
633           
634            if (x < 0) x = arr_width - 1;
635            else if (x >= arr_width) x = 0;
636           
637            if (y < 0) y = arr_height - 1;
638            else if (y >= arr_height) y = 0;
639           
640            newcell (&arr[y * arr_width + x], a->col, a->speed);
641        }
642
643        if (a->growth >= diaglim)
644            killcell (a);
645    }
646   
647    randblip ((head->next) == tail);
648   
649    for (a = head->next; a != tail; a = a->next)
650    {
651        if (a->isnext)
652        {
653            a->isnext = 0;
654            a->speed = a->nextspeed;
655            a->growth = 0.0;
656            a->col = a->nextcol;
657            drawblock (cell_x(a), cell_y(a), a->col + count);
658        }
659    }
660}
661
662
663char *progclass = "Petri";
664
665char *defaults [] = {
666  ".background:         black",
667  ".foreground:         white",
668  "*delay:              10000",
669  "*count:              8",
670  "*size:               4",
671  "*diaglim:            1.414",
672  "*anychan:            0.0015",
673  "*minorchan:          0.5",
674  "*instantdeathchan:   0.2",
675  "*minlifespan:        500",
676  "*maxlifespan:        1500",
677  "*minlifespeed:       0.04",
678  "*maxlifespeed:       0.13",
679  "*mindeathspeed:      0.42",
680  "*maxdeathspeed:      0.46",
681  "*originalcolors:     false",
682  "*memThrottle:        22M",   /* don't malloc more than this much.
683                                   Scale the pixels up if necessary. */
684    0
685};
686
687XrmOptionDescRec options [] = {
688  { "-delay",            ".delay",              XrmoptionSepArg, 0 },
689  { "-size",             ".size",               XrmoptionSepArg, 0 },
690  { "-count",            ".count",              XrmoptionSepArg, 0 },
691  { "-diaglim",          ".diaglim",            XrmoptionSepArg, 0 },
692  { "-anychan",          ".anychan",            XrmoptionSepArg, 0 },
693  { "-minorchan",        ".minorchan",          XrmoptionSepArg, 0 },
694  { "-instantdeathchan", ".instantdeathchan",   XrmoptionSepArg, 0 },
695  { "-minlifespan",      ".minlifespan",        XrmoptionSepArg, 0 },
696  { "-maxlifespan",      ".maxlifespan",        XrmoptionSepArg, 0 },
697  { "-minlifespeed",     ".minlifespeed",       XrmoptionSepArg, 0 },
698  { "-maxlifespeed",     ".maxlifespeed",       XrmoptionSepArg, 0 },
699  { "-mindeathspeed",    ".mindeathspeed",      XrmoptionSepArg, 0 },
700  { "-maxdeathspeed",    ".maxdeathspeed",      XrmoptionSepArg, 0 },
701  { "-originalcolors",   ".originalcolors",     XrmoptionNoArg,  "true" },
702  { "-mem-throttle",     ".memThrottle",        XrmoptionSepArg,  0 },
703  { 0, 0, 0, 0 }
704};
705
706void screenhack (Display *dpy, Window win)
707{
708    int delay = get_integer_resource ("delay", "Delay");
709    display = dpy;
710    window = win;
711    setup_display ();
712   
713    setup_arr ();
714   
715    randblip (1);
716   
717    for (;;)
718    {
719        update ();
720        XSync (dpy, False);
721        screenhack_handle_events (dpy);
722        usleep (delay);
723    }
724}
Note: See TracBrowser for help on using the repository browser.