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