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