#include #include #include #include #define NSTEP 10 /* number of steps between throws */ #define RBALL 10 /* radius of ball images */ #define NBALL 100 int nhand=2; int delay=20; /* ms delay between steps */ int nball; int maxhgt; Rectangle win; #define add addpt #define sub subpt #define inset insetrect /* * pattern lists the heights of a repeating sequence of throws. * At time t, hand t%nhand throws. At that time, it must * hold exactly one ball, unless it executes a 0 throw, * in which case it must hold no ball. A throw of height h * at time t lands at time t+h in hand (t+h)%nhand. */ typedef struct Ball Ball; struct Ball{ int oldhand; /* hand that previously held the ball */ int hgt; /* how high the throw from oldhand was */ int time; /* time at which ball will arrive */ int hand; /* hand in which ball will rest on arrival */ }; Ball ball[NBALL]; void throw(int t, int hgt){ int hand=t%nhand; int i, b, n; b=n=0; for(i=0;i!=nball;i++) if(ball[i].hand==hand && ball[i].time<=t){ n++; b=i; } if(hgt==0){ if(n!=0){ print("bad zero throw at t=%d, nball=%d\n", t, n); exits("bad"); } } else if(n!=1){ print("bad ball count at t=%d, nball=%d\n", t, n); exits("bad"); } else{ ball[b].oldhand=hand; ball[b].hgt=hgt; ball[b].time=t+hgt; ball[b].hand=(hand+hgt)%nhand; } } Point bpos(int b, int step, int t){ Ball *bp=&ball[b]; double dt=t-1+(step+1.)/NSTEP-(bp->time-bp->hgt); double hgt=(bp->hgt*dt-dt*dt)*4./(maxhgt*maxhgt); double alpha=(bp->oldhand+(bp->hand-bp->oldhand)*dt/bp->hgt)/(nhand-1); return (Point){win.min.x+(win.max.x-win.min.x)*alpha, win.max.y-1+(win.min.y-win.max.y)*hgt}; } Image *image, *disk; void move(int t){ int i, j; for(i=0;i!=NSTEP;i++){ if(ecanmouse()) emouse(); draw(image, inset(image->r, 3), display->white, nil, ZP); for(j=0;j!=nball;j++) draw(image, rectaddpt(disk->r, sub(bpos(j, i, t), Pt(RBALL, RBALL))), disk, nil, ZP); draw(screen, screen->r, image, nil, image->r.min); flushimage(display, 1); if(delay>0) sleep(delay); } } void usage(char *name) { fprint(2, "usage: %s [start] pattern\n", name); exits("usage"); } void eresized(int new){ if(new && getwindow(display, Refnone) < 0) { sysfatal("can't reattach to window"); } if(image) freeimage(image); image=allocimage(display, screen->r, screen->chan, 0, DNofill); draw(image, image->r, display->black, nil, ZP); win=inset(screen->r, 4+2*RBALL); } void main(int argc, char *argv[]){ int sum, i, t, hgt, nstart, npattern; char *s, *start = nil, *pattern = nil; ARGBEGIN{ default: usage(argv0); case 'd': s = ARGF(); if(s == nil) usage(argv0); delay = strtol(argv[0], &s, 0); if(delay < 0 || s == argv[0] || *s != '\0') usage(argv0); break; case 'h': s = ARGF(); if(s == nil) usage(argv0); nhand = strtol(argv[0], &s, 0); if(nhand <= 0 || s == argv[0] || *s != '\0') usage(argv0); break; }ARGEND switch(argc) { case 1: start=""; pattern=argv[0]; break; case 2: start=argv[0]; pattern=argv[1]; break; default: usage(argv0); } sum=0; maxhgt=0; for(s=pattern;*s;s++){ hgt=*s-'0'; sum+=hgt; if(maxhgtchan, 0, DWhite); fillellipse(disk, Pt(RBALL, RBALL), RBALL, RBALL, display->black, ZP); eresized(0); if(image==0){ print("can't allocate bitmap"); exits("no space"); } for(t=0;start[t];t++){ move(t); throw(t, start[t]-'0'); } nstart=t; for(;;t++){ move(t); throw(t, pattern[(t-nstart)%npattern]-'0'); } }