/* -*- Mode: C; tab-width: 4 -*- */ /* petri --- */ #if !defined( lint ) && !defined( SABER ) static const char sccsid[] = "@(#)petri.c 5.04 2002/07/19 xlockmore"; #endif /* petri, simulate mold in a petri dish. v2.7 * by Dan Bornstein, danfuzz@milk.com * with help from Jamie Zawinski, jwz@jwz.org * Copyright (c) 1992-1999 Dan Bornstein. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. * * * Brief description of options/resources: * * delay: the delay in microseconds between iterations * size: the size of a cell in pixels * count: the number of different kinds of mold (minimum: 2) * diaglim: the age limit for diagonal growth as a multiplier of orthogonal * growth (minimum: 1, maximum 2). 1 means square growth, 1.414 * (i.e., sqrt(2)) means approximately circular growth, 2 means diamond * growth. * anychan: the chance (fraction, between 0 and 1) that at each iteration, * any new cell will be born * minorchan: the chance (fraction, between 0 and 1) that, given that new * cells will be added, that only two will be added (a minor cell birth * event) * instantdeathchan: the chance (fraction, between 0 and 1) that, given * that death and destruction will happen, that instead of using plague * cells, death will be instantaneous * minlifespan: the minimum lifespan of a colony (before black death ensues) * maxlifespan: the maximum lifespan of a colony (before black death ensues) * minlifespeed: the minimum speed for living cells as a fraction of the * maximum possible speed (fraction, between 0 and 1) * maxlifespeed: the maximum speed for living cells as a fraction of the * maximum possible speed (fraction, between 0 and 1) * mindeathspeed: the minimum speed for black death cells as a fraction of the * maximum possible speed (fraction, between 0 and 1) * maxdeathspeed: the maximum speed for black death cells as a fraction of the * maximum possible speed (fraction, between 0 and 1) * originalcolors: if true, count must be 8 or less and the colors are a * fixed set of primary and secondary colors (the artist's original choices) * * Interesting settings: * * petri -originalcolors -size 8 * petri -size 2 * petri -size 8 -diaglim 1.8 * petri -diaglim 1.1 * * petri -count 4 -anychan 0.01 -minorchan 1 \ * -minlifespan 2000 -maxlifespan 5000 * * petri -count 3 -anychan 1 -minlifespan 100000 \ * -instantdeathchan 0 * * petri -minlifespeed 0.02 -maxlifespeed 0.03 -minlifespan 1 \ * -maxlifespan 1 -instantdeathchan 0 -minorchan 0 \ * -anychan 0.3 -delay 4000 * * Revision History: * 22 Jul-2002: xlockmore version by Jouk Jansen */ #ifdef STANDALONE #define MODE_petri #define PROGCLASS "Petri" #define HACK_INIT init_petri #define HACK_DRAW draw_petri #define petri_opts xlockmore_opts #define DEFAULTS "*delay: 10000 \n" \ "*size: 4 \n" \ "*ncolors: 8 \n" \ "*fullrandom: True \n" \ "*verbose: False \n" #include "xlockmore.h" /* in xscreensaver distribution */ #else /* STANDALONE */ #include "xlock.h" /* in xlockmore distribution */ #include "color.h" #include "automata.h" #endif /* STANDALONE */ #ifdef MODE_petri #include #define FLOAT float #define RAND_FLOAT (((FLOAT) (LRAND() & 0xffff)) / ((FLOAT) 0x10000)) #define DEF_MEMTHROTTLE "22M" #define DEF_DIAGLIM "1.414" #define DEF_ANYCHAN "0.0015" #define DEF_MINORCHAN "0.5" #define DEF_INSTANTDEATHCHAN "0.2" #define DEF_MINLIFESPAN "500" #define DEF_MAXLIFESPAN "1500" #define DEF_MINLIFESPEED "0.04" #define DEF_MAXLIFESPEED "0.13" #define DEF_MINDEATHSPEED "0.42" #define DEF_MAXDEATHSPEED "0.46" #define REDRAWSTEP 2000 /* How many cells to draw per cycle */ static char* memThrottle; static FLOAT st_diaglim; static FLOAT orthlim = 1.0; static FLOAT st_anychan; static FLOAT st_minorchan; static FLOAT st_instantdeathchan; static int st_minlifespan; static int st_maxlifespan; static FLOAT st_minlifespeed; static FLOAT st_maxlifespeed; static FLOAT st_mindeathspeed; static FLOAT st_maxdeathspeed; static XrmOptionDescRec opts[] = { {(char *) "-anychan", (char *) ".petri.anychan", XrmoptionSepArg, (caddr_t) NULL}, {(char *) "-diaglim", (char *) ".petri.diaglim", XrmoptionSepArg, (caddr_t) NULL}, {(char *) "-instantdeathchan", (char *) ".petri.instantdeathchan", XrmoptionSepArg, (caddr_t) NULL}, {(char *) "-memthrottle", (char *) ".petri.memthrottle", XrmoptionSepArg, (caddr_t) NULL}, {(char *) "-maxlifespan", (char *) ".petri.maxlifespan", XrmoptionSepArg, (caddr_t) NULL}, {(char *) "-minlifespan", (char *) ".petri.minlifespan", XrmoptionSepArg, (caddr_t) NULL}, {(char *) "-maxlifespeed", (char *) ".petri.maxlifespeed", XrmoptionSepArg, (caddr_t) NULL}, {(char *) "-minlifespeed", (char *) ".petri.minlifespeed", XrmoptionSepArg, (caddr_t) NULL}, {(char *) "-maxdeathspeed", (char *) ".petri.maxdeathspeed", XrmoptionSepArg, (caddr_t) NULL}, {(char *) "-mindeathspeed", (char *) ".petri.mindeathspeed", XrmoptionSepArg, (caddr_t) NULL}, {(char *) "-minorchan", (char *) ".petri.minorchan", XrmoptionSepArg, (caddr_t) NULL} }; static argtype vars[] = { {(void *) &st_anychan, (char *) "anychan", (char *) "Anychan", (char *) DEF_ANYCHAN , t_Float}, {(void *) &st_diaglim, (char *) "diaglim", (char *) "Diaglim", (char *) DEF_DIAGLIM , t_Float}, {(void *) &st_instantdeathchan, (char *) "instantdeathchan", (char *) "Instantdeathchan", (char *) DEF_DIAGLIM , t_Float}, {(void *) &memThrottle, (char *) "memthrottle", (char *) "Memthrottle", (char *) DEF_MEMTHROTTLE , t_String}, {(void *) &st_maxlifespan, (char *) "maxlifespan", (char *) "Maxlifespan", (char *) DEF_MAXLIFESPAN , t_Int}, {(void *) &st_minlifespan, (char *) "minlifespan", (char *) "Minlifespan", (char *) DEF_MINLIFESPAN , t_Int}, {(void *) &st_maxlifespeed, (char *) "maxlifespeed", (char *) "Maxlifespeed", (char *) DEF_MAXLIFESPEED , t_Float}, {(void *) &st_minlifespeed, (char *) "minlifespeed", (char *) "Minlifespeed", (char *) DEF_MINLIFESPEED , t_Float}, {(void *) &st_maxdeathspeed, (char *) "maxdeathspeed", (char *) "Maxdeathspeed", (char *) DEF_MAXDEATHSPEED , t_Float}, {(void *) &st_mindeathspeed, (char *) "mindeathspeed", (char *) "Mindeathspeed", (char *) DEF_MINDEATHSPEED , t_Float}, {(void *) &st_minorchan, (char *) "minorchan", (char *) "Minorchan", (char *) DEF_MINORCHAN , t_Float} }; static OptionStruct desc[] = { {(char *) "-anychan float", (char *) "The chance that new cell will be born"}, {(char *) "-diaglim float", (char *) "The age limit for diagonal growth"}, {(char *) "-instantdeathchan float", (char *) "Instant death chance"}, {(char *) "-mem-throttle string", (char *) "memThrottle"}, {(char *) "-maxlifespan num", (char *) "the maximum lifespan of a colony"}, {(char *) "-minlifespan num", (char *) "the minimum lifespan of a colony"}, {(char *) "-maxlifespeed float", (char *) "the maximum speed for living cells"}, {(char *) "-minlifespeed float", (char *) "the minimum speed for living cells"}, {(char *) "-maxdeathspeed float", (char *) "the maximum speed for black death cells"}, {(char *) "-mindeathspeed float", (char *) "the minimum speed for black death cells"}, {(char *) "-minorchan float", (char *) "The chance on a minor cell birth"} }; ModeSpecOpt petri_opts = {sizeof opts / sizeof opts[0], opts, sizeof vars / sizeof vars[0], vars, desc}; #ifdef USE_MODULES ModStruct petri_description = {"petri", "init_petri", "draw_petri", "release_petri", "refresh_petri", "init_petri", (char *) NULL, &petri_opts, 10000, 1, 1, 4, 8, 1.0, "", "Shows a mold simulation in a petri dish", 0, NULL}; #endif typedef struct cell_s { unsigned char col; /* 0 */ unsigned char isnext; /* 1 */ unsigned char nextcol; /* 2 */ /* 3 */ struct cell_s *next; /* 4 */ struct cell_s *prev; /* 8 - */ FLOAT speed; /* 12 */ FLOAT growth; /* 16 20 - */ FLOAT nextspeed; /* 20 28 */ /* 24 36 - */ } cell; #define PETRIBITS(n,w,h)\ if ((sp->pixmaps[sp->init_bits]=\ XCreatePixmapFromBitmapData(display,window,(char *)n,w,h,1,0,1))==None){\ free_petri(display,sp); return False;} else {sp->init_bits++;} #define cell_x(c) (((c) - sp->arr) % sp->arr_width) #define cell_y(c) (((c) - sp->arr) / sp->arr_width) typedef struct { cell *arr; cell *head; cell *tail; int count; Bool originalcolors; GC *coloredGCs; int blastcount; int windowWidth; int windowHeight; int arr_width; int arr_height; int xSize; int ySize; int xOffset; int yOffset; FLOAT diaglim; FLOAT anychan; FLOAT minorchan; FLOAT instantdeathchan; int minlifespan; int maxlifespan; FLOAT minlifespeed; FLOAT maxlifespeed; FLOAT mindeathspeed; FLOAT maxdeathspeed; int redrawing, redrawpos; Colormap cmap; Bool mono; int init_bits; unsigned char colors[NUMSTIPPLES - 1]; GC stippledGC; Pixmap pixmaps[NUMSTIPPLES - 1]; } petristruct; static petristruct *petries = (petristruct *) NULL; static int random_life_value (petristruct *sp) { return (int) ((RAND_FLOAT * (sp->maxlifespan - sp->minlifespan)) + sp->minlifespan); } static void free_petri(Display *display, petristruct *sp) { int n, shade; if (sp->coloredGCs != NULL) { for (n = 0; n < sp->count*2; n++) if (sp->coloredGCs[n] != None) XFreeGC(display, sp->coloredGCs[n]); free(sp->coloredGCs); sp->coloredGCs = (GC *) NULL; } if (sp->stippledGC != None) { XFreeGC(display, sp->stippledGC); sp->stippledGC = None; } for (shade = 0; shade < sp->init_bits; shade++) { XFreePixmap(display, sp->pixmaps[shade]); } sp->init_bits = 0; if (sp->arr != NULL) { free(sp->arr); sp->arr = (cell *) NULL; } if (sp->head != NULL) { free(sp->head); sp->head = (cell *) NULL; } if (sp->tail != NULL) { free(sp->tail); sp->tail = (cell *) NULL; } } static int setup_random_colormap (ModeInfo * mi) { petristruct *sp = &petries[MI_SCREEN(mi)]; int lose = 0; int ncolors = sp->count - 1; int n; XColor *colors = (XColor *) NULL; Display *display = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); colors = (XColor *) calloc (sizeof(*colors), sp->count*2); if (colors == NULL) { free_petri(display, sp); return False; } colors[0].pixel = MI_BLACK_PIXEL(mi); if (MI_NPIXELS(mi) <= 2) { sp->mono = True; } else make_random_colormap ( #ifdef STANDALONE display , window , #else mi , #endif sp->cmap , colors+1, &ncolors, True, True, 0); if (ncolors < 1) { sp->mono = True; } ncolors++; sp->count = ncolors; if (sp->mono) { if (colors != NULL) { XFree((caddr_t) colors); colors = (XColor *) NULL; } if (2 * sp->count > NUMSTIPPLES - 1) sp->count = (NUMSTIPPLES - 1) / 2; if (sp->stippledGC == None) { XGCValues gcv; gcv.fill_style = FillOpaqueStippled; if ((sp->stippledGC = XCreateGC(display, window, GCFillStyle, &gcv)) == None) { free_petri(display, sp); return False; } } if (sp->init_bits == 0) { int i; for (i = 1; i < 2 * sp->count; i++) { PETRIBITS(stipples[i], STIPPLESIZE, STIPPLESIZE); } } return True; } (void) memcpy (colors + sp->count, colors, sp->count * sizeof(*colors)); colors[sp->count].pixel = MI_WHITE_PIXEL(mi); for (n = 1; n < sp->count; n++) { int m = n + sp->count; colors[n].red = colors[m].red / 2; colors[n].green = colors[m].green / 2; colors[n].blue = colors[m].blue / 2; if (!XAllocColor (display, sp->cmap, &colors[n])) { lose++; colors[n] = colors[m]; } } if (lose) { (void) fprintf (stderr, "petri: unable to allocate %d half-intensity colors.\n", lose); } for (n = 0; n < sp->count*2; n++) { XGCValues gcv; gcv.foreground = colors[n].pixel; sp->coloredGCs[n] = XCreateGC (display, window, GCForeground, &gcv); } if (colors != NULL) { XFree((caddr_t) colors); colors = (XColor *) NULL; } return True; } static int setup_original_colormap (ModeInfo * mi) { petristruct *sp = &petries[MI_SCREEN(mi)]; int lose = 0; int n; XColor *colors = (XColor *) NULL; Display *display = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); if (MI_NPIXELS(mi) <= 2) sp->mono = True; if (sp->mono) { if (2 * sp->count > NUMSTIPPLES - 1) sp->count = (NUMSTIPPLES - 1) / 2; if (sp->stippledGC == None) { XGCValues gcv; gcv.fill_style = FillOpaqueStippled; if ((sp->stippledGC = XCreateGC(display, window, GCFillStyle, &gcv)) == None) { free_petri(display, sp); return False; } } if (sp->init_bits == 0) { int i; for (i = 1; i < 2 * sp->count; i++) { PETRIBITS(stipples[i], STIPPLESIZE, STIPPLESIZE); } } return True; } colors = (XColor *) calloc (sizeof(*colors), sp->count*2); if (colors == NULL) { free_petri(display, sp); return False; } colors[0].pixel = MI_BLACK_PIXEL(mi); colors[sp->count].pixel = MI_WHITE_PIXEL(mi); for (n = 1; n < sp->count; n++) { int m = n + sp->count; colors[n].red = ((n & 0x01) != 0) * 0x8000; colors[n].green = ((n & 0x02) != 0) * 0x8000; colors[n].blue = ((n & 0x04) != 0) * 0x8000; if (!sp->mono && !XAllocColor (display, sp->cmap, &colors[n])) { lose++; colors[n] = colors[0]; } colors[m].red = colors[n].red + 0x4000; colors[m].green = colors[n].green + 0x4000; colors[m].blue = colors[n].blue + 0x4000; if (!XAllocColor (display, sp->cmap, &colors[m])) { lose++; colors[m] = colors[sp->count]; } } if (lose) { (void) fprintf (stderr, "petri: unable to allocate %d colors.\n", lose); } for (n = 0; n < sp->count*2; n++) { XGCValues gcv; gcv.foreground = colors[n].pixel; sp->coloredGCs[n] = XCreateGC (display, window, GCForeground, &gcv); } if (colors != NULL) { XFree((caddr_t) colors); colors = (XColor *) NULL; } return True; } static int setup_display (ModeInfo * mi) { petristruct *sp = &petries[MI_SCREEN(mi)]; Display *display = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); XWindowAttributes xgwa; int cell_size = MI_SIZE( mi ); int osize, alloc_size, oalloc; int mem_throttle = 0; char *s; if (cell_size < 1) cell_size = 1; osize = cell_size; s = (char*) malloc( strlen( memThrottle ) + 1); if (s) { int n; char c; (void) strcpy( s , memThrottle ); if (1 == sscanf (s, " %d M %c", &n, &c) || 1 == sscanf (s, " %d m %c", &n, &c)) mem_throttle = n * (1 << 20); else if (1 == sscanf (s, " %d K %c", &n, &c) || 1 == sscanf (s, " %d k %c", &n, &c)) mem_throttle = n * (1 << 10); else if (1 == sscanf (s, " %d %c", &n, &c)) mem_throttle = n; else { (void) fprintf (stderr, "petri: invalid memThrottle \"%s\" (try \"10M\")\n", s); free_petri(display, sp); return False; } free(s); } (void) XGetWindowAttributes (display, window, &xgwa); sp->originalcolors = ( NRAND( 3 ) == 1 ); sp->count = MI_NCOLORS(mi); if ( sp->count < 2) sp->count = 2; /* number of colors can't be greater than the half depth of the screen. */ if ( sp->count > (1L << (xgwa.depth-1))) sp->count = (1L << (xgwa.depth-1)); /* Actually, since cell->col is of type char, this has to be small. */ if ( sp->count >= (1L << ((sizeof( sp->arr[0].col) * 8) - 1))) sp->count = (1L << ((sizeof( sp->arr[0].col) * 8) - 1)); if ( sp->originalcolors && ( sp->count > 8)) { sp->count = 8; } sp->coloredGCs = (GC *) calloc (sizeof(GC), sp->count * 2); if (sp->coloredGCs == NULL) { free_petri(display, sp); return False; } if (MI_IS_FULLRANDOM(mi)) sp->diaglim = 1.0 + (float) LRAND() / (float) MAXRAND; else sp->diaglim = st_diaglim; if (sp->diaglim < 1.0) { sp->diaglim = 1.0; } else if (sp->diaglim > 2.0) { sp->diaglim = 2.0; } sp->diaglim *= orthlim; if (MI_IS_FULLRANDOM(mi)) sp->anychan = (float) pow( (double) LRAND() / (double) MAXRAND , 15.0 ); else sp->anychan = st_anychan; if (sp->anychan < 0.0) { sp->anychan = 0.0; } else if (sp->anychan > 1.0) { sp->anychan = 1.0; } if (MI_IS_FULLRANDOM(mi)) sp->minorchan = (float) LRAND() / (float) MAXRAND; else sp->minorchan = st_minorchan; if (sp->minorchan < 0.0) { sp->minorchan = 0.0; } else if (sp->minorchan > 1.0) { sp->minorchan = 1.0; } if (MI_IS_FULLRANDOM(mi)) sp->instantdeathchan = (float) pow( (double) LRAND() / (double) MAXRAND , 8.0 ); else sp->instantdeathchan = st_instantdeathchan; if (sp->instantdeathchan < 0.0) { sp->instantdeathchan = 0.0; } else if (sp->instantdeathchan > 1.0) { sp->instantdeathchan = 1.0; } if (MI_IS_FULLRANDOM(mi)) sp->minlifespan = NRAND( st_minlifespan ) + 1; else sp->minlifespan = st_minlifespan; if (sp->minlifespan < 1) { sp->minlifespan = 1; } if (MI_IS_FULLRANDOM(mi)) sp->maxlifespan = NRAND( st_maxlifespan ) + sp->minlifespan; else sp->maxlifespan = st_maxlifespan; if (sp->maxlifespan < sp->minlifespan) { sp->maxlifespan = sp->minlifespan; } if (MI_IS_FULLRANDOM(mi)) sp->minlifespeed = st_maxlifespeed * (float) LRAND() / (float) MAXRAND; else sp->minlifespeed = st_minlifespeed; if (sp->minlifespeed < 0.0) { sp->minlifespeed = 0.0; } else if (sp->minlifespeed > 1.0 ) { sp->minlifespeed = 1.0; } if (MI_IS_FULLRANDOM(mi)) sp->maxlifespeed = ( (st_maxlifespeed - sp->minlifespeed ) * (float) LRAND() / (float) MAXRAND ) + sp->minlifespeed; else sp->maxlifespeed = st_maxlifespeed; if (sp->maxlifespeed < sp->minlifespeed) { sp->maxlifespeed = sp->minlifespeed; } else if (sp->maxlifespeed > 1.0) { sp->maxlifespeed = 1.0; } if (MI_IS_FULLRANDOM(mi)) sp->mindeathspeed = st_maxdeathspeed * (float) LRAND() / (float) MAXRAND; else sp->mindeathspeed = st_mindeathspeed; if (sp->mindeathspeed < 0.0) { sp->mindeathspeed = 0.0; } else if (sp->mindeathspeed > 1.0) { sp->mindeathspeed = 1.0; } if (MI_IS_FULLRANDOM(mi)) sp->maxdeathspeed = ( (st_maxdeathspeed - sp->mindeathspeed ) * (float) LRAND() / (float) MAXRAND ) + sp->mindeathspeed; else sp->maxdeathspeed = st_maxdeathspeed; if (sp->maxdeathspeed < sp->mindeathspeed) { sp->maxdeathspeed = sp->mindeathspeed; } else if (sp->maxdeathspeed > 1.0) { sp->maxdeathspeed = 1.0; } sp->minlifespeed *= sp->diaglim; sp->maxlifespeed *= sp->diaglim; sp->mindeathspeed *= sp->diaglim; sp->maxdeathspeed *= sp->diaglim; sp->cmap = xgwa.colormap; sp->windowWidth = xgwa.width; sp->windowHeight = xgwa.height; sp->arr_width = sp->windowWidth / cell_size; sp->arr_height = sp->windowHeight / cell_size; alloc_size = sizeof(cell) * sp->arr_width * sp->arr_height; oalloc = alloc_size; if (mem_throttle > 0) while (cell_size < sp->windowWidth/10 && cell_size < sp->windowHeight/10 && alloc_size > mem_throttle) { cell_size++; sp->arr_width = sp->windowWidth / cell_size; sp->arr_height = sp->windowHeight / cell_size; alloc_size = sizeof(cell) * sp->arr_width * sp->arr_height; } if (osize != cell_size) { static int warned = 0; if (!warned) { (void) fprintf (stderr, "petri: throttling cell size from %d to %d because of %dM limit.\n", osize, cell_size, mem_throttle / (1 << 20)); (void) fprintf (stderr, "petri: %dx%dx%d = %.1fM, %dx%dx%d = %.1fM.\n", sp->windowWidth, sp->windowHeight, osize, ((float) oalloc) / (1 << 20), sp->windowWidth, sp->windowHeight, cell_size, ((float) alloc_size) / (1 << 20)); warned = 1; } } sp->xSize = sp->windowWidth / sp->arr_width; sp->ySize = sp->windowHeight / sp->arr_height; if (sp->xSize > sp->ySize) { sp->xSize = sp->ySize; } else { sp->ySize = sp->xSize; } sp->xOffset = (sp->windowWidth - (sp->arr_width * sp->xSize)) / 2; sp->yOffset = (sp->windowHeight - (sp->arr_height * sp->ySize)) / 2; if (sp->originalcolors) { if (!setup_original_colormap (mi)) return False; } else { /* if (!setup_random_colormap (mi)) *//* PseudoColor can error out */ if (!setup_original_colormap (mi)) return False; } return True; } static void drawblock (ModeInfo * mi , int x, int y, unsigned char c) { petristruct *sp = &petries[MI_SCREEN(mi)]; Display *display = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); if (sp->mono) { if (c == 0) { XSetForeground(display, MI_GC(mi), MI_BLACK_PIXEL(mi)); XFillRectangle (display, window, MI_GC(mi), x * sp->xSize + sp->xOffset, y * sp->ySize + sp->yOffset, sp->xSize, sp->ySize); } else { XGCValues gcv; gcv.stipple = sp->pixmaps[c - 1]; gcv.foreground = MI_WHITE_PIXEL(mi); gcv.background = MI_BLACK_PIXEL(mi); XChangeGC(display, sp->stippledGC, GCStipple | GCForeground | GCBackground, &gcv); XFillRectangle (display, window, sp->stippledGC, x * sp->xSize + sp->xOffset, y * sp->ySize + sp->yOffset, sp->xSize, sp->ySize); } } else if (sp->xSize == 1 && sp->ySize == 1) XDrawPoint (display, window, sp->coloredGCs[c], x + sp->xOffset, y + sp->yOffset); else XFillRectangle (display, window, sp->coloredGCs[c], x * sp->xSize + sp->xOffset, y * sp->ySize + sp->yOffset, sp->xSize, sp->ySize); } static int setup_arr (ModeInfo * mi) { petristruct *sp = &petries[MI_SCREEN(mi)]; int x, y; Display *display = MI_DISPLAY(mi); Window window = MI_WINDOW(mi); if (sp->arr != NULL) { free(sp->arr); } if (sp->mono) { XSetForeground(display, MI_GC(mi), MI_BLACK_PIXEL(mi)); XFillRectangle (display, window, MI_GC(mi), 0, 0, sp->windowWidth, sp->windowHeight); } else XFillRectangle (display, window, sp->coloredGCs[0], 0, 0, sp->windowWidth, sp->windowHeight); sp->arr = (cell *) calloc (sizeof(cell), sp->arr_width * sp->arr_height); if (!sp->arr) { (void) fprintf (stderr, "petri: out of memory allocating %dx%d grid\n", sp->arr_width, sp->arr_height); free_petri(display, sp); return False; } for (y = 0; y < sp->arr_height; y++) { int row = y * sp->arr_width; for (x = 0; x < sp->arr_width; x++) { sp->arr[row+x].speed = 0.0; sp->arr[row+x].growth = 0.0; sp->arr[row+x].col = 0; sp->arr[row+x].isnext = 0; sp->arr[row+x].next = 0; sp->arr[row+x].prev = 0; } } if (sp->head == NULL) { sp->head = (cell *) malloc (sizeof (cell)); if (!sp->head) { (void) fprintf (stderr, "petri: out of memory allocating %dx%d grid\n", sp->arr_width, sp->arr_height); free_petri(display, sp); return False; } } if (sp->tail == NULL) { sp->tail = (cell *) malloc (sizeof (cell)); if (!sp->tail) { (void) fprintf (stderr, "petri: out of memory allocating %dx%d grid\n", sp->arr_width, sp->arr_height); free_petri(display, sp); return False; } } sp->head->next = sp->tail; sp->head->prev = sp->head; sp->tail->next = sp->tail; sp->tail->prev = sp->head; sp->blastcount = random_life_value (sp); return True; } static void newcell (ModeInfo * mi , cell *c, unsigned char col, FLOAT spf) { petristruct *sp = &petries[MI_SCREEN(mi)]; if (! c) return; if (c->col == col) return; c->nextcol = col; c->nextspeed = spf; c->isnext = 1; if (c->prev == 0) { c->next = sp->head->next; c->prev = sp->head; sp->head->next = c; c->next->prev = c; } } static void killcell (ModeInfo * mi , cell *c) { petristruct *sp = &petries[MI_SCREEN(mi)]; c->prev->next = c->next; c->next->prev = c->prev; c->prev = 0; c->speed = 0.0; drawblock (mi , cell_x(c), cell_y(c), c->col); } static int randblip (ModeInfo * mi , int doit) { petristruct *sp = &petries[MI_SCREEN(mi)]; int n; int b = 0; if (!doit && (sp->blastcount-- >= 0) && (RAND_FLOAT > sp->anychan)) { return True; } if (sp->blastcount < 0) { b = 1; n = 2; sp->blastcount = random_life_value(sp); if (RAND_FLOAT < sp->instantdeathchan) { /* clear everything every so often to keep from getting into a * rut */ if (!setup_arr(mi)) { return False; } b = 0; } } else if (RAND_FLOAT <= sp->minorchan) { n = 2; } else { n = NRAND(3) + 3; } while (n--) { int x = NRAND(sp->arr_width); int y = NRAND(sp->arr_height); int c; FLOAT s; if (b) { c = 0; s = RAND_FLOAT * (sp->maxdeathspeed - sp->mindeathspeed) + sp->mindeathspeed; } else { if ( sp->count <= 1 ) c = 1; else c = (NRAND(sp->count-1)) + 1; s = RAND_FLOAT * (sp->maxlifespeed - sp->minlifespeed) + sp->minlifespeed; } newcell (mi , &sp->arr[y * sp->arr_width + x], c, s); } return True; } void draw_petri (ModeInfo * mi) { petristruct *sp = &petries[MI_SCREEN(mi)]; cell *a; if (petries == NULL) return; if (sp->arr == NULL) return; MI_IS_DRAWN(mi) = True; for (a = sp->head->next; a != sp->tail; a = a->next) { static XPoint all_coords[] = {{-1, -1}, {-1, 1}, {1, -1}, {1, 1}, {-1, 0}, { 1, 0}, {0, -1}, {0, 1}, {99, 99}}; XPoint *coords = 0; if (a->speed == 0) continue; a->growth += a->speed; if (a->growth >= sp->diaglim) { coords = all_coords; } else if (a->growth >= orthlim) { coords = &all_coords[4]; } else { continue; } while (coords->x != 99) { int x = cell_x(a) + coords->x; int y = cell_y(a) + coords->y; coords++; if (x < 0) x = sp->arr_width - 1; else if (x >= sp->arr_width) x = 0; if (y < 0) y = sp->arr_height - 1; else if (y >= sp->arr_height) y = 0; newcell (mi , &sp->arr[y * sp->arr_width + x], a->col, a->speed); } if (a->growth >= sp->diaglim) killcell (mi ,a); } if (!randblip (mi , (sp->head->next) == sp->tail)) return; for (a = sp->head->next; a != sp->tail; a = a->next) { if (a->isnext) { a->isnext = 0; a->speed = a->nextspeed; a->growth = 0.0; a->col = a->nextcol; drawblock (mi , cell_x(a), cell_y(a), a->col + sp->count); } } if (sp->redrawing) { int i; for (i = 0; i < REDRAWSTEP; i++) { cell *a = &sp->arr[sp->redrawpos]; drawblock(mi, cell_x(a), cell_y(a), a->col); if (++(sp->redrawpos) >= sp->arr_width * sp->arr_height) { sp->redrawing = 0; break; } } } } void init_petri(ModeInfo * mi) { Display *display = MI_DISPLAY(mi); petristruct *sp; if (MI_IS_VERBOSE(mi)) { (void) printf( "memThrottle : %s\n" , memThrottle ); (void) printf( "diaglim : %f\n" , st_diaglim ); (void) printf( "anychan : %f\n" , st_anychan ); (void) printf( "minorchan : %f\n" , st_minorchan ); (void) printf( "instantdeathchan : %f\n" , st_instantdeathchan ); (void) printf( "minlifespan : %d\n" , st_minlifespan ); (void) printf( "maxlifespan : %d\n" , st_maxlifespan ); (void) printf( "minlifespeed : %f\n" , st_minlifespeed ); (void) printf( "maxlifespeed : %f\n" , st_maxlifespeed ); (void) printf( "mindeathspeed : %f\n" , st_mindeathspeed ); (void) printf( "maxdeathspeed : %f\n" , st_maxdeathspeed ); } /* initialize */ if (petries == NULL) { if ((petries = (petristruct *) calloc(MI_NUM_SCREENS(mi), sizeof (petristruct))) == NULL) return; } sp = &petries[MI_SCREEN(mi)]; free_petri(display, sp); sp->redrawing = 0; if (!setup_display(mi)) return; if (!setup_arr(mi)) return; (void) randblip (mi , 1); } void release_petri(ModeInfo * mi) { if (petries != NULL) { int screen; for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) free_petri(MI_DISPLAY(mi), &petries[screen]); free(petries); petries = (petristruct *) NULL; } } void refresh_petri(ModeInfo * mi) { petristruct *sp; if (petries == NULL) return; sp = &petries[MI_SCREEN(mi)]; sp->redrawing = 1; sp->redrawpos = 0; } #endif /* MODE_petri */