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