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