/* Xfires, a forest fire simulator for X windows. Michael Creutz creutz@bnl.gov compiling: cc -O -o xfires -L/usr/X11R6/lib xfires.c -lX11 version of August 2006 The latest version is kept at "http://thy.phy.bnl.gov/www/xtoys/xtoys.html" A text description of this program is there as well. */ # include # include # include # include # include # include # include # include # include /* size and position parameters for buttons, etc. */ # define PLAYTOP 116 # define PLAYLEFT 14 # define BUTTONWIDTH 68 # define BBWIDTH 96 # define SRHEIGHT (2*18) # define BPW (sizeof(long)) /* lattice dimensions; ncols will be truncated to a multiple of bytes per long word (BPW), and then two units are lost for boundaries */ int nrows,ncols,volume,mask; int block, /* size of cells displayed on the screen */ blockshift; /* log_2(block) */ unsigned char *field[2]={NULL,NULL}; /* pointers to old and new system */ int old=0,new=1; /* which field is current? */ # define barren 0 # define tree 1 # define fire 2 int paused=0, /* is the system paused? */ iteration=0; /* counting sweeps */ long mrand48(),lrand48(); /* generates a random word, lrand is non-negative */ double drand48(); /* generates a random double */ /* for fast but crude random number generation */ # define RSIZE 127 long births[RSIZE]; int randomizer=57,birthindex=0; char stringbuffer[256]; /* generally useful */ /* things for regulating the updating speed */ int speed=0; /* updating delay proportional to speed */ int delay=0; /* counter for implementing speed control */ struct timeval timeout; /* timer for speed control */ /* various window stuff */ Display *display; int screen; static char *progname; Window window,quitbutton,pausebutton,playground,blockbutton,srbutton, speedbutton,makebutton(); XColor xcolor,colorcell; Colormap cmap; GC gc,gcpen; int windowwidth,windowheight; XFontStruct *font=NULL; int font_height,font_width; XSizeHints size_hints; int darkcolor,lightcolor,black,white; XImage *spinimage=NULL; long translate[256]; /* for converting colors */ char * srtext[2]={"save","restore"}; void drawbutton(),openwindow(),makebuttons(),update(),repaint(), cleanup(),showpic(),fixboundary(),loadpic(),savepic(),compress(), decompress(),inittable(); int main(argc,argv) int argc; char **argv; {unsigned int width, height,x,y; int i; XEvent report; progname=argv[0]; /* initial lattice size */ block=1; nrows=200/block; ncols=(~(BPW-1))&(200/block); volume=nrows*ncols; /* set up array for fast random number generation */ for (i=0;i>2); /* open the window and make the buttons */ openwindow(argc,argv); makebuttons(); /* loop forever, looking for events */ while(1) {if (0==paused) {if (delay) /* don't update yet */ {delay--; /* this use of select() seems a kludge to me; why can't usleep() be more standard? */ timeout.tv_sec=0; timeout.tv_usec=100000; /* .1 sec per delay unit */ select(0,NULL,NULL,NULL,&timeout); } else {delay=speed; update(); } } while (paused|XPending(display)) {XNextEvent(display,&report); switch (report.type) {case Expose: if ((report.xexpose.window)!=window) break; /* cuts down flashing, but you might remove this line if things aren't being redrawn */ if (report.xexpose.count!=0) break; /* more in queue, wait for them */ repaint(); break; case ConfigureNotify: width=report.xconfigure.width; height=report.xconfigure.height; if ((width10) speed=10; delay=speed; drawbutton(speedbutton,0,0,BBWIDTH,18,"speed",-1); drawbutton(speedbutton,1+((10-speed)*(BBWIDTH-2))/11,1, (BBWIDTH-2)/11,16,"",2); } else update(); /* do a sweep when mouse clicked */ break; default: break; } /* end of switch */ } /* end of if XPending */ } /* end of while(1) */ } /* end of main */ void update() {int i,nfires,ntrees,newtrees=volume/32; unsigned char *pold,*pnew,*pup,*pdown,*pleft,*pright,*ptop; /* grow new trees */ while (newtrees) {newtrees--; i=volume; while (i>=volume) {i=mask&births[birthindex]; births[birthindex]^=births[randomizer]; if ((++birthindex)>=RSIZE) birthindex=0; if ((++randomizer)>=RSIZE) randomizer=0; } if (field[old][i]!=fire) field[new][i]=field[old][i]=tree; } /* spread fires */ pnew=field[new]+ncols; ptop=field[old]+volume-ncols; for (pold=field[old]+ncols; pold4) blocktop=block-1; if (8==(*spinimage).depth) {if (block>1) /* I wish I knew how to do this faster */ for (row=0;row1) /* I wish I knew how to do this faster */ for (row=0;rowascent+font->descent; font_width=font->max_bounds.width; /* make graphics contexts: gc for black on white gcpen for varying purposes */ gc=XCreateGC(display,window,0,NULL); XSetFont(display,gc,font->fid); XSetForeground(display,gc,black); XSetBackground(display,gc,lightcolor); gcpen=XCreateGC(display,window,0,NULL); XSetFont(display,gcpen,font->fid); XSetForeground(display,gcpen,darkcolor); XSetBackground(display,gcpen,lightcolor); /* show the window */ XMapWindow(display,window); return; } void makebuttons() {int i; long event_mask; XEvent report; Cursor cursor; /* first destroy any old buttons */ XDestroySubwindows(display,window); /* now make the new buttons */ quitbutton =makebutton(4,4,BUTTONWIDTH,18); pausebutton =makebutton(BUTTONWIDTH+8,4,BUTTONWIDTH,18); blockbutton =makebutton(16,32,BBWIDTH,18); srbutton =makebutton(windowwidth-BUTTONWIDTH-4,4,BUTTONWIDTH,18*2); speedbutton=makebutton(windowwidth-BBWIDTH-8, 46, BBWIDTH,18); playground=XCreateSimpleWindow(display,window, PLAYLEFT,PLAYTOP,block*ncols,block*nrows,0,black,translate[3]); event_mask=ExposureMask|ButtonReleaseMask|ButtonPressMask| PointerMotionHintMask|ButtonMotionMask; XSelectInput(display,playground,event_mask); XMapWindow(display,playground); /* wait for playground to be displayed befor proceeding */ i=1; /* a flag */ while (i) {XNextEvent(display,&report); switch (report.type) {case Expose: if (report.xexpose.window!=playground) i=0; default: break; } } /* make image structure */ if (NULL!=spinimage) {XDestroyImage(spinimage); spinimage=NULL; } spinimage=XGetImage((Display *) display, (Drawable) playground, 0,0,block*ncols,block*nrows,AllPlanes,ZPixmap); if (NULL==spinimage) {fprintf(stderr,"trouble creating image structure\n"); exit(-1); } /* make special cursors to be cute */ cursor=XCreateFontCursor(display,XC_sb_up_arrow); XDefineCursor(display,playground,cursor); cursor=XCreateFontCursor(display,XC_hand2); XDefineCursor(display,blockbutton,cursor); XDefineCursor(display,quitbutton,cursor); XDefineCursor(display,pausebutton,cursor); XDefineCursor(display,srbutton,cursor); XDefineCursor(display,speedbutton,cursor); /* reallocate various arrays */ for (i=0;i<2;i++) {if (NULL!=field[i]) free((char *) field[i]); if (NULL==( field[i]= (unsigned char *) malloc(volume))) {fprintf(stderr,"allocation problems\n"); cleanup(); } } /* set blockshift to log_2 of block */ blockshift=0; i=block; while(i>>=1) blockshift++; /* mask used for random site selection */ mask=1; while (maskfid); XFreeGC(display,gc); XFreeGC(display,gcpen); XCloseDisplay(display); XDestroyImage(spinimage); if (NULL!=field[0]) free((char *) field[0]); if (NULL!=field[1]) free((char *) field[1]); exit(1); } /* from here on is the stuff for saving and restoring from a gif file */ /* for info on how gif works, see ftp://network.ucsd.edu/graphics/GIF.shar.Z */ char *picturename="xfires.gif"; void loadpic(data,xsize,ysize) /* load GIF image to field */ unsigned char *data; /* where the output data starts */ int xsize,ysize; /* output picture dimensions */ {int i,j,filesize,gwidth,gheight,gvolume; unsigned char *ptr, *ptr1, *rawgif; int colorbits,codesize; FILE *infile; if (NULL==(infile=fopen(picturename,"r"))) {fprintf(stderr,"couldn't open input file\n"); return; } /* find the file size */ fseek(infile, 0L, 2); filesize = ftell(infile); fseek(infile, 0L, 0); /* make a place in memory for the file */ if (NULL==(rawgif= (unsigned char *) malloc(filesize) )) {fprintf(stderr, "not enough memory to read gif file\n"); return; } ptr=rawgif; /* read in the file */ if (fread(ptr, filesize, 1, infile) != 1) {fprintf(stderr, "read failed\n"); free((char *) rawgif); return; } fclose(infile); /* check for GIF signature */ if (strncmp((char *) ptr,"GIF87a", 6)) {fprintf(stderr, "not a GIF87a file\n"); free((char *) rawgif); return; } ptr+=6; ptr+=4; /* skip over screen size */ colorbits=1+((*ptr)&0xf); /* how many bits of color */ ptr+=2; /* skip over background */ if (*ptr++) /* should be zero */ {fprintf(stderr, "corrupt GIF file\n"); free((char *) rawgif); return; } ptr+=(3*(1<=gheight) break; for (i=0;i=gwidth) break; data[i+j*xsize]=ptr1[i+j*gwidth]; } } free((char *) ptr1); fixboundary(); return; } void savepic(data,xsize,ysize) /* save the field as a GIF image */ unsigned char *data; /* where the input data starts */ int xsize,ysize; /* picture dimensions */ {int i; int colorbits=5,codesize=5; /* assume a 32 color image */ FILE *outfile; if (NULL==(outfile=fopen(picturename,"w"))) {fprintf(stderr,"couldn't open output file\n"); return; } /* GIF signature */ fwrite("GIF87a",6,1,outfile); /* screen descriptor */ stringbuffer[0]=xsize&0xff; /* screen width */ stringbuffer[1]=(xsize>>8)&0xff; stringbuffer[2]=ysize&0xff; /* screen height */ stringbuffer[3]=(ysize>>8)&0xff; stringbuffer[4]=(0x80) /* M=1; global color map follows */ |((colorbits-1)<<4) /* -1+ bits of color reslution */ |(colorbits-1); /* -1+bits per pixel in image */ stringbuffer[5]=0; /* background color */ stringbuffer[6]=0; /* should be zero */ fwrite(stringbuffer,7,1,outfile); /* global color map */ for (i=0;i<(1<>8,outfile); fputc(colorcell.green>>8,outfile); fputc(colorcell.blue>>8,outfile); } /* image descriptor */ stringbuffer[0]=','; /* image descriptor separator */ stringbuffer[1]=0; /* image offset */ stringbuffer[2]=0; stringbuffer[3]=0; stringbuffer[4]=0; stringbuffer[5]=xsize&0xff; /* image width */ stringbuffer[6]=(xsize>>8)&0xff; stringbuffer[7]=ysize&0xff; /* image height */ stringbuffer[8]=(ysize>>8)&0xff; stringbuffer[9]=0; /* use global color map, no interlace */ fwrite(stringbuffer,10,1,outfile); /* start of image data */ fputc(codesize,outfile); compress(codesize,data,outfile,volume); /* gif terminator */ fputc(';',outfile); fclose(outfile); return; } /* LZW compression */ /* hash function assumes TABLELENGTH is a power of 2 */ # define TABLELENGTH (1<<13) char **addresses=NULL; /* where to find the string */ int *codes=NULL, /* the code value */ *linktonext=NULL, /* the next index in the hash chain */ *lengths=NULL, /* the length of the coded string */ *codeindex=NULL; /* the index for a given code */ int nextcode; /* the next unused code */ /* hashit is supposed to give a unique fairly random number in the table for each length a and string b */ # define hashit(a,b) (51*a+53*(57*b[0]+59*(61*b[a-1]+b[a>>1])))&(TABLELENGTH-1) void compress(initcodesize,ptr,outfile,size) int initcodesize; /* the initial compression bits */ char * ptr; /* where the data comes from */ FILE * outfile; /* where the output goes */ int size; /* how much data */ {int currentcode,prefixcode=0,codesize,maxbits=12,maxcode; int clearcode,eoicode,currentplace=0,length,blocksize=0,bitoffset; int findcode(); unsigned long outputword; unsigned char blockbuffer[256]; /* to hold data blocks before writing */ /* allocate space for hash tables */ if (NULL==(codes=(int *) malloc(sizeof(int)*TABLELENGTH))) {fprintf(stderr,"compress: trouble allocating tables\n"); currentplace=size; } if (NULL==(linktonext=(int *) malloc(sizeof(int)*TABLELENGTH))) {fprintf(stderr,"compress: trouble allocating tables\n"); currentplace=size; } if (NULL==(lengths=(int *) malloc(sizeof(int)*TABLELENGTH))) {fprintf(stderr,"compress: trouble allocating tables\n"); currentplace=size; } /* need one extra place in codeindex for overflow before resetting: */ if (NULL==(codeindex=(int *) malloc(sizeof(int)*4097))) {fprintf(stderr,"compress: trouble allocating tables\n"); currentplace=size; } if (NULL==(addresses=(char **) malloc(sizeof(char *)*TABLELENGTH))) {fprintf(stderr,"compress: trouble allocating tables\n"); currentplace=size; } /* set up initial code table */ inittable(initcodesize); clearcode=(1<maxcode) {codesize++; maxcode=1<maxbits) {if (bitoffset) outputword|=(clearcode< (currentcode=findcode(length,(char *)(ptr+currentplace)))) {prefixcode=currentcode; length++; if ((currentplace+length)>=size) break; } nextcode++; currentplace+=(length-1); /* output the prefix code */ if (bitoffset) outputword|=(prefixcode<=8) {blockbuffer[blocksize]=outputword&0xff; outputword>>=8; bitoffset-=8; blocksize++; /* output filled block */ if (blocksize>=254) {fputc((char) blocksize, outfile); fwrite(blockbuffer,blocksize,1,outfile); blocksize=0; } } } /* output the end of information code */ if (bitoffset) outputword|=(eoicode<=0) {blockbuffer[blocksize]=(char) (outputword&0xff); outputword>>=8; bitoffset-=8; blocksize++; if (blocksize>=254) {fputc((char) blocksize, outfile); fwrite(blockbuffer,blocksize,1,outfile); blocksize=0; } } /* output the last block */ if (blocksize) {fputc((char) blocksize, outfile); fwrite(blockbuffer,blocksize,1,outfile); } /* a final zero block count */ fputc(0, outfile); /* deallocate tables */ if (NULL!=codes) free((char *) codes); if (NULL!=linktonext) free((char *) linktonext); if (NULL!=lengths) free((char *) lengths); if (NULL!=codeindex) free((char *) codeindex); if (NULL!=addresses) free((char *) addresses); codes=linktonext=lengths=codeindex=NULL; addresses=(char **) NULL; return; } void decompress(initcodesize,ptr,ptr1,size) int initcodesize; unsigned char *ptr, *ptr1; /* compressed data from ptr go to ptr1 */ int size; /* an upper limit purely as a check */ {int i,currentcode,codesize=0,maxbits=12,blocksize; int clearcode,eoicode,codemask=0; int bitoffset=0,indx,oldindx=0; int currentplace=0,oldplace=0; int findcode(); unsigned long inputword=0; unsigned char *p1, *p2; /* first deblock the data */ p1=p2=ptr; blocksize=(*p1++); while (blocksize) {while (blocksize--) (*p2++)=(*p1++); /* a wonderful example of how abstruse C can be */ blocksize=(*p1++); } /* set up initial code table */ currentcode=clearcode=(1<=0) /* it is there */ { /* put it into the output */ for (i=0;i=0) /* first character treated differently */ {findcode(lengths[oldindx]+1,(char *) (ptr1+oldplace)); nextcode++; /* add new code to table */ } oldplace=currentplace; currentplace+=lengths[indx]; oldindx=indx; } else /* not in table yet; must be old code plus last=first character */ {for (i=0;isize)) {fprintf(stderr,"gif file appears to be corrupt\n"); break; } } /* check if codesize needs increasing */ if (nextcode>codemask) if (codesize>=codesize; bitoffset-=codesize; if (currentcode>nextcode) {fprintf(stderr,"gif file appears to be corrupt\n"); break; } } /* deallocate tables */ if (NULL!=codes) free((char *) codes); if (NULL!=linktonext) free((char *) linktonext); if (NULL!=lengths) free((char *) lengths); if (NULL!=codeindex) free((char *) codeindex); if (NULL!=addresses) free((char *) addresses); codes=linktonext=lengths=codeindex=NULL; addresses=(char **) NULL; return; } void inittable(size) int size; {int i,findcode(); for (i=0;i0) {if (lengths[indx]==length) /* is the length right ? */ {for (j=0;j=0) /* find an unused slot in table */ {i++; if (i>=TABLELENGTH) i-=TABLELENGTH; } if (i!=indx) {linktonext[indx]=i; /* link to it */ indx=i; /* move to it */ } codes[indx]=nextcode; /* save the new code */ lengths[indx]=length; addresses[indx]=string; codeindex[nextcode]=indx; return nextcode; }