#include #include #include #include "gene.h" #define TITLE "P2" extern int WorldWidth, WorldHeight, MaxGeneration; extern double CellSize; extern unsigned char MessageStdout; extern unsigned long Step, Generation; extern unsigned char ViscaOn, FullScreen, AutoDemo; Display *dpy; Window RootWin, MainWin, win, msgwin; int screen, depth; GC gc; XGCValues gcv; unsigned long fg, bg; Colormap cmap; XFontStruct *fst; XSetWindowAttributes att; char MyStipple[] = { 0x55, 0xaa, 0x55, 0xaa }; int WinX = 0, WinY = 0; int WinWidth, WinHeight; int ButtonWidth, ButtonHeight, ButtonX, ButtonBase; int GenerationBase, StepBase, PopSizeBase; unsigned long ButtonFG, ButtonBG, WorkBG; int color_flag, Ncolors; double ViewX = 0.0, ViewY = 0.0, ViewSize = 1.0; #define MAXCOLORS 128 #define ButtonPad 10 int pixel[MAXCOLORS]; void b_run(), b_gene(), b_sele(), b_step(), b_replay(), b_reset(), b_zoom(), b_unzoom(), b_load(), b_save(), b_quit(); struct button_rec { char *label; void (*proc)(); int flag; Window win; } buttons[] = { {"Run", b_run, 0}, {"Generation", b_gene, 0}, {"Selection", b_sele, 0}, {"Step", b_step, 0}, {"Replay", b_replay, 0}, {"Reset", b_reset, 0}, {"Zoom", b_zoom, 0}, {"Unzoom", b_unzoom, 0}, {"Load", b_load, 0}, {"Save", b_save, 0}, {"Quit", b_quit, 0}, { NULL, b_quit, 0 } }; parse_geom(str) char *str; { XParseGeometry(str,&WinX,&WinY,&WorldWidth,&WorldHeight); } gr_init(argc,argv) int argc; char **argv; { int w; struct button_rec *b; extern char *DisplayName, *FontName; if(!(dpy = XOpenDisplay(DisplayName))) { fprintf(stderr,"%s: cannot open %s.\n",argv[0], XDisplayName(DisplayName)); exit(1); } if (!(fst = XLoadQueryFont(dpy, FontName))) { fprintf(stderr,"%s: has not font %s.\n", argv[0], FontName); exit(1); } screen = DefaultScreen(dpy); depth = DefaultDepth(dpy,screen); bg = BlackPixel(dpy,screen); fg = WhitePixel(dpy,screen); for (ButtonWidth = 0, b = buttons; b->label; b ++) { w = XTextWidth(fst,b->label,strlen(b->label)) + fst->max_bounds.width; if (ButtonWidth < w) ButtonWidth = w; } if (FullScreen) { WinX = WinY = 0; WorldWidth = DisplayWidth(dpy,screen)-ButtonWidth-ButtonPad*2; WorldHeight = DisplayHeight(dpy,screen); if (WorldHeight > WorldWidth) { WinY = lrand48() % (WorldHeight - WorldWidth + 1); WorldHeight = WorldWidth; } else if (WorldWidth > WorldHeight) { WinX = lrand48() % (WorldWidth - WorldHeight + 1); WorldWidth = WorldHeight; } } ButtonX = WorldWidth + ButtonPad; WinWidth = ButtonX + ButtonWidth + ButtonPad; WinHeight = WorldHeight; ButtonHeight = (fst->max_bounds.ascent + fst->max_bounds.descent) * 1.5; ButtonBase = ButtonHeight * 0.2 + fst->max_bounds.ascent; RootWin = DefaultRootWindow(dpy); MainWin = XCreateSimpleWindow(dpy,RootWin, WinX, WinY, WinWidth, WinHeight, (FullScreen?0:1), fg, bg); XSetStandardProperties(dpy, MainWin, TITLE, argv[0], None, argv, argc, NULL); att.event_mask = ExposureMask | ResizeRedirectMask | ButtonPressMask | ButtonReleaseMask; XSelectInput(dpy,MainWin,att.event_mask); if (FullScreen) { att.override_redirect = True; XChangeWindowAttributes(dpy,MainWin,CWOverrideRedirect,&att); } win = XCreateSimpleWindow(dpy,MainWin, 0, 0, WorldWidth, WorldHeight, 0, fg, bg); att.backing_store = Always; att.event_mask = ButtonPressMask | ButtonReleaseMask; XChangeWindowAttributes(dpy,win,(CWBackingStore|CWEventMask),&att); for (w = ButtonPad, b = buttons; b->label; b ++) { b->win = XCreateSimpleWindow(dpy,MainWin,ButtonX,w, ButtonWidth,ButtonHeight,1,ButtonFG,ButtonBG); XSelectInput(dpy,b->win,ExposureMask| ButtonPressMask|ButtonReleaseMask| EnterWindowMask|LeaveWindowMask); XMapRaised(dpy,b->win); w += ButtonHeight + ButtonPad; } GenerationBase = w + ButtonHeight; StepBase = GenerationBase + ButtonHeight; PopSizeBase = StepBase + ButtonHeight; if (!MessageStdout && !FullScreen) { msgwin = XCreateSimpleWindow(dpy,RootWin,0,0,100,100,1,fg,bg); att.override_redirect = True; XChangeWindowAttributes(dpy,msgwin,CWOverrideRedirect,&att); XSelectInput(dpy,msgwin,ExposureMask); } gcv.foreground = fg; gcv.background = bg; gcv.stipple = XCreateBitmapFromData(dpy,win,MyStipple,4,4); gcv.font = fst->fid; gc = XCreateGC(dpy,win,GCForeground|GCBackground|GCStipple|GCFont,&gcv); if (color_flag = (depth >= 8)) color_setup(); else bw_setup(); XMapRaised(dpy, MainWin); XMapRaised(dpy, win); clear_world(); XFlush(dpy); } bw_setup() { ButtonFG = bg; ButtonBG = fg; } color_setup() { XColor scr, exc, acolor[MAXCOLORS]; int pl[1], i; extern int GenerationLength; cmap = DefaultColormap(dpy,screen); XAllocNamedColor(dpy,cmap,"NavyBlue",&scr,&exc); ButtonFG = scr.pixel; XAllocNamedColor(dpy,cmap,"SkyBlue",&scr,&exc); ButtonBG = scr.pixel; XAllocNamedColor(dpy,cmap,"grey50",&scr,&exc); WorkBG = scr.pixel; Ncolors = (GenerationLength+1 < MAXCOLORS)? GenerationLength+1 : MAXCOLORS; while (!XAllocColorCells(dpy, cmap, True, pl, 0, pixel, Ncolors)) Ncolors --; for (i = 0; i < Ncolors; i ++) { acolor[i].pixel = pixel[i]; acolor[i].flags = DoRed | DoGreen | DoBlue; } set_colors(acolor,Ncolors); XStoreColors(dpy, cmap, acolor, Ncolors); } set_colors(ac, n) XColor *ac; int n; { int i, j, s, e; float r1,g1,b1, r2,g2,b2; static struct { float r, g, b, p; } d[] = { { 0.0, 0.0, 1.0, 0.12}, /* blue */ { 0.0, 0.7, 1.0, 0.11}, { 0.0, 1.0, 1.0, 0.10}, /* cyan */ { 0.0, 1.0, 0.7, 0.10}, { 0.0, 1.0, 0.0, 0.10}, /* green */ { 0.7, 1.0, 0.0, 0.10}, { 1.0, 1.0, 0.0, 0.10}, /* yellow */ { 1.0, 0.7, 0.0, 0.10}, { 1.0, 0.0, 0.0, 0.17}, /* red */ { 1.0, 1.0, 1.0, 999.}};/* white */ for (j = e = 0; e < n; j ++) { r1 = d[j].r; g1 = d[j].g; b1 = d[j].b; r2 = d[j+1].r; g2 = d[j+1].g; b2 = d[j+1].b; for (i = s = e, e += n * d[j].p; i < e && i < n; i ++) { ac[i].red = (r1 + (i-s)*(r2-r1)/(e-s)) * 65535; ac[i].green = (g1 + (i-s)*(g2-g1)/(e-s)) * 65535; ac[i].blue = (b1 + (i-s)*(b2-b1)/(e-s)) * 65535; } } } event_loop() { XEvent event; while (1) { XNextEvent(dpy,&event); switch (event.type) { case Expose: if (!event.xexpose.count) redisplay(&event); break; case EnterNotify: case LeaveNotify: redraw_button(&event); break; case ButtonRelease: if (!AutoDemo) exec_button(&event); } } } check_event() { XEvent event; while (XCheckMaskEvent(dpy,ButtonPressMask|ButtonReleaseMask| ExposureMask,&event)) switch (event.type) { case ButtonRelease: return True; case Expose: if (!event.xexpose.count) redisplay(&event); } return False; } demo_loop() { int flag; XEvent event; do XNextEvent(dpy,&event); while (event.type != Expose || event.xexpose.count); redisplay(&event); do { while (XCheckMaskEvent(dpy,ButtonPressMask|ButtonReleaseMask| ExposureMask,&event)) switch (event.type) { case ButtonRelease: exit(1); case Expose: if (!event.xexpose.count) redisplay(&event); } flag = exec_n_step(); XFlush(dpy); if (!flag) { if (MaxGeneration && Generation >= MaxGeneration) flag = 2; else selection(); } } while (flag != 2); sleep(4); exit(0); } redraw_button(e) XCrossingEvent *e; { struct button_rec *b; for (b = buttons; b->label; b ++) if (e->window == b->win) { b->flag = (e->type == EnterNotify); draw_button(b); break; } } exec_button(e) XCrossingEvent *e; { struct button_rec *b; for (b = buttons; b->label; b ++) if (e->window == b->win) { (*b->proc)(); break; } if (e->window == win) { double cx, cy; char buf[1300]; if ((cx = e->x*ViewSize/WorldWidth + ViewX) >= 1.0) cx -= 1.0; if ((cy = e->y*ViewSize/WorldHeight + ViewY) >= 1.0) cy -= 1.0; gene_string(buf,cx,cy); draw_message(buf); } } draw_button(b) struct button_rec *b; { unsigned long bfg = ButtonFG, bbg = ButtonBG; int x; if (b->flag) { bfg = ButtonBG; bbg = ButtonFG; } XSetForeground(dpy,gc,bbg); XFillRectangle(dpy,b->win,gc,0,0,ButtonWidth,ButtonHeight); x = (ButtonWidth - XTextWidth(fst,b->label,strlen(b->label))) / 2; XSetForeground(dpy,gc,bfg); XDrawString(dpy,b->win,gc,x,ButtonBase,b->label,strlen(b->label)); } draw_circle(px,py,pr) double px,py,pr; { int x, y, r; r = pr / ViewSize * WorldWidth; if ((px -= ViewX) < -pr) px += 1.0; x = px / ViewSize * WorldWidth; if (x - r >= WorldWidth) return; if ((py -= ViewY) < -pr) py += 1.0; y = py / ViewSize * WorldHeight; if (y - r >= WorldHeight) return; if (r < 1) XDrawPoint(dpy,win,gc,x,y); else XFillArc(dpy,win,gc,x-r,y-r,r+r,r+r,0,64*360); } draw_cell(c) cell *c; { double th; if (color_flag) XSetForeground(dpy,gc,pixel[c->step%Ncolors]); draw_circle(c->x,c->y,CellSize); XSetForeground(dpy,gc,fg); th = c->d * M_PI / 128.0; draw_circle(c->x+CellSize*cos(th),c->y+CellSize*sin(th),CellSize/2); } erase_cell(c) cell *c; { XSetForeground(dpy,gc,bg); draw_circle(c->x,c->y,CellSize); } clear_world() { XClearWindow(dpy,win); } void b_run() { go_forward(1); } void b_gene() { go_forward(0); } go_forward(cont) int cont; { int flag; draw_message("Click mouse button to stop."); do { if (ViscaOn) video_rec_short(); flag = exec_n_step(); XFlush(dpy); if (!flag && ViscaOn) video_rec_long(); if (MaxGeneration && Generation > MaxGeneration) world_reset(); else if (!flag && cont) selection(); } while ((flag || cont) && !check_event()); if (!MessageStdout && !FullScreen) XUnmapWindow(dpy,msgwin); } void b_sele() { selection(); } void b_step() { exec_n_step(); } unsigned long ReplayStep; void replay_cells(c) cell *c; { if (c->step == ReplayStep) draw_cell(c); } void b_replay() { clear_world(); for (ReplayStep = 0; ReplayStep <= Step; ReplayStep ++) { for_all_cells(replay_cells); XFlush(dpy); } } void b_reset() { world_reset(); } void b_quit() { if (ViscaOn) video_close(); exit(0); } #define ZoomStackSize 128 struct { double x,y,s; } ZoomStack[ZoomStackSize]; int ZoomStackPointer = 0; void b_zoom() { XEvent event; draw_message("Specify the center of zoomed area."); do { XNextEvent(dpy,&event); if (event.type == LeaveNotify) redraw_button(&event); } while (event.xany.window != win || event.type != ButtonRelease); if (ZoomStackPointer < ZoomStackSize) { ZoomStack[ZoomStackPointer].x = ViewX; ZoomStack[ZoomStackPointer].y = ViewY; ZoomStack[ZoomStackPointer].s = ViewSize; ZoomStackPointer ++; } ViewX += (ViewSize * (event.xbutton.x - WorldWidth/4)) / WorldWidth; if (ViewX < 0.0) ViewX += 1.0; ViewY += (ViewSize * (event.xbutton.y - WorldHeight/4)) / WorldHeight; if (ViewY < 0.0) ViewY += 1.0; ViewSize *= 0.5; clear_world(); for_all_cells(draw_cell); if (!MessageStdout && !FullScreen) XUnmapWindow(dpy,msgwin); } void b_unzoom() { if (ZoomStackPointer <= 0) return; ZoomStackPointer --; ViewX = ZoomStack[ZoomStackPointer].x; ViewY = ZoomStack[ZoomStackPointer].y; ViewSize = ZoomStack[ZoomStackPointer].s; clear_world(); for_all_cells(draw_cell); } char MassageString[1300]; draw_message(msg) char *msg; { int len, x, y, w, h, ww, wh, bw, d; Window cwin, root,parent,*children; if (MessageStdout) { puts(msg); return; } else if (FullScreen) return; strcpy(MassageString,msg); for (w = 0, h = ButtonHeight; *msg; msg += len) { if (*msg == '\n') { msg ++; h += ButtonHeight; } for (len = 0; msg[len] && msg[len] != '\n'; len++); if (len && w < (ww = XTextWidth(fst,msg,len))) w = ww; } parent = MainWin; do { cwin = parent; XQueryTree(dpy,cwin,&root,&parent,&children,&d); } while (root != parent); XGetGeometry(dpy,cwin,&root,&x,&y,&ww,&wh,&bw,&d); XMoveResizeWindow(dpy,msgwin,x,y+wh+bw+1,w+ButtonPad*2,h); XMapRaised(dpy,msgwin); display_message(); } display_message() { int len, y; char *msg; XSetForeground(dpy,gc,fg); for (msg = MassageString, y = ButtonBase; *msg; msg += len) { if (*msg == '\n') { msg ++; y += ButtonHeight; } for (len = 0; msg[len] && msg[len] != '\n'; len++); if (len) XDrawImageString(dpy,msgwin,gc,ButtonPad,y,msg,len); } } draw_info() { char buf[32]; extern unsigned long PopSize, Generation, Step; XSetForeground(dpy,gc,fg); sprintf(buf,"%5d",Generation); XDrawImageString(dpy,MainWin,gc,ButtonX,GenerationBase,buf,strlen(buf)); sprintf(buf,"%5d",Step); XDrawImageString(dpy,MainWin,gc,ButtonX,StepBase,buf,strlen(buf)); sprintf(buf,"%5d",PopSize); XDrawImageString(dpy,MainWin,gc,ButtonX,PopSizeBase,buf,strlen(buf)); } redisplay(e) XExposeEvent *e; { struct button_rec *b; cell *p; if (e->window == MainWin) { if (color_flag) XSetForeground(dpy,gc,WorkBG); else { XSetFillStyle(dpy,gc,FillOpaqueStippled); XSetForeground(dpy,gc,fg); } XFillRectangle(dpy,MainWin,gc,WorldWidth,0, WinWidth-WorldWidth,WinHeight); if (!color_flag) XSetFillStyle(dpy,gc,FillSolid); draw_info(); } else if (e->window == msgwin) { display_message(); } else for (b = buttons; b->label; b ++) if (e->window == b->win) { draw_button(b); break; } }