juggle.c 4.0 KB

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