|
@@ -0,0 +1,180 @@
|
|
|
+#include <u.h>
|
|
|
+#include <libc.h>
|
|
|
+#include <draw.h>
|
|
|
+#include <event.h>
|
|
|
+#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(maxhgt<hgt) maxhgt=hgt;
|
|
|
+ }
|
|
|
+ npattern=s-pattern;
|
|
|
+ for(s=start;*s;s++){
|
|
|
+ hgt=*s-'0';
|
|
|
+ if(maxhgt<hgt) maxhgt=hgt;
|
|
|
+ }
|
|
|
+ if(sum%npattern){
|
|
|
+ print("%s: non-integral ball count\n",argv[0]);
|
|
|
+ exits("partial ball");
|
|
|
+ }
|
|
|
+ nball=sum/npattern;
|
|
|
+ for(i=0;i!=nball;i++){
|
|
|
+ ball[i].oldhand=(i-nball)%nhand;
|
|
|
+ if(ball[i].oldhand<0) ball[i].oldhand+=nhand;
|
|
|
+ ball[i].hgt=nball;
|
|
|
+ ball[i].time=i;
|
|
|
+ ball[i].hand=i%nhand;
|
|
|
+ }
|
|
|
+ if(initdraw(nil, nil, "juggle") < 0)
|
|
|
+ sysfatal("initdraw failed: %r");
|
|
|
+ einit(Emouse);
|
|
|
+ disk=allocimage(display, Rect(0, 0, 2*RBALL+1, 2*RBALL+1), screen->chan, 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');
|
|
|
+ }
|
|
|
+}
|