8080ccb334b35ab485748b2950cef06aa37af662
[xscreensaver] / hacks / juggle.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* juggle */
3
4 #if 0
5 static const char sccsid[] = "@(#)juggle.c      5.00 2000/11/01 xlockmore";
6 #endif
7
8 /*-
9  * Copyright (c) 1996 by Tim Auckland <Tim.Auckland@Procket.com>
10  *
11  * Permission to use, copy, modify, and distribute this software and its
12  * documentation for any purpose and without fee is hereby granted,
13  * provided that the above copyright notice appear in all copies and that
14  * both that copyright notice and this permission notice appear in
15  * supporting documentation.
16  *
17  * This file is provided AS IS with no warranties of any kind.  The author
18  * shall have no liability with respect to the infringement of copyrights,
19  * trade secrets or any patents by this file or any part thereof.  In no
20  * event will the author be liable for any lost revenue or profits or
21  * other special, indirect and consequential damages.
22  *
23  * Revision History
24  * 01-Nov-2000: Allocation checks
25  * 1996: Written
26  */
27
28 /*-
29  * TODO
30  * Fix timing to run at approx same speed on all machines.
31  * Store shorter pattern and refill when required.
32  * Use -cycles and -count in a rational manner.
33  * Merge pattern selector with pattern generator.
34  * Add clubs
35  * Clap when all the balls are in the air
36  */
37
38
39 /*-
40 Notes on Adam Chalcraft Juggling Notation (used by permission)
41 a-> Adam's notation  s-> Site swap (Cambridge) notation
42
43 To define a map from a-notation to s-notation
44 ("site-swap"), both of which look like doubly infinite sequences of natural
45 numbers. In s-notation, there is a restriction on what is allowed, namely
46 for the sequence s_n, the associated function f(n)=n+s_n must be a
47 bijection. In a-notation, there is no restriction.
48
49 To go from a-notation to s-notation, you start by mapping each a_n to a
50 permutation of N, the natural numbers.
51
52 0 -> the identity
53 1 -> (10) [i.e. f(1)=0, f(0)=1]
54 2 -> (210) [i.e. f(2)=1, f(1)=0, f(0)=2]
55 3 -> (3210) [i.e. f(3)=2, f(2)=1, f(1)=0, f(0)=3]
56 etc.
57
58 Then for each n, you look at how long 0 takes to get back to 0 again and
59 you call this t_n. If a_n=0, for example, then since the identity leaves 0
60 alone, it gets back to 0 in 1 step, so t_n=1. If a_n=1, then f(0)=1. Now any
61 further a_n=0 leave 1 alone, but the next a_n>0 sends 1 back to 0. Hence t_n
62 is 2 + the number of 0's following the 1. Finally, set s_n = t_n - 1.
63
64 To give some examples, it helps to have a notation for cyclic sequences. By
65 (123), for example, I mean ...123123123123... . Now under the a-notation ->
66 s-notation mapping we have some familiar examples:
67
68 (0)->(0), (1)->(1), (2)->(2) etc.
69 (21)->(31), (31)->(51), (41)->(71) etc.
70 (10)->(20), (20)->(40), (30)->(60) etc.
71 (331)->(441), (312)->(612), (303)->(504), (321)->(531)
72 (43)->(53), (434)->(534), (433)->(633)
73 (552)->(672)
74
75 In general, the number of balls is the *average* of the s-notation, and the
76 *maximum* of the a-notation. Another theorem is that the minimum values in
77 the a-notation and the s-notation and equal, and preserved in the same
78 positions.
79
80 The usefulness of a-notation is the fact that there are no restrictions on
81 what is allowed. This makes random juggle generation much easier. It also
82 makes enumeration very easy. Another handy feature is computing changes.
83 Suppose you can do (5) and want a neat change up to (771) in s-notation
84 [Mike Day actually needed this example!]. Write them both in a-notation,
85 which gives (5) and (551). Now concatenate them (in general, there may be
86 more than one way to do this, but not in this example), to get
87 ...55555555551551551551551...
88 Now convert back to s-notation, to get
89 ...55555566771771771771771...
90 So the answer is to do two 6 throws and then go straight into (771).
91 Coming back down of course,
92 ...5515515515515515555555555...
93 converts to
94 ...7717717717716615555555555...
95 so the answer is to do a single 661 and then drop straight down to (5).
96
97 [The number of balls in the generated pattern occasionally changes.  In
98  order to decrease the number of balls I had to introduce a new symbol
99  into the Adam notation, [*] which means 'lose the current ball'.]
100 */
101
102 #ifdef STANDALONE
103 #define MODE_juggle
104 #define PROGCLASS "Juggle"
105 #define HACK_INIT init_juggle
106 #define HACK_DRAW draw_juggle
107 #define juggle_opts xlockmore_opts
108 #define DEFAULTS "*delay: 10000 \n" \
109 "*count: 150 \n" \
110 "*cycles: 30 \n" \
111 "*ncolors: 32 \n"
112 #define SMOOTH_COLORS
113 #include "xlockmore.h"          /* in xscreensaver distribution */
114 #else /* STANDALONE */
115 #include "xlock.h"              /* in xlockmore distribution */
116 #endif /* STANDALONE */
117
118 #ifdef MODE_juggle
119
120 #define DEF_PATTERN "." /* All patterns */
121 #define DEF_TRAIL "0" /* No trace */
122 #ifdef UNI
123 #define DEF_UNI "FALSE" /* No unicycle */ /* Not implemented yet */
124 #endif
125 #define DEF_SOLID "FALSE" /* Not solid */
126
127 static char *pattern;
128 static int trail;
129 #ifdef UNI
130 static Bool uni;
131 #endif
132 static Bool solid;
133
134 static XrmOptionDescRec opts[] =
135 {
136   {(char* ) "-pattern", (char *) ".juggle.pattern",
137    XrmoptionSepArg, (caddr_t) NULL},
138   {(char* ) "-trail", (char *) ".juggle.trail",
139    XrmoptionSepArg, (caddr_t) NULL},
140 #ifdef UNI
141   {(char *) "-uni", (char *) ".juggle.uni", XrmoptionNoArg, (caddr_t) "on"},
142   {(char *) "+uni", (char *) ".juggle.uni", XrmoptionNoArg, (caddr_t) "off"},
143 #endif
144   {(char *) "-solid", (char *) ".juggle.solid", XrmoptionNoArg, (caddr_t) "on"},
145   {(char *) "+solid", (char *) ".juggle.solid", XrmoptionNoArg, (caddr_t) "off"}
146 };
147 static argtype vars[] =
148 {
149   {(caddr_t *) &pattern, (char *) "pattern",
150    (char *) "Pattern", (char *) DEF_PATTERN, t_String},
151   {(caddr_t *) &trail, (char *) "trail",
152    (char *) "Trail", (char *) DEF_TRAIL, t_Int},
153 #ifdef UNI
154   {(caddr_t *) &uni, (char *) "uni",
155    (char *) "Uni", (char *) DEF_UNI, t_Bool},
156 #endif
157   {(caddr_t *) &solid, (char *) "solid",
158    (char *) "Solid", (char *) DEF_SOLID, t_Bool}
159 };
160 static OptionStruct desc[] =
161 {
162   {(char *) "-pattern string", (char *) "Cambridge Juggling Pattern"},
163   {(char *) "-trail num", (char *) "Trace Juggling Patterns"},
164 #ifdef UNI
165   {(char *) "-/+uni", (char *) "Unicycle"},
166 #endif
167   {(char *) "-/+solid", (char *) "solid color (else its a 4 panel look (half white))"}
168 };
169
170 ModeSpecOpt juggle_opts =
171 {sizeof opts / sizeof opts[0], opts,
172  sizeof vars / sizeof vars[0], vars, desc};
173
174 #ifdef USE_MODULES
175 ModStruct   juggle_description = {
176         "juggle", "init_juggle", "draw_juggle", "release_juggle",
177         "draw_juggle", "init_juggle", (char *) NULL, &juggle_opts,
178         10000, 150, 30, 1, 64, 1.0, "",
179         "Shows a Juggler, juggling", 0, NULL
180 };
181
182 #endif
183
184 #ifdef USE_XVMSUTILS
185 #include <X11/unix_time.h>
186 #endif
187 #include <time.h>
188 #if HAVE_SYS_TIME_H
189 #include <sys/time.h>
190 #else
191 #if HAVE_SYS_SELECT_H
192 #include <sys/select.h>
193 #endif
194 #endif
195
196 /* Figure */
197 #define ARMLENGTH ((int) (40.0 * sp->scale))
198 #define ARMWIDTH ((int) (8.0 * sqrt(sp->scale)))
199 #define POSE ((int) (10.0 * sp->scale))
200 #define SX ((int) (25.0 * sp->scale))
201 #define SZ ((int) (25.0 * sp->scale))
202 #define SY ((int) (25.0 * sp->scale))
203 #define HIPY ((int) (85.0 * sp->scale))
204 #define RHIPX ((int) (-15.0 * sp->scale))
205 #define LHIPX ((int) (15.0 * sp->scale))
206 #define RFX ((int) (-25.0 * sp->scale))
207 #define LFX ((int) (25.0 * sp->scale))
208 #define FY ((int) (155.0 * sp->scale))
209 #define WSTY ((int) (65.0 * sp->scale))
210 #define NEY ((int) (15.0 * sp->scale))
211 #define HED ((int) (35.0 * sp->scale))
212 #define BALLRADIUS ARMWIDTH
213 #define FIGURE1 7
214 #define FIGURE2 3
215 #define TRACE_LENGTH 50
216 #define SPIN_DEGREES 750  /* Average spinning between a throw and the next catch */
217
218 /* macros */
219
220 #ifndef XtNumber
221 #define XtNumber(arr)   ((unsigned int) (sizeof(arr) / sizeof(arr[0])))
222 #endif
223
224 #define GRAVITY(h, t) 4*(double)(h)/((t)*(t))
225
226 #define THROW_CATCH_INTERVAL (sp->count)
227 #define THROW_NULL_INTERVAL  (sp->count * 0.5)
228 #define CATCH_THROW_INTERVAL (sp->count * 0.2)
229 #define COR 0.8  /* coeff of restitution of balls (1 = perfect bounce) */
230
231
232 /* typedefs */
233
234 typedef enum {HEIGHT, ADAM} Notation;
235 typedef enum {Empty, Full, Ball} Throwable;
236 typedef enum {LEFT, RIGHT} Hand;
237 typedef enum {THROW, CATCH} Action; /* DROP is not an option */
238 typedef enum {ATCH, THRATCH, ACTION, LINKEDACTION, PTHRATCH, BPREDICTOR,
239         PREDICTOR} TrajectoryStatus;
240
241 typedef struct trajectory *TrajectoryPtr;
242
243 typedef struct {double a, b, c, d; } Spline;
244
245 typedef struct trajectory {
246   TrajectoryPtr prev, next;  /* for building list */
247   TrajectoryStatus status;
248
249   /* Throw */
250   char posn;
251   int height;
252   int adam;
253
254   /* Action */
255   Hand hand;
256   Action action;
257
258   /* LinkedAction */
259   int color;
260   int spin, divisions;
261   double degree_offset;
262   TrajectoryPtr balllink;
263   TrajectoryPtr handlink;
264
265   /* PThratch */
266
267   double dx; /* initial velocity */
268   double dy;
269
270   /* Predictor */
271   Throwable type;
272   int start, finish;
273   Spline xp, yp;
274   int x, y; /* current position */
275 } Trajectory;
276
277 /* structs */
278
279 typedef struct {
280   int         width;
281   int         height;
282   double      scale;
283   int         complexity;
284   int         cx;
285   int         cy;
286   double      Gr;
287   int         pattern;
288   Trajectory  *head;
289   XPoint   figure_path[FIGURE1];
290   XSegment figure_segs[FIGURE2];
291   XPoint      arm[2][3];
292   XPoint      *trace;
293   int         traceindex;
294   int         count;
295   time_t      begintime; /* seconds */
296   int         time; /* millisecond timer */
297   Bool        solid, uni;       
298 } jugglestruct;
299
300 static jugglestruct *juggles = (jugglestruct *) NULL;
301
302 typedef struct {
303   char * pattern;
304   char * name;
305 } patternstruct;
306
307 #define MINBALLS 2
308 #define MAXBALLS 7
309
310 typedef struct {
311   int start;
312   int number;
313 } PatternIndex;
314
315 static PatternIndex* patternindex = (PatternIndex *) NULL;
316
317 /* List of popular patterns, in any order */
318 static patternstruct portfolio[] = {
319   {(char *) "[+2 1]", (char *) "+3 1, Typical 2 ball juggler"},
320   {(char *) "[2 0]", (char *) "4 0, 2 balls 1 hand"},
321   {(char *) "[2 0 1]", (char *) "5 0 1"},
322   {(char *) "[+2 0 +2 0 0]", (char *) "+5 0 +5 0 0"},
323   {(char *) "[3]", (char *) "3, cascade"},
324   {(char *) "[+3]", (char *) "+3, reverse cascade"},
325   {(char *) "[=3]", (char *) "=3, cascade under arm"},
326   {(char *) "[&3]", (char *) "&3, cascade catching under arm"},
327   {(char *) "[_3]", (char *) "_3, bouncing cascade"},
328   {(char *) "[+3 x3 =3]", (char *) "+3 x3 =3, Mill's mess"},
329   {(char *) "[3 2 1]", (char *) "5 3 1"},
330   {(char *) "[3 3 1]", (char *) "4 4 1"},
331   {(char *) "[3 1 2]", (char *) "6 1 2, See-saw"},
332   {(char *) "[=3 3 1 2]", (char *) "=4 5 1 2"},
333   {(char *) "[=3 2 2 3 1 2]", (char *) "=6 2 2 5 1 2, =4 5 1 2 stretched"},
334   {(char *) "[+3 3 1 3]", (char *) "+4 4 1 3, anemic shower box"},
335   {(char *) "[3 3 1]", (char *) "4 4 1"},
336   {(char *) "[+3 2 3]", (char *) "+4 2 3"},
337   {(char *) "[+3 1]", (char *) "+5 1, 3 shower"},
338   {(char *) "[_3 1]", (char *) "_5 1, bouncing 3 shower"},
339   {(char *) "[3 0 3 0 3]", (char *) "5 0 5 0 5, shake 3 out of 5"},
340   {(char *) "[3 3 3 0 0]", (char *) "5 5 5 0 0, flash 3 out of 5"},
341   {(char *) "[3 3 0]", (char *) "4 5 0, complete waste of a 5 ball juggler"},
342   {(char *) "[3 3 3 0 0 0 0]", (char *) "7 7 7 0 0 0 0, 3 flash"},
343   {(char *) "[+3 0 +3 0 +3 0 0]", (char *) "+7 0 +7 0 +7 0 0"},
344   {(char *) "[4]", (char *) "4, 4 cascade"},
345   {(char *) "[+4 3]", (char *) "+5 3, 4 ball half shower"},
346   {(char *) "[4 4 2]", (char *) "5 5 2"},
347   {(char *) "[+4 4 4 +4]", (char *) "+4 4 4 +4, 4 columns"},
348   {(char *) "[4 3 +4]", (char *) "5 3 +4"},
349   {(char *) "[+4 1]", (char *) "+7 1, 4 shower"},
350   {(char *) "[4 4 4 4 0]", (char *) "5 5 5 5 0, learning 5"},
351   {(char *) "[5]", (char *) "5, 5 cascade"},
352   {(char *) "[_5 _5 _5 _5 _5 5 5 5 5 5]", (char *) "_5 _5 _5 _5 _5 5 5 5 5 5, jump rope"},
353   {(char *) "[+5 x5 =5]", (char *) "+5 x5 =5, Mill's mess for 5"},
354   {(char *) "[6]", (char *) "6, 6 cascade"},
355   {(char *) "[7]", (char *) "7, 7 cascade"},
356   {(char *) "[_7]", (char *) "_7, bouncing 7 cascade"},
357 };
358
359 /* Private Functions */
360
361 /* list management */
362
363 /* t receives trajectory to be created.  ot must point to an existing
364    trajectory or be identical to t to start a new list. */
365 #define INSERT_AFTER_TOP(t, ot)                                 \
366   if ((t = (Trajectory *)calloc(1, sizeof(Trajectory))) == NULL) \
367     {free_juggle(sp); return;}                                  \
368   (t)->next = (ot)->next;                                       \
369   (t)->prev = (ot);                                             \
370   (ot)->next = (t);                                             \
371   (t)->next->prev = (t)
372 #define INSERT_AFTER(t, ot)                                     \
373   if ((t = (Trajectory *)calloc(1, sizeof(Trajectory))) == NULL) \
374     {free_juggle(sp); return False;}                            \
375   (t)->next = (ot)->next;                                       \
376   (t)->prev = (ot);                                             \
377   (ot)->next = (t);                                             \
378   (t)->next->prev = (t)
379
380
381 /* t must point to an existing trajectory.  t must not be an
382    expression ending ->next or ->prev */
383 #define REMOVE(t)                                               \
384   (t)->next->prev = (t)->prev;                                  \
385   (t)->prev->next = (t)->next;                                  \
386   (void) free((void *) t)
387
388 static void
389 free_pattern(jugglestruct *sp) {
390         if (sp->head != NULL) {
391                 while (sp->head->next != sp->head) {
392                         Trajectory *t = sp->head->next;
393
394                         REMOVE(t); /* don't eliminate t */
395                 }
396                 (void) free((void *) sp->head);
397                 sp->head = (Trajectory *) NULL;
398         }
399 }
400
401 static void
402 free_juggle(jugglestruct *sp)
403 {
404         if (sp->trace != NULL) {
405                 (void) free((void *) sp->trace);
406                 sp->trace = (XPoint *) NULL;
407         }
408         free_pattern(sp);
409 }
410
411 static Bool
412 add_throw(jugglestruct *sp, char type, int h, Notation n)
413 {
414   Trajectory *t;
415
416   INSERT_AFTER(t, sp->head->prev);
417   t->posn = type;
418   if (n == ADAM) {
419         t->adam = h;
420         t->status = ATCH;
421   } else {
422         t->height = h;
423         t->status = THRATCH;
424   }
425   return True;
426 }
427
428 /* add a Thratch to the performance */
429 static Bool
430 program(ModeInfo *mi, const char *patn, int repeat)
431 {
432   jugglestruct *sp = &juggles[MI_SCREEN(mi)];
433   const char *p;
434   int h, i, seen;
435   Notation notation;
436   char type;
437
438   if (MI_IS_VERBOSE(mi)) {
439         (void) fprintf(stderr, "%s x %d\n", patn, repeat);
440   }
441
442   for(i=0; i < repeat; i++) {
443         type=' ';
444         h = 0;
445         seen = 0;
446         notation = HEIGHT;
447         for(p=patn; *p; p++) {
448                 if (*p >= '0' && *p <='9') {
449                 seen = 1;
450                 h = 10*h + (*p - '0');
451           } else {
452                 Notation nn = notation;
453                 switch (*p) {
454                 case '[':            /* begin Adam notation */
455                   notation = ADAM;
456                   break;
457                 case '-':            /* Inside throw */
458                 case '+':            /* Outside throw */
459                 case '=':            /* Cross throw */
460                 case '&':            /* Cross catch */
461                 case 'x':            /* Cross throw and catch */
462                 case '_':            /* Bounce */
463                   type = *p;
464                   break;
465                 case '*':            /* Lose ball */
466                   seen = 1;
467                   h = -1;
468                   /* fall through */
469                 case ']':             /* end Adam notation */
470                   nn = HEIGHT;
471                   /* fall through */
472                 case ' ':
473                   if (seen) {
474                         if (!add_throw(sp, type, h, notation))
475                                 return False;
476                         type=' ';
477                         h = 0;
478                         seen = 0;
479                   }
480                   notation = nn;
481                   break;
482                 default:
483                   (void) fprintf(stderr, "Unexpected pattern instruction: '%s'\n", p);
484                   break;
485                 }
486           }
487         }
488         if (seen) {
489           if (!add_throw(sp, type, h, notation))
490                 return False;
491         }
492   }
493         return True;
494 }
495
496 /*
497  ~~~~\~~~~~\~~~
498  \\~\\~\~\\\~~~
499  \\~\\\\~\\\~\~
500  \\\\\\\\\\\~\\
501
502 [33134231334021]
503
504 4 4 1 3 12 2 4 1 4 4 13 0 3 1
505
506 */
507 #define BOUNCEOVER 10
508
509 static void
510 adam(jugglestruct *sp)
511 {
512   Trajectory *t, *p;
513   for(t = sp->head->next; t != sp->head; t = t->next) {
514         if (t->status == ATCH) {
515           int a = t->adam;
516           t->height = 0;
517           for(p = t->next; a > 0 && p != sp->head; p = p->next) {
518                 if (p->status != ATCH || p->adam < 0 || p->adam>= a) {
519                   a--;
520                 }
521                 t->height++;
522           }
523           if(t->height > BOUNCEOVER && t->posn == ' '){
524                 t->posn = '_'; /* high defaults can be bounced */
525           }
526           t->status = THRATCH;
527 #if 0
528           (void) fprintf(stderr, "%d\t%d\n", t->adam, t->height);
529 #endif
530         }
531   }
532 }
533
534 /* Split Thratch notation into explicit throws and catches.
535    Usually Catch follows Throw in same hand, but take care of special
536    cases. */
537
538 /* ..n1.. -> .. LTn RT1 LC RC .. */
539 /* ..nm.. -> .. LTn LC RTm RC .. */
540
541 static Bool
542 part(jugglestruct *sp)
543 {
544   Trajectory *t, *nt, *p;
545   Hand hand = (LRAND() & 1) ? RIGHT : LEFT;
546
547   for (t = sp->head->next; t != sp->head; t = t->next) {
548         if (t->status > THRATCH) {
549           hand = t->hand;
550         } else if (t->status == THRATCH) {
551           char posn = '=';
552
553           /* plausibility check */
554           if (t->height <= 2 && t->posn == '_') {
555                 t->posn = ' '; /* no short bounces */
556           }
557           if (t->height <= 1 && (t->posn == '=' || t->posn == '&')) {
558                 t->posn = ' '; /* 1's need close catches */
559           }
560
561           switch (t->posn) {
562                   /*         throw          catch    */
563           case ' ': /* fall through */
564           case '-': posn = '-'; t->posn = '+'; break;
565           case '+': posn = '+'; t->posn = '-'; break;
566           case '=': posn = '='; t->posn = '+'; break;
567           case '&': posn = '+'; t->posn = '='; break;
568           case 'x': posn = '='; t->posn = '='; break;
569           case '_': posn = '_'; t->posn = '-'; break;
570           default: (void) fprintf(stderr, "unexpected posn %c\n", t->posn); break;
571           }
572           hand = (Hand) ((hand + 1) % 2);
573           t->status = ACTION;
574           t->hand = hand;
575           p = t->prev;
576
577           if (t->height == 1) {
578                 p = p->prev; /* early throw */
579           }
580           t->action = CATCH;
581           INSERT_AFTER(nt, p);
582           nt->status = ACTION;
583           nt->action = THROW;
584           nt->height = t->height;
585           nt->hand = hand;
586           nt->posn = posn;
587         }
588   }
589   return True;
590 }
591
592 /* Connnect up throws and catches to figure out which ball goes where.
593    Do the same with the juggler's hands. */
594
595 static void
596 lob(ModeInfo *mi)
597 {
598   jugglestruct *sp = &juggles[MI_SCREEN(mi)];
599   Trajectory *t, *p;
600   int h;
601   for (t = sp->head->next; t != sp->head; t = t->next) {
602         if (t->status == ACTION) {
603 #if 0
604           (void) fprintf(stderr, (t->action == CATCH) ? "A %c%c%c\n" : "A %c%c%c%d\n",
605                           t->posn,
606                           t->hand ? 'R' : 'L',
607                           (t->action == THROW) ? 'T' : (t->action == CATCH ? 'C' : 'N'),
608                           t->height);
609 #endif
610           if (t->action == THROW) {
611                 if (t->type == Empty) {
612                   if (MI_NPIXELS(mi) > 2) {
613                         t->color = 1 + NRAND(MI_NPIXELS(mi) - 2);
614                   }
615                   t->spin = NRAND(5) - 2;
616                   t->degree_offset = NRAND(360);
617                   t->divisions = 2 * ((LRAND() & 1) + 1);
618                 }
619
620                 /* search forward for next hand catch */
621                 for (p = t->next; t->handlink == NULL && p != sp->head; p = p->next) {
622                   if (p->action == CATCH) {
623                         if (t->handlink == NULL && p->hand == t->hand) {
624                           t->handlink = p; /* next catch in this hand */
625                         }
626                   }
627                 }
628
629                 if (t->height > 0) {
630                   h = t->height - 1;
631
632                   /* search forward for next ball catch */
633                   for (p = t->next; t->balllink == NULL&& p != sp->head; p = p->next) {
634                         if (p->action == CATCH) {
635                           if (t->balllink == NULL && --h < 1) { /* caught */
636 #if 0
637                                 if (p->type == Full) {
638                                   /* dropped */
639                                 }
640 #endif
641                                 t->balllink = p; /* complete trajectory */
642                                 p->type = Full;
643                                 p->color = t->color; /* accept catch */
644                                 p->spin = t->spin;
645                                 p->degree_offset = t->degree_offset;
646                                 p->divisions = t->divisions;
647                           }
648                         }
649                   }
650                 }
651                 t->type = Empty; /* thrown */
652           } else if (t->action == CATCH) {
653                 /* search forward for next throw from this hand */
654                 for (p = t->next; t->handlink == NULL && p != sp->head; p = p->next) {
655                   if (p->action == THROW && p->hand == t->hand) {
656                         p->type = t->type; /* pass ball */
657                         p->color = t->color; /* pass color */
658                         p->spin = NRAND(5) - 2;
659                         p->degree_offset = NRAND(360);
660                         p->divisions = 2 * ((LRAND() & 1) + 1);
661                         t->handlink = p;
662                   }
663                 }
664           }
665           t->status = LINKEDACTION;
666         }
667   }
668 }
669
670 /* Convert hand position symbols into actual time/space coordinates */
671 static void
672 positions(jugglestruct *sp)
673 {
674   Trajectory *t;
675   int now = 0;
676   for (t = sp->head->next; t != sp->head; t = t->next) {
677         if (t->status == PTHRATCH) {
678           now = t->start;
679         } else if (t->status == LINKEDACTION) {
680           int xo = 0, yo;
681
682           /* time */
683           if (t->action == CATCH) {
684                 if (t->type == Empty) {
685                   now += (int) THROW_NULL_INTERVAL; /* failed catch is short */
686                 } else {
687                   now += THROW_CATCH_INTERVAL;     /* successful catch */
688                 }
689           } else {
690                 now += (int) CATCH_THROW_INTERVAL;  /* throws are very short */
691           }
692           t->start = now;
693
694           /* space */
695           yo = ARMLENGTH;
696           switch (t->posn) {
697           case '-': xo = SX - POSE; break;
698           case '_':
699           case '+': xo = SX + POSE; break;
700           case '=': xo = - SX - POSE; yo += 2 * POSE; break;
701           default: (void) fprintf(stderr, "unexpected posn %c\n", t->posn); break;
702           }
703           t->x = sp->cx + ((t->hand == LEFT) ? xo : -xo);
704           t->y = sp->cy + yo;
705
706           t->status = PTHRATCH;
707         }
708   }
709 }
710
711
712 /* Private physics functions */
713
714 static Spline
715 makeSpline(int x0, double dx0, int t0, int x1, double dx1, int t1)
716 {
717   Spline s;
718   double a, b, c, d;
719   int x10;
720   double t10;
721
722   x10 = x1 - x0;
723   t10 = t1 - t0;
724   a = ((dx0 + dx1)*t10 - 2*x10) / (t10*t10*t10);
725   b = (3*x10 - (2*dx0 + dx1)*t10) / (t10*t10);
726   c = dx0;
727   d = x0;
728   s.a = a;
729   s.b = -3*a*t0 + b;
730   s.c = (3*a*t0 - 2*b)*t0 + c;
731   s.d = ((-a*t0 + b)*t0 - c)*t0 +d;
732   return s;
733 }
734
735 static double
736 makeSplinePair(Spline *s1, Spline *s2,
737                            int x0, double dx0, int t0,
738                            int x1, int t1,
739                            int x2, double dx2, int t2)
740 {
741   int x10, x21;
742   double t21, t10, t20, dx1;
743   x10 = x1 - x0;
744   x21 = x2 - x1;
745   t21 = t2 - t1;
746   t10 = t1 - t0;
747   t20 = t2 - t0;
748   dx1 = (3*x10*t21*t21 + 3*x21*t10*t10 + 3*dx0*t10*t21*t21
749                  - dx2*t10*t10*t21 - 4*dx0*t10*t21*t21) /
750         (2*t10*t21*t20);
751   *s1 = makeSpline(x0, dx0, t0, x1, dx1, t1);
752   *s2 = makeSpline(x1, dx1, t1, x2, dx2, t2);
753   return dx1;
754 }
755
756 /* Turn abstract timings into physically appropriate ball trajectories. */
757 static Bool
758 projectile(jugglestruct *sp)
759 {
760   Trajectory *t, *n;
761   for (t = sp->head->next; t != sp->head; t = t->next) {
762         if (t->status != PTHRATCH) {
763           continue;
764         }
765         if (t->action == THROW) {
766           if (t->balllink != NULL) {
767                 if (t->posn == '_') { /* Bounce once */
768                   double tc, y0, yf, yc, tb, e, i;
769
770                   tc = t->balllink->start - t->start;
771
772                   yf = sp->cy + FY;
773                   y0 = t->y;
774                   yc = t->balllink->y;
775                   e = 1; /* permissible error in yc */
776
777                   /*
778                          tb = time to bounce
779                          yt = height at catch time after one bounce
780                          one or three roots according to timing
781                          find one by interval bisection
782                   */
783                   tb = tc;
784                   for(i = tc / 2; i > 0; i/=2){
785                         double dy, dt, yt;
786                         if(tb == 0){
787                           (void) fprintf(stderr, "div by zero!\n");
788                           break;
789                         }
790                         dy = (yf - y0)/tb + 0.5*sp->Gr*tb;
791                         dt = tc - tb;
792                         yt = -COR*dy*dt + 0.5*sp->Gr*dt*dt + yf;
793                         if(yt > yc + e){
794                           tb-=i;
795                         }else if(yt < yc - e){
796                           tb+=i;
797                         }else{
798                           break;
799                         }
800                   }
801
802                   {
803                         double t0, dy;
804
805                         t->dx = (t->balllink->x - t->x) / tc;
806
807                         /* ball follows parabola down */
808                         INSERT_AFTER(n, t->prev);
809                         n->start = t->start;
810                         n->finish = (int) (t->start + tb);
811                         n->type = Ball;
812                         n->color = t->color;
813                         n->spin = t->spin;
814                         n->degree_offset = t->degree_offset;
815                         n->divisions = t->divisions;
816                         n->status = PREDICTOR;
817
818                         t->dy = (yf - y0)/tb - 0.5*sp->Gr*tb;
819                         t0 = n->start;
820                         /* Represent parabola as a degenerate spline -
821                            linear in x, quadratic in y */
822                         n->xp.a = 0;
823                         n->xp.b = 0;
824                         n->xp.c = t->dx;
825                         n->xp.d = -t->dx*t0 + t->x;
826                         n->yp.a = 0;
827                         n->yp.b = sp->Gr/2;
828                         n->yp.c = -sp->Gr*t0 + t->dy;
829                         n->yp.d = sp->Gr/2*t0*t0 - t->dy*t0 + t->y;
830
831
832                         /* ball follows parabola up */
833                         INSERT_AFTER(n, t->prev);
834                         n->start = (int) (t0 + tb);
835                         n->finish = (int) (t0 + tc);
836                         n->type = Ball;
837                         n->color = t->color;
838                         n->spin = t->spin;
839                         n->degree_offset = t->degree_offset;
840                         n->divisions = t->divisions;
841                         n->status = PREDICTOR;
842
843                         n->xp.a = 0;
844                         n->xp.b = 0;
845                         n->xp.c = t->dx;
846                         n->xp.d = -t->dx*t0 + t->x;
847
848                         dy = (yf - y0)/tb + 0.5*sp->Gr*tb;
849                         t0 = n->start;
850                         /* Represent parabola as a degenerate spline -
851                            linear in x, quadratic in y */
852                         n->yp.a = 0;
853                         n->yp.b = sp->Gr/2;
854                         n->yp.c = -sp->Gr*t0 - COR*dy;
855                         n->yp.d = sp->Gr/2*t0*t0 + COR*dy*t0 + yf;
856                   }
857
858                   t->status = BPREDICTOR;
859
860                 } else {
861                   double t0, dt;
862
863                   /* ball follows parabola */
864                   INSERT_AFTER(n, t->prev);
865                   n->start = t->start;
866                   n->finish = t->balllink->start;
867                   n->type = Ball;
868                   n->color = t->color;
869                   n->spin = t->spin;
870                   n->degree_offset = t->degree_offset;
871                   n->divisions = t->divisions;
872                   n->status = PREDICTOR;
873
874                   t0 = n->start;
875                   dt = t->balllink->start - t->start;
876                   t->dx = (t->balllink->x - t->x) / dt;
877                   t->dy = (t->balllink->y - t->y) / dt - sp->Gr/2 * dt;
878
879                   /* Represent parabola as a degenerate spline -
880                          linear in x, quadratic in y */
881                   n->xp.a = 0;
882                   n->xp.b = 0;
883                   n->xp.c = t->dx;
884                   n->xp.d = -t->dx*t0 + t->x;
885                   n->yp.a = 0;
886                   n->yp.b = sp->Gr/2;
887                   n->yp.c = -sp->Gr*t0 + t->dy;
888                   n->yp.d = sp->Gr/2*t0*t0 - t->dy*t0 + t->y;
889
890
891                   t->status = BPREDICTOR;
892                 }
893           } else { /* Zero Throw */
894                 t->status = BPREDICTOR;
895           }
896         }
897   }
898   return True;
899 }
900
901 /* Turn abstract hand motions into cubic splines. */
902 static void
903 hands(jugglestruct *sp)
904 {
905   Trajectory *t, *u, *v;
906   for (t = sp->head->next; t != sp->head; t = t->next) {
907         /* no throw => no velocity */
908         if (t->status != BPREDICTOR) {
909           continue;
910         }
911
912         u = t->handlink;
913         if (u == NULL) { /* no next catch */
914           continue;
915         }
916         v = u->handlink;
917         if (v == NULL) { /* no next throw */
918           continue;
919         }
920
921         /* double spline takes hand from throw, thru catch, to
922            next throw */
923
924         t->finish = u->start;
925         t->status = PREDICTOR;
926
927         u->finish = v->start;
928         u->status = PREDICTOR;
929
930         (void) makeSplinePair(&t->xp, &u->xp,
931                                                   t->x, t->dx, t->start,
932                                                   u->x, u->start,
933                                                   v->x, v->dx, v->start);
934         (void) makeSplinePair(&t->yp, &u->yp,
935                                                   t->y, t->dy, t->start,
936                                                   u->y, u->start,
937                                                   v->y, v->dy, v->start);
938
939         t->status = PREDICTOR;
940   }
941 }
942
943 /* Given target x, y find_elbow puts hand at target if possible,
944  * otherwise makes hand point to the target */
945 static void
946 find_elbow(jugglestruct *sp, XPoint *h, XPoint *e, int x, int y, int z)
947 {
948   double r, h2, t;
949
950   h2 = x*x + y*y + z*z;
951   if (h2 > 4*ARMLENGTH*ARMLENGTH) {
952         t = ARMLENGTH/sqrt(h2);
953         e->x = (short) (t*x);
954         e->y = (short) (t*y);
955         h->x = 2 * e->x;
956         h->y = 2 * e->y;
957   } else {
958         r = sqrt((double)(x*x + z*z));
959         t = sqrt(4 * ARMLENGTH * ARMLENGTH / h2 - 1);
960         e->x = (short) (x*(1 - y*t/r)/2);
961         e->y = (short) ((y + r*t)/2);
962         h->x = x;
963         h->y = y;
964   }
965 }
966
967 /* NOTE: returned x, y adjusted for arm reach */
968 static void
969 draw_arm(ModeInfo * mi, Hand side, int *x, int *y)
970 {
971   Display *dpy = MI_DISPLAY(mi);
972   Window win = MI_WINDOW(mi);
973   GC gc = MI_GC(mi);
974   jugglestruct *sp = &juggles[MI_SCREEN(mi)];
975
976   int sig = (side == LEFT) ? 1 : -1;
977
978   XSetLineAttributes(dpy, gc,
979                 ARMWIDTH, LineSolid, CapRound, JoinRound);
980   if (sp->arm[side][0].x != *x || sp->arm[side][0].y != *y) {
981         XPoint h, e;
982         XSetForeground(dpy, gc, MI_BLACK_PIXEL(mi));
983         find_elbow(sp, &h, &e, *x - sig*SX - sp->cx, *y - SY - sp->cy, SZ);
984         XDrawLines(dpy, win, gc, sp->arm[side], 3, CoordModeOrigin);
985         *x = sp->arm[side][0].x = sp->cx + sig*SX + h.x;
986         *y = sp->arm[side][0].y = sp->cy + SY + h.y;
987         sp->arm[side][1].x = sp->cx + sig*SX + e.x;
988         sp->arm[side][1].y = sp->cy + SY + e.y;
989   }
990   XSetForeground(dpy, gc, MI_WHITE_PIXEL(mi));
991   XDrawLines(dpy, win, gc, sp->arm[side], 3, CoordModeOrigin);
992   XSetLineAttributes(dpy, gc,
993                 1, LineSolid, CapNotLast, JoinRound);
994 }
995
996 static void
997 draw_figure(ModeInfo * mi)
998 {
999   Display *dpy = MI_DISPLAY(mi);
1000   Window win = MI_WINDOW(mi);
1001   GC gc = MI_GC(mi);
1002   jugglestruct *sp = &juggles[MI_SCREEN(mi)];
1003
1004   XSetLineAttributes(dpy, gc,
1005                 ARMWIDTH, LineSolid, CapRound, JoinRound);
1006   XSetForeground(dpy, gc, MI_WHITE_PIXEL(mi));
1007   XDrawLines(dpy, win, gc, sp->figure_path, FIGURE1, CoordModeOrigin);
1008   XDrawSegments(dpy, win, gc, sp->figure_segs, FIGURE2);
1009   XDrawArc(dpy, win, gc,
1010          sp->cx - HED/2, sp->cy + NEY - HED, HED, HED, 0, 64*360);
1011   XSetLineAttributes(dpy, gc,
1012                 1, LineSolid, CapNotLast, JoinRound);
1013 }
1014
1015
1016 /* dumps a human-readable rendition of the current state of the juggle
1017    pipeline to stderr for debugging */
1018 #ifdef OLDDEBUG
1019 static void
1020 dump(jugglestruct *sp)
1021 {
1022   Trajectory *t;
1023
1024   for (t = sp->head->next; t != sp->head; t = t->next) {
1025         switch (t->status) {
1026         case THROW:
1027           (void) fprintf(stderr, "T %c%d\n", t->posn, t->height);
1028           break;
1029         case ACTION:
1030           (void) fprintf(stderr, t->action == CATCH?"A %c%c%c\n":"A %c%c%c%d\n",
1031                           t->posn,
1032                           t->hand ? 'R' : 'L',
1033                           (t->action == THROW)?'T':(t->action == CATCH?'C':'N'),
1034                           t->height);
1035           break;
1036         case LINKEDACTION:
1037           (void) fprintf(stderr, "L %c%c%c%d %d\n",
1038                           t->posn,
1039                           t->hand?'R':'L',
1040                           (t->action == THROW)?'T':(t->action == CATCH?'C':'N'),
1041                           t->height, t->color);
1042           break;
1043         case PTHRATCH:
1044           (void) fprintf(stderr, "O %c%c%c%d %d %2d %6d %6d\n", t->posn,
1045                           t->hand?'R':'L',
1046                           (t->action == THROW)?'T':(t->action == CATCH?'C':'N'),
1047                           t->height, t->type, t->color,
1048                           t->start, t->finish);
1049           break;
1050         case PREDICTOR:
1051           (void) fprintf(stderr, "P %c      %2d %6d %6d %g\n",
1052                           t->type == Ball?'b':t->type == Empty?'e':'f',
1053                           t->color,
1054                           t->start, t->finish, t->yp.c);
1055           break;
1056         default:
1057           (void) fprintf(stderr, "status %d not implemented\n", t->status);
1058           break;
1059         }
1060   }
1061 }
1062 #endif
1063
1064 static int get_num_balls(const char *j)
1065 {
1066   int balls = 0;
1067   const char *p;
1068   int h = 0;
1069   for (p = j; *p; p++) {
1070         if (*p >= '0' && *p <='9') { /* digit */
1071           h = 10*h + (*p - '0');
1072         } else {
1073           if (h > balls) {
1074                 balls = h;
1075           }
1076           h = 0;
1077         }
1078   }
1079   return balls;
1080 }
1081
1082 #ifdef __cplusplus
1083 extern "C" {
1084 #endif
1085
1086 static int compare_num_balls(const void *p1, const void *p2)
1087 {
1088   int i = get_num_balls(((patternstruct*)p1)->pattern);
1089   int j = get_num_balls(((patternstruct*)p2)->pattern);
1090   if (i > j) {
1091         return (1);
1092   } else if (i < j) {
1093         return (-1);
1094   } else {
1095         return (0);
1096   }
1097 }
1098
1099 #ifdef __cplusplus
1100 }
1101 #endif
1102
1103 /* Public functions */
1104
1105 void
1106 release_juggle(ModeInfo * mi)
1107 {
1108         if (juggles != NULL) {
1109                 int screen;
1110
1111                 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
1112                         free_juggle(&juggles[screen]);
1113                 (void) free((void *) juggles);
1114                 juggles = (jugglestruct *) NULL;
1115         }
1116         if (patternindex != NULL) {
1117                 (void) free((void *) patternindex);
1118                 patternindex = (PatternIndex *) NULL;
1119         }
1120 }
1121
1122 void
1123 init_juggle(ModeInfo * mi)
1124 {
1125         jugglestruct *sp;
1126         int i;
1127         XPoint figure1[FIGURE1];
1128         XSegment figure2[FIGURE2];
1129         if (pattern != NULL && *pattern == '.') {
1130           pattern = NULL;
1131         }
1132         if (pattern == NULL && patternindex == NULL) {
1133           /* pattern list needs indexing */
1134           int nelements = sizeof(portfolio)/sizeof(patternstruct);
1135           int maxballs;
1136           int numpat = 0;
1137
1138           /* sort according to number of balls */
1139           qsort((void*)portfolio, nelements,
1140                         sizeof(patternstruct), compare_num_balls);
1141
1142           /* last pattern has most balls */
1143           maxballs = get_num_balls(portfolio[nelements - 1].pattern);
1144           /* allocate index */
1145           if ((patternindex = (PatternIndex *) calloc(maxballs + 1,
1146                                 sizeof (PatternIndex))) == NULL) {
1147                 return;
1148           }
1149
1150           /* run through sorted list, indexing start of each group
1151                  and number in group */
1152           maxballs = 1;
1153           for (i = 0; i < nelements; i++) {
1154                 int b = get_num_balls(portfolio[i].pattern);
1155                 if (b > maxballs) {
1156                   if (MI_IS_VERBOSE(mi)) {
1157                         (void) fprintf(stderr, "%d %d ball pattern%s\n",
1158                                         numpat, maxballs, (numpat == 1) ? "" : "s");
1159                   }
1160                   patternindex[maxballs].number = numpat;
1161                   maxballs = b;
1162                   numpat = 1;
1163                   patternindex[maxballs].start = i;
1164                 } else {
1165                   numpat++;
1166                 }
1167           }
1168           if (MI_IS_VERBOSE(mi)) {
1169                 (void) fprintf(stderr, "%d %d ball pattern%s\n",
1170                                 numpat, maxballs, (numpat == 1) ? "" : "s");
1171           }
1172           patternindex[maxballs].number = numpat;
1173         }
1174
1175         if (juggles == NULL) { /* allocate jugglestruct */
1176                 if ((juggles = (jugglestruct *) calloc(MI_NUM_SCREENS(mi),
1177                                sizeof (jugglestruct))) == NULL) {
1178                         release_juggle(mi);
1179                         return;
1180                 }
1181         }
1182         sp = &juggles[MI_SCREEN(mi)];
1183
1184         sp->count = 0;
1185
1186         if (MI_IS_FULLRANDOM(mi)) {
1187                 sp->solid = (Bool) (LRAND() & 1);
1188 #ifdef UNI
1189                 sp->uni = (Bool) (LRAND() & 1);
1190 #endif
1191         } else {
1192                 sp->solid = solid;
1193 #ifdef UNI
1194                 sp->uni = uni;
1195 #endif
1196         }
1197
1198         sp->width = MI_WIDTH(mi);
1199         sp->height = MI_HEIGHT(mi);
1200         sp->count = ABS(MI_COUNT(mi));
1201         if (sp->count == 0)
1202                 sp->count = 150;
1203         sp->scale = sp->height / 480.0;
1204         /* vary x a little so the juggler does not burn the screen */
1205         sp->cx = sp->width / 2 + RFX + NRAND(LFX - RFX + 1);
1206         sp->cy = sp->height - FY - ((int) sp->uni) * FY / 3; /* raise higher */
1207         /* "7" hits top of screen */
1208         sp->Gr = GRAVITY(sp->cy, 7 * THROW_CATCH_INTERVAL);
1209
1210         figure1[0].x = LHIPX, figure1[0].y = HIPY;
1211         figure1[1].x = 0, figure1[1].y = WSTY;
1212         figure1[2].x = SX, figure1[2].y = SY;
1213         figure1[3].x = -SX, figure1[3].y = SY;
1214         figure1[4].x = 0, figure1[4].y = WSTY;
1215         figure1[5].x = RHIPX, figure1[5].y = HIPY;
1216         figure1[6].x = LHIPX, figure1[6].y = HIPY;
1217         figure2[0].x1 = 0, figure2[0].y1 = SY,
1218           figure2[0].x2 = 0, figure2[0].y2 = NEY;
1219         figure2[1].x1 = LHIPX, figure2[1].y1 = HIPY,
1220           figure2[1].x2 = LFX, figure2[1].y2 = FY;
1221         figure2[2].x1 = RHIPX, figure2[2].y1 = HIPY,
1222           figure2[2].x2 = RFX, figure2[2].y2 = FY;
1223
1224         /* Body Path */
1225         for (i = 0; i <  FIGURE1; i++) {
1226           sp->figure_path[i].x = figure1[i].x + sp->cx;
1227           sp->figure_path[i].y = figure1[i].y + sp->cy;
1228         }
1229         /* Body Segments */
1230         for (i = 0; i < FIGURE2; i++) {
1231           sp->figure_segs[i].x1 = figure2[i].x1 + sp->cx;
1232           sp->figure_segs[i].y1 = figure2[i].y1 + sp->cy;
1233           sp->figure_segs[i].x2 = figure2[i].x2 + sp->cx;
1234           sp->figure_segs[i].y2 = figure2[i].y2 + sp->cy;
1235         }
1236         /* Shoulders */
1237         sp->arm[LEFT][2].x = sp->cx + SX;
1238         sp->arm[LEFT][2].y = sp->cy + SY;
1239         sp->arm[RIGHT][2].x = sp->cx - SX;
1240         sp->arm[RIGHT][2].y = sp->cy + SY;
1241
1242         if (sp->trace == NULL) {
1243           if ((sp->trace = (XPoint *)calloc(trail, sizeof(XPoint))) == NULL) {
1244                 free_juggle(sp);
1245                 return;
1246           }
1247         }
1248
1249         /* Clear the background. */
1250         MI_CLEARWINDOW(mi);
1251
1252         draw_figure(mi);
1253
1254         /* record start time */
1255         sp->begintime = time(NULL);
1256
1257         free_pattern(sp);
1258
1259         /* create circular list */
1260         INSERT_AFTER_TOP(sp->head, sp->head);
1261
1262         /* generate pattern */
1263         if (pattern == NULL) {
1264
1265 #define MAXPAT 10
1266 #define MAXREPEAT 30
1267 #define CHANGE_BIAS 8 /* larger makes num_ball changes less likely */
1268 #define POSITION_BIAS 20 /* larger makes hand movements less likely */
1269
1270           int count = 0;
1271           int num_balls = MINBALLS + NRAND(MAXBALLS - MINBALLS);
1272           while (count < MI_CYCLES(mi)) {
1273                 char buf[MAXPAT * 3 + 3], *b = buf;
1274                 int maxseen = 0;
1275                 int l = NRAND(MAXPAT) + 1;
1276                 int t = NRAND(MAXREPEAT) + 1;
1277
1278                 { /* vary number of balls */
1279                   int new_balls = num_balls;
1280                   int change;
1281
1282                   if (new_balls == 2) /* Do not juggle 2 that often */
1283                     change = NRAND(2 + CHANGE_BIAS / 4);
1284                   else
1285                     change = NRAND(2 + CHANGE_BIAS);
1286                   switch (change) {
1287                   case 0:
1288                         new_balls++;
1289                         break;
1290                   case 1:
1291                         new_balls--;
1292                         break;
1293                   default:
1294                         break; /* NO-OP */
1295                   }
1296                   if (new_balls < MINBALLS) {
1297                         new_balls += 2;
1298                   }
1299                   if (new_balls > MAXBALLS) {
1300                         new_balls -= 2;
1301                   }
1302                   if (new_balls < num_balls) {
1303                         if (!program(mi, "[*]", 1)) /* lose ball */
1304                                 return;
1305                   }
1306                   num_balls = new_balls;
1307                 }
1308                 count++;
1309
1310                 if (NRAND(2) && patternindex[num_balls].number) {
1311                   /* Pick from PortFolio */
1312                   if (!program(mi,
1313                           portfolio[patternindex[num_balls].start +
1314                                   NRAND(patternindex[num_balls].number)].pattern,
1315                                   t))
1316                         return;
1317                 } else {
1318                   /* Invent a new pattern */
1319                   *b++='[';
1320                   for(i = 0; i < l; i++){
1321                         int n, m;
1322                         do { /* Triangular Distribution => high values more likely */
1323                           m = NRAND(num_balls + 1);
1324                           n = NRAND(num_balls + 1);
1325                         } while(m >= n);
1326                         if (n == num_balls) {
1327                           maxseen = 1;
1328                         }
1329                         switch(NRAND(6 + POSITION_BIAS)){
1330                         case 0:            /* Inside throw */
1331                           *b++ = '-'; break;
1332                         case 1:            /* Outside throw */
1333                           *b++ = '+'; break;
1334                         case 2:            /* Cross throw */
1335                           *b++ = '='; break;
1336                         case 3:            /* Cross catch */
1337                           *b++ = '&'; break;
1338                         case 4:            /* Cross throw and catch */
1339                           *b++ = 'x'; break;
1340                         case 5:            /* Bounce */
1341                           *b++ = '_'; break;
1342                         default:
1343                           break; /* NO-OP */
1344                         }
1345
1346                         *b++ = n + '0';
1347                         *b++ = ' ';
1348                   }
1349                   *b++ = ']';
1350                   *b = '\0';
1351                   if (maxseen) {
1352                         if (!program(mi, buf, t))
1353                                 return;
1354                   }
1355                 }
1356           }
1357         } else { /* pattern supplied in height or 'a' notation */
1358           if (!program(mi, pattern, MI_CYCLES(mi)))
1359                 return;
1360         }
1361
1362         adam(sp);
1363
1364         if (!part(sp))
1365                 return;
1366
1367         lob(mi);
1368
1369         positions(sp);
1370
1371         if (!projectile(sp))
1372                 return;
1373
1374         hands(sp);
1375
1376 #ifdef OLDDEBUG
1377         dump(sp);
1378 #endif
1379 }
1380
1381 #define CUBIC(s, t) ((((s).a * (t) + (s).b) * (t) + (s).c) * (t) + (s).d)
1382
1383 #ifdef SUNOS4
1384 /*-
1385  * Workaround SunOS 4 framebuffer bug which causes balls to leave dust
1386  * trace behind when erased
1387  */
1388 #define ERASE_BALL(x,y) \
1389         XSetForeground(dpy, gc, MI_BLACK_PIXEL(mi)); \
1390         XFillArc(dpy, window, gc, \
1391                 (x) - BALLRADIUS - 2, (y) - BALLRADIUS - 2, \
1392                 2*(BALLRADIUS + 2), 2*(BALLRADIUS + 2), 0, 23040)
1393 #else
1394
1395 #define ERASE_BALL(x,y) \
1396         XSetForeground(dpy, gc, MI_BLACK_PIXEL(mi)); \
1397         XFillArc(dpy, window, gc, \
1398                 (x) - BALLRADIUS, (y) - BALLRADIUS, \
1399                 2*BALLRADIUS, 2*BALLRADIUS, 0, 23040)
1400 #endif
1401
1402 static void
1403 draw_juggle_ball(ModeInfo *mi, unsigned long color, int x, int y, double degree_offset, int divisions)
1404 {
1405         Display    *dpy = MI_DISPLAY(mi);
1406         Window      window = MI_WINDOW(mi);
1407         GC          gc = MI_GC(mi);
1408         jugglestruct *sp = &juggles[MI_SCREEN(mi)];
1409         int offset;
1410
1411         XSetForeground(dpy, gc, color);
1412         if ((color == MI_WHITE_PIXEL(mi)) ||
1413             ((divisions != 2) && (divisions != 4)) || sp->solid) {
1414                 XFillArc(dpy, window, gc,
1415                         x - BALLRADIUS, y - BALLRADIUS,
1416                         2*BALLRADIUS, 2*BALLRADIUS,
1417                         0, 23040);
1418                 return;
1419         }
1420         offset = (int) (degree_offset * 64);
1421         if (divisions == 4) { /* 90 degree divisions */
1422                 XFillArc(dpy, window, gc,
1423                         x - BALLRADIUS, y - BALLRADIUS,
1424                         2*BALLRADIUS, 2*BALLRADIUS,
1425                         offset, 5760);
1426                 XFillArc(dpy, window, gc,
1427                         x - BALLRADIUS, y - BALLRADIUS,
1428                         2*BALLRADIUS, 2*BALLRADIUS,
1429                         (offset + 11520) % 23040, 5760);
1430                 XSetForeground(dpy, gc, MI_WHITE_PIXEL(mi));
1431                 XFillArc(dpy, window, gc,
1432                         x - BALLRADIUS, y - BALLRADIUS,
1433                         2*BALLRADIUS, 2*BALLRADIUS,
1434                         (offset + 5760) % 23040, 5760);
1435                 XFillArc(dpy, window, gc,
1436                         x - BALLRADIUS, y - BALLRADIUS,
1437                         2*BALLRADIUS, 2*BALLRADIUS,
1438                         (offset + 17280) % 23040, 5760);
1439         } else { /* 180 degree divisions */
1440                 XFillArc(dpy, window, gc,
1441                         x - BALLRADIUS, y - BALLRADIUS,
1442                         2*BALLRADIUS, 2*BALLRADIUS,
1443                         offset, 11520);
1444                 XSetForeground(dpy, gc, MI_WHITE_PIXEL(mi));
1445                 XFillArc(dpy, window, gc,
1446                         x - BALLRADIUS, y - BALLRADIUS,
1447                         2*BALLRADIUS, 2*BALLRADIUS,
1448                         (offset + 11520) % 23040, 11520);
1449         }
1450         XFlush(dpy);
1451 }
1452
1453 void
1454 draw_juggle(ModeInfo * mi)
1455 {
1456         Display    *dpy = MI_DISPLAY(mi);
1457         Window      window = MI_WINDOW(mi);
1458         GC          gc = MI_GC(mi);
1459         Trajectory *traj;
1460         int future = 0;
1461         int length = 0;
1462         jugglestruct *sp;
1463
1464         if (juggles == NULL)
1465                 return;
1466         sp = &juggles[MI_SCREEN(mi)];
1467         if (sp->trace == NULL)
1468                 return;
1469
1470         MI_IS_DRAWN(mi) = True;
1471
1472         draw_figure(mi);
1473
1474         {
1475           struct timeval tv;
1476 # ifdef GETTIMEOFDAY_TWO_ARGS
1477       struct timezone tzp;
1478           gettimeofday(&tv, &tzp);
1479 # else
1480           gettimeofday(&tv);
1481 # endif
1482
1483           sp->time = (int) ((tv.tv_sec - sp->begintime)*1000 + tv.tv_usec/1000);
1484         }
1485         for (traj = sp->head->next; traj != sp->head; traj = traj->next) {
1486           length++;
1487           if (traj->status != PREDICTOR) {
1488                 continue;
1489           }
1490           if (traj->start > future) {
1491                 future = traj->start;
1492           }
1493           if (sp->time < traj->start) { /* early */
1494                 continue;
1495           } else if (sp->time < traj->finish) { /* working */
1496                 int x = (int) CUBIC(traj->xp, sp->time);
1497                 int y = (int) CUBIC(traj->yp, sp->time);
1498                 unsigned long color;
1499
1500                 if (MI_NPIXELS(mi) > 2) {
1501                   color = MI_PIXEL(mi, traj->color);
1502                 } else {
1503                   color = MI_WHITE_PIXEL(mi);
1504                 }
1505                 if (traj->type == Empty || traj->type == Full) {
1506                   draw_arm(mi, traj->hand, &x, &y);
1507                 }
1508                 if (traj->type == Ball || traj->type == Full) {
1509                   if(trail > 0) {
1510                         ERASE_BALL(sp->trace[sp->traceindex].x,
1511                                 sp->trace[sp->traceindex].y);
1512                         sp->trace[sp->traceindex].x = traj->x;
1513                         sp->trace[sp->traceindex].y = traj->y;
1514                         if (++sp->traceindex >= trail) {
1515                           sp->traceindex = 0;
1516                         }
1517                   } else {
1518                         ERASE_BALL(traj->x, traj->y);
1519                   }
1520                   draw_juggle_ball(mi, color, x, y, traj->degree_offset, traj->divisions);
1521                   traj->degree_offset = traj->degree_offset +
1522                     SPIN_DEGREES * traj->spin / sp->count;
1523                   if (traj->degree_offset < 0.0)
1524                         traj->degree_offset += 360.0;
1525                   else if (traj->degree_offset >= 360.0)
1526                         traj->degree_offset -= 360.0;
1527                 }
1528                 traj->x = x;
1529                 traj->y = y;
1530           } else { /* expired */
1531                 Trajectory *n = traj;
1532
1533                 ERASE_BALL(traj->x, traj->y);
1534                 traj=traj->prev;
1535                 REMOVE(n);
1536           }
1537         }
1538
1539         /*** FIXME-BEGIN ***/
1540         /* pattern generator would refill here when necessary */
1541 #if 1
1542         if (future == 0) {
1543 #else
1544         if (sp->count > MI_CYCLES(mi)) { /* pick a new juggle */
1545 #endif
1546                         init_juggle(mi);
1547         }
1548         /*** FIXME-END ***/
1549
1550 }
1551
1552 #endif /* MODE_juggle */