1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <event.h>
5
6 enum
7 {
8 NSTEP = 10, /* number of steps between throws */
9 RBALL = 10, /* radius of ball images */
10 Nball = 100,
11 };
12
13 Image *image, **disk;
14 int ndisk=0;
15 int nhand=2;
16 int delay=20; /* ms delay between steps */
17 int nball;
18 int maxhgt;
19 Rectangle win;
20
21
22 #define add addpt
23 #define sub subpt
24 #define inset insetrect
25
26
27 /*
28 * pattern lists the heights of a repeating sequence of throws.
29 * At time t, hand t%nhand throws. At that time, it must
30 * hold exactly one ball, unless it executes a 0 throw,
31 * in which case it must hold no ball. A throw of height h
32 * at time t lands at time t+h in hand (t+h)%nhand.
33 */
34 typedef struct Ball Ball;
35 struct Ball{
36 int oldhand; /* hand that previously held the ball */
37 int hgt; /* how high the throw from oldhand was */
38 int time; /* time at which ball will arrive */
39 int hand; /* hand in which ball will rest on arrival */
40 };
41 Ball ball[Nball];
throw(int t,int hgt)42 void throw(int t, int hgt){
43 int hand=t%nhand;
44 int i, b, n;
45 b=n=0;
46 for(i=0;i!=nball;i++) if(ball[i].hand==hand && ball[i].time<=t){
47 n++;
48 b=i;
49 }
50 if(hgt==0){
51 if(n!=0){
52 print("bad zero throw at t=%d, nball=%d\n", t, n);
53 exits("bad");
54 }
55 }
56 else if(n!=1){
57 print("bad ball count at t=%d, nball=%d\n", t, n);
58 exits("bad");
59 }
60 else{
61 ball[b].oldhand=hand;
62 ball[b].hgt=hgt;
63 ball[b].time=t+hgt;
64 ball[b].hand=(hand+hgt)%nhand;
65 }
66 }
bpos(int b,int step,int t)67 Point bpos(int b, int step, int t){
68 Ball *bp=&ball[b];
69 double dt=t-1+(step+1.)/NSTEP-(bp->time-bp->hgt);
70 double hgt=(bp->hgt*dt-dt*dt)*4./(maxhgt*maxhgt);
71 double alpha=(bp->oldhand+(bp->hand-bp->oldhand)*dt/bp->hgt)/(nhand-1);
72 return (Point){win.min.x+(win.max.x-win.min.x)*alpha,
73 win.max.y-1+(win.min.y-win.max.y)*hgt};
74 }
75
move(int t)76 void move(int t){
77 int i, j;
78 for(i=0;i!=NSTEP;i++){
79 if(ecanmouse()) emouse();
80 draw(image, inset(image->r, 3), display->white, nil, ZP);
81 for(j=0;j!=nball;j++)
82 fillellipse(image, bpos(j, i, t), RBALL, RBALL, disk[j%ndisk], ZP);
83 draw(screen, screen->r, image, nil, image->r.min);
84 flushimage(display, 1);
85 if(delay>0)
86 sleep(delay);
87 }
88 }
89
90 void
adddisk(int c)91 adddisk(int c)
92 {
93 Image *col;
94 disk = realloc(disk, (ndisk+1)*sizeof(Image*));
95 col=allocimage(display, Rect(0,0,1,1), CMAP8, 1, c);
96 disk[ndisk]=col;
97 ndisk++;
98 }
99
100 void
diskinit(void)101 diskinit(void)
102 {
103 /* colors taken from /sys/src/cmd/stats.c */
104
105 adddisk(0xFFAAAAFF);
106 adddisk(DPalegreygreen);
107 adddisk(DDarkyellow);
108 adddisk(DMedgreen);
109 adddisk(0x00AAFFFF);
110 adddisk(0xCCCCCCFF);
111
112 adddisk(0xBB5D5DFF);
113 adddisk(DPurpleblue);
114 adddisk(DYellowgreen);
115 adddisk(DDarkgreen);
116 adddisk(0x0088CCFF);
117 adddisk(0x888888FF);
118 }
119
120 void
usage(char * name)121 usage(char *name)
122 {
123 fprint(2, "usage: %s [start] pattern\n", name);
124 exits("usage");
125 }
126
127 void
eresized(int new)128 eresized(int new){
129 if(new && getwindow(display, Refnone) < 0) {
130 sysfatal("can't reattach to window");
131 }
132 if(image) freeimage(image);
133 image=allocimage(display, screen->r, screen->chan, 0, DNofill);
134 draw(image, image->r, display->black, nil, ZP);
135 win=inset(screen->r, 4+2*RBALL);
136 }
137 void
main(int argc,char * argv[])138 main(int argc, char *argv[]){
139 int sum, i, t, hgt, nstart, npattern;
140 char *s, *start = nil, *pattern = nil;
141
142 ARGBEGIN{
143 default:
144 usage(argv0);
145 case 'd':
146 s = ARGF();
147 if(s == nil)
148 usage(argv0);
149 delay = strtol(argv[0], &s, 0);
150 if(delay < 0 || s == argv[0] || *s != '\0')
151 usage(argv0);
152 break;
153 case 'h':
154 s = ARGF();
155 if(s == nil)
156 usage(argv0);
157 nhand = strtol(argv[0], &s, 0);
158 if(nhand <= 0 || s == argv[0] || *s != '\0')
159 usage(argv0);
160 break;
161 }ARGEND
162
163 switch(argc) {
164 case 1:
165 start="";
166 pattern=argv[0];
167 break;
168 case 2:
169 start=argv[0];
170 pattern=argv[1];
171 break;
172 default:
173 usage(argv0);
174 }
175 sum=0;
176 maxhgt=0;
177 for(s=pattern;*s;s++){
178 hgt=*s-'0';
179 sum+=hgt;
180 if(maxhgt<hgt) maxhgt=hgt;
181 }
182 npattern=s-pattern;
183 for(s=start;*s;s++){
184 hgt=*s-'0';
185 if(maxhgt<hgt) maxhgt=hgt;
186 }
187 if(sum%npattern){
188 print("%s: non-integral ball count\n",argv[0]);
189 exits("partial ball");
190 }
191 nball=sum/npattern;
192 for(i=0;i!=nball;i++){
193 ball[i].oldhand=(i-nball)%nhand;
194 if(ball[i].oldhand<0) ball[i].oldhand+=nhand;
195 ball[i].hgt=nball;
196 ball[i].time=i;
197 ball[i].hand=i%nhand;
198 }
199 if(initdraw(nil, nil, "juggle") < 0)
200 sysfatal("initdraw failed: %r");
201 einit(Emouse);
202 diskinit();
203 eresized(0);
204 if(image==0){
205 print("can't allocate bitmap");
206 exits("no space");
207 }
208 for(t=0;start[t];t++){
209 move(t);
210 throw(t, start[t]-'0');
211 }
212 nstart=t;
213 for(;;t++){
214 move(t);
215 throw(t, pattern[(t-nstart)%npattern]-'0');
216 }
217 }
218