From http://www.jwz.org/xscreensaver/xscreensaver-5.38.tar.gz
[xscreensaver] / hacks / juggle.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* juggle */
3
4 #if 0
5 static const char sccsid[] = "@(#)juggle.c      5.10 2003/09/02 xlockmore";
6 #endif
7
8 /*-
9  * Copyright (c) 1996 by Tim Auckland <tda10.geo@yahoo.com>
10  *
11  * Permission to use, copy, modify, and distribute this software and its
12  * documentation for any purpose and without fee is hereby granted,
13  * provided that the above copyright notice appear in all copies and that
14  * both that copyright notice and this permission notice appear in
15  * supporting documentation.
16  *
17  * This file is provided AS IS with no warranties of any kind.  The author
18  * shall have no liability with respect to the infringement of copyrights,
19  * trade secrets or any patents by this file or any part thereof.  In no
20  * event will the author be liable for any lost revenue or profits or
21  * other special, indirect and consequential damages.
22  *
23  * Revision History
24  * 13-Dec-2004: [TDA] Use -cycles and -count in a rational manner.
25  *              Add -rings, -bballs.  Add -describe.  Finally made
26  *              live pattern updates possible.  Add refill_juggle(),
27  *              change_juggle() and reshape_juggle().  Make
28  *              init_juggle() non-destructive.  Reorder erase/draw
29  *              operations.  Update xscreensaver xml and manpage.
30  * 15-Nov-2004: [TDA] Fix all memory leaks.
31  * 12-Nov-2004: [TDA] Add -torches and another new trail
32  *              implementation, so that different objects can have
33  *              different length trails.
34  * 11-Nov-2004: [TDA] Clap when all the balls are in the air.
35  * 10-Nov-2004: [TDA] Display pattern name converted to hight
36  *              notation.
37  * 31-Oct-2004: [TDA] Add -clubs and new trail implementation.
38  * 02-Sep-2003: Non-real time to see what is happening without a
39  *              strobe effect for slow machines.
40  * 01-Nov-2000: Allocation checks
41  * 1996: Written
42  */
43
44 /*-
45  * TODO
46  * Implement the anonymously promised -uni option.
47  */
48
49
50 /*
51  * Notes on Adam Chalcraft Juggling Notation (used by permission)
52  * a-> Adam's notation  s-> Site swap (Cambridge) notation
53  *
54  * To define a map from a-notation to s-notation ("site-swap"), both
55  * of which look like doubly infinite sequences of natural numbers. In
56  * s-notation, there is a restriction on what is allowed, namely for
57  * the sequence s_n, the associated function f(n)=n+s_n must be a
58  * bijection. In a-notation, there is no restriction.
59  *
60  * To go from a-notation to s-notation, you start by mapping each a_n
61  * to a permutation of N, the natural numbers.
62  *
63  * 0 -> the identity
64  * 1 -> (10) [i.e. f(1)=0, f(0)=1]
65  * 2 -> (210) [i.e. f(2)=1, f(1)=0, f(0)=2]
66  * 3 -> (3210) [i.e. f(3)=2, f(2)=1, f(1)=0, f(0)=3]
67  * etc.
68  *
69  * Then for each n, you look at how long 0 takes to get back to 0
70  * again and you call this t_n. If a_n=0, for example, then since the
71  * identity leaves 0 alone, it gets back to 0 in 1 step, so t_n=1. If
72  * a_n=1, then f(0)=1. Now any further a_n=0 leave 1 alone, but the
73  * next a_n>0 sends 1 back to 0. Hence t_n is 2 + the number of 0's
74  * following the 1. Finally, set s_n = t_n - 1.
75  *
76  * To give some examples, it helps to have a notation for cyclic
77  * sequences. By (123), for example, I mean ...123123123123... . Now
78  * under the a-notation -> s-notation mapping we have some familiar
79  * examples:
80  *
81  * (0)->(0), (1)->(1), (2)->(2) etc.
82  * (21)->(31), (31)->(51), (41)->(71) etc.
83  * (10)->(20), (20)->(40), (30)->(60) etc.
84  * (331)->(441), (312)->(612), (303)->(504), (321)->(531)
85  * (43)->(53), (434)->(534), (433)->(633)
86  * (552)->(672)
87  *
88  * In general, the number of balls is the *average* of the s-notation,
89  * and the *maximum* of the a-notation. Another theorem is that the
90  * minimum values in the a-notation and the s-notation and equal, and
91  * preserved in the same positions.
92  *
93  * The usefulness of a-notation is the fact that there are no
94  * restrictions on what is allowed. This makes random juggle
95  * generation much easier. It also makes enumeration very
96  * easy. Another handy feature is computing changes.  Suppose you can
97  * do (5) and want a neat change up to (771) in s-notation [Mike Day
98  * actually needed this example!]. Write them both in a-notation,
99  * which gives (5) and (551). Now concatenate them (in general, there
100  * may be more than one way to do this, but not in this example), to
101  * get
102  *
103  * ...55555555551551551551551...
104  *
105  * Now convert back to s-notation, to get
106  *
107  * ...55555566771771771771771...
108  *
109  * So the answer is to do two 6 throws and then go straight into
110  * (771).  Coming back down of course,
111  *
112  * ...5515515515515515555555555...
113  *
114  * converts to
115  *
116  * ...7717717717716615555555555...
117  *
118  * so the answer is to do a single 661 and then drop straight down to
119  * (5).
120  *
121  * [The number of balls in the generated pattern occasionally changes.
122  * In order to decrease the number of balls I had to introduce a new
123  * symbol into the Adam notation, [*] which means 'lose the current
124  * ball'.]
125  */
126
127 /* This code uses so many linked lists it's worth having a built-in
128  * leak-checker */
129 #undef MEMTEST
130
131 #ifdef STANDALONE
132 # define MODE_juggle
133 # define DEFAULTS       "*delay:   10000 \n" \
134                                         "*count:   200   \n" \
135                                         "*cycles:  1000  \n" \
136                                         "*ncolors: 32    \n" \
137                                         "*font:    -*-helvetica-bold-r-normal-*-180-*\n" \
138                                         "*fpsSolid: true\n" \
139
140 # define release_juggle 0
141 # define reshape_juggle 0
142 # define juggle_handle_event 0
143 # undef SMOOTH_COLORS
144 # include "xlockmore.h"         /* in xscreensaver distribution */
145 #else /* STANDALONE */
146 # include "xlock.h"             /* in xlockmore distribution */
147 #endif /* STANDALONE */
148
149 #ifdef MODE_juggle
150
151 #if 0
152 #define XClearWindow(d, w) \
153 { \
154  XSetForeground(d, MI_GC(mi), MI_PIXEL(mi, 3)); \
155  XFillRectangle(d, w, MI_GC(mi), \
156    0, 0, (unsigned int) MI_WIDTH(mi), (unsigned int) MI_HEIGHT(mi)); \
157 }
158 #endif
159
160 #define DEF_PATTERN "random" /* All patterns */
161 #define DEF_TAIL "1" /* No trace */
162 #ifdef UNI
163 /* Maybe a ROLA BOLA would be at a better angle for viewing */
164 #define DEF_UNI "False" /* No unicycle */ /* Not implemented yet */
165 #endif
166 #define DEF_REAL "True"
167 #define DEF_DESCRIBE "True"
168
169 #define DEF_BALLS "True" /* Use Balls */
170 #define DEF_CLUBS "True" /* Use Clubs */
171 #define DEF_TORCHES "True" /* Use Torches */
172 #define DEF_KNIVES "True" /* Use Knives */
173 #define DEF_RINGS "True" /* Use Rings */
174 #define DEF_BBALLS "True" /* Use Bowling Balls */
175
176 #ifndef XtNumber
177 #define XtNumber(arr)   ((unsigned int) (sizeof(arr) / sizeof(arr[0])))
178 #endif
179
180 static char *pattern;
181 static int tail;
182 #ifdef UNI
183 static Bool uni;
184 #endif
185 static Bool real;
186 static Bool describe;
187 static Bool balls;
188 static Bool clubs;
189 static Bool torches;
190 static Bool knives;
191 static Bool rings;
192 static Bool bballs;
193 static char *only;
194
195 static XrmOptionDescRec opts[] =
196 {
197   {"-pattern",  ".juggle.pattern",  XrmoptionSepArg, NULL  },
198   {"-tail",     ".juggle.tail",     XrmoptionSepArg, NULL  },
199 #ifdef UNI
200   {"-uni",      ".juggle.uni",      XrmoptionNoArg,  "on"  },
201   {"+uni",      ".juggle.uni",      XrmoptionNoArg,  "off" },
202 #endif
203   {"-real",     ".juggle.real",     XrmoptionNoArg,  "on"  },
204   {"+real",     ".juggle.real",     XrmoptionNoArg,  "off" },
205   {"-describe", ".juggle.describe", XrmoptionNoArg,  "on"  },
206   {"+describe", ".juggle.describe", XrmoptionNoArg,  "off" },
207   {"-balls",    ".juggle.balls",    XrmoptionNoArg,  "on"  },
208   {"+balls",    ".juggle.balls",    XrmoptionNoArg,  "off" },
209   {"-clubs",    ".juggle.clubs",    XrmoptionNoArg,  "on"  },
210   {"+clubs",    ".juggle.clubs",    XrmoptionNoArg,  "off" },
211   {"-torches",  ".juggle.torches",  XrmoptionNoArg,  "on"  },
212   {"+torches",  ".juggle.torches",  XrmoptionNoArg,  "off" },
213   {"-knives",   ".juggle.knives",   XrmoptionNoArg,  "on"  },
214   {"+knives",   ".juggle.knives",   XrmoptionNoArg,  "off" },
215   {"-rings",    ".juggle.rings",    XrmoptionNoArg,  "on"  },
216   {"+rings",    ".juggle.rings",    XrmoptionNoArg,  "off" },
217   {"-bballs",   ".juggle.bballs",   XrmoptionNoArg,  "on"  },
218   {"+bballs",   ".juggle.bballs",   XrmoptionNoArg,  "off" },
219   {"-only",     ".juggle.only",     XrmoptionSepArg, NULL  },
220 };
221 static argtype vars[] =
222 {
223   { &pattern,  "pattern",  "Pattern",  DEF_PATTERN,  t_String },
224   { &tail,     "tail",     "Tail",     DEF_TAIL,     t_Int    },
225 #ifdef UNI
226   { &uni,      "uni",      "Uni",      DEF_UNI,      t_Bool   },
227 #endif
228   { &real,     "real",     "Real",     DEF_REAL,     t_Bool   },
229   { &describe, "describe", "Describe", DEF_DESCRIBE, t_Bool   },
230   { &balls,    "balls",    "Clubs",    DEF_BALLS,    t_Bool   },
231   { &clubs,    "clubs",    "Clubs",    DEF_CLUBS,    t_Bool   },
232   { &torches,  "torches",  "Torches",  DEF_TORCHES,  t_Bool   },
233   { &knives,   "knives",   "Knives",   DEF_KNIVES,   t_Bool   },
234   { &rings,    "rings",    "Rings",    DEF_RINGS,    t_Bool   },
235   { &bballs,   "bballs",   "BBalls",   DEF_BBALLS,   t_Bool   },
236   { &only,     "only",     "BBalls",   " ",          t_String },
237 };
238 static OptionStruct desc[] =
239 {
240   { "-pattern string", "Cambridge Juggling Pattern" },
241   { "-tail num",       "Trace Juggling Patterns" },
242 #ifdef UNI
243   { "-/+uni",          "Unicycle" },
244 #endif
245   { "-/+real",         "Real-time" },
246   { "-/+describe",     "turn on/off pattern descriptions." },
247   { "-/+balls",        "turn on/off Balls." },
248   { "-/+clubs",        "turn on/off Clubs." },
249   { "-/+torches",      "turn on/off Flaming Torches." },
250   { "-/+knives",       "turn on/off Knives." },
251   { "-/+rings",        "turn on/off Rings." },
252   { "-/+bballs",       "turn on/off Bowling Balls." },
253   { "-only",           "Turn off all objects but the named one." },
254 };
255
256 ENTRYPOINT ModeSpecOpt juggle_opts =
257   { XtNumber(opts), opts, XtNumber(vars), vars, desc };
258
259 #ifdef USE_MODULES
260 ModStruct   juggle_description = {
261         "juggle", "init_juggle", "draw_juggle", (char *) NULL,
262         "draw_juggle", "change_juggle", "free_juggle", &juggle_opts,
263         10000, 200, 1000, 1, 64, 1.0, "",
264         "Shows a Juggler, juggling", 0, NULL
265 };
266
267 #endif
268
269 #ifdef USE_XVMSUTILS
270 # include <X11/unix_time.h>
271 #endif
272
273 /* Note: All "lengths" are scaled by sp->scale = MI_HEIGHT/480.  All
274    "thicknesses" are scaled by sqrt(sp->scale) so that they are
275    proportionally thicker for smaller windows.  Objects spinning out
276    of the plane (such as clubs) fake perspective by compressing their
277    horizontal coordinates by PERSPEC  */
278
279 /* Figure */
280 #define ARMLENGTH 50
281 #define ARMWIDTH ((int) (8.0 * sqrt(sp->scale)))
282 #define POSE 10
283 #define BALLRADIUS ARMWIDTH
284
285 #define PERSPEC  0.4
286
287 /* macros */
288 #define GRAVITY(h, t) 4*(double)(h)/((t)*(t))
289
290 /* Timing based on count.  Units are milliseconds.  Juggles per second
291        is: 2000 / THROW_CATCH_INTERVAL + CATCH_THROW_INTERVAL */
292
293 #define THROW_CATCH_INTERVAL (sp->count)
294 #define THROW_NULL_INTERVAL  (sp->count * 0.5)
295 #define CATCH_THROW_INTERVAL (sp->count * 0.2)
296
297 /********************************************************************
298  * Trace Definitions                                                *
299  *                                                                  *
300  * These record rendering data so that a drawn object can be erased *
301  * later.  Each object has its own Trace list.                      *
302  *                                                                  *
303  ********************************************************************/
304
305 typedef struct {double x, y; } DXPoint;
306 typedef struct trace *TracePtr;
307 typedef struct trace {
308   TracePtr next, prev;
309   double x, y;
310   double angle;
311   int divisions;
312   DXPoint dlast;
313 #ifdef MEMTEST
314   char pad[1024];
315 #endif
316 } Trace;
317
318 /*******************************************************************
319  * Object Definitions                                              *
320  *                                                                 *
321  * These describe the various types of Object that can be juggled  *
322  *                                                                 *
323  *******************************************************************/
324 typedef void (DrawProc)(ModeInfo*, unsigned long, Trace *);
325
326 static DrawProc show_ball, show_europeanclub, show_torch, show_knife;
327 static DrawProc show_ring, show_bball;
328
329 typedef enum {BALL, CLUB, TORCH, KNIFE, RING, BBALLS,
330                           NUM_OBJECT_TYPES} ObjType;
331 #define OBJMIXPROB 20   /* inverse of the chances of using an odd
332                                                    object in the pattern */
333
334 static const struct {
335   DrawProc *draw;                           /* Object Rendering function */
336   int       handle;                         /* Length of object's handle */
337   int       mintrail;                            /* Minimum trail length */
338   double    cor;      /* Coefficient of Restitution.  perfect bounce = 1 */
339   double    weight;          /* Heavier objects don't get thrown as high */
340 } ObjectDefs[] = {
341   { /* Ball */
342         show_ball,
343         0,
344         1,
345         0.9,
346         1.0,
347   },
348   { /* Club */
349         show_europeanclub,
350         15,
351         1,
352         0.55, /* Clubs don't bounce too well */
353         1.0,
354   },
355   { /* Torch */
356         show_torch,
357         15,
358         20, /* Torches need flames */
359         0, /* Torches don't bounce -- fire risk! */
360         1.0,
361   },
362   { /* Knife */
363         show_knife,
364         15,
365         1,
366         0, /* Knives don't bounce */
367         1.0,
368   },
369   { /* Ring */
370         show_ring,
371         15,
372         1,
373         0.8,
374         1.0,
375   },
376   { /* Bowling Ball */
377         show_bball,
378         0,
379         1,
380         0.2,
381         5.0,
382   },
383 };
384
385 /**************************
386  * Trajectory definitions *
387  **************************/
388
389 typedef enum {HEIGHT, ADAM} Notation;
390 typedef enum {Empty, Full, Ball} Throwable;
391 typedef enum {LEFT, RIGHT} Hand;
392 typedef enum {THROW, CATCH} Action;
393 typedef enum {HAND, ELBOW, SHOULDER} Joint;
394 typedef enum {ATCH, THRATCH, ACTION, LINKEDACTION,
395                           PTHRATCH, BPREDICTOR, PREDICTOR} TrajectoryStatus;
396 typedef struct {double a, b, c, d; } Spline;
397 typedef DXPoint Arm[3];
398
399 /* A Wander contains a Spline and a time interval.  A list of Wanders
400  * describes the performer's position as he moves around the screen.  */
401 typedef struct wander *WanderPtr;
402 typedef struct wander {
403   WanderPtr next, prev;
404   double x;
405   unsigned long finish;
406   Spline s;
407 #ifdef MEMTEST
408   char pad[1024];
409 #endif
410 } Wander;
411
412 /* Object is an arbitrary object being juggled.  Each Trajectory
413  * references an Object ("count" tracks this), and each Object is also
414  * linked into a global Objects list.  Objects may include a Trace
415  * list for tracking erasures. */
416 typedef struct object *ObjectPtr;
417 typedef struct object {
418   ObjectPtr next, prev;
419
420   ObjType type;
421   int     color;
422   int     count; /* reference count */
423   Bool    active; /* Object is in use */
424
425   Trace  *trace;
426   int     tracelen;
427   int     tail;
428 #ifdef MEMTEST
429   char pad[1024];
430 #endif
431 } Object;
432
433 /* Trajectory is a segment of juggling action.  A list of Trajectories
434  * defines the juggling performance.  The Trajectory list goes through
435  * multiple processing steps to convert it from basic juggling
436  * notation into rendering data. */
437
438 typedef struct trajectory *TrajectoryPtr;
439 typedef struct trajectory {
440   TrajectoryPtr prev, next;  /* for building list */
441   TrajectoryStatus status;
442
443   /* Throw */
444   char posn;
445   int height;
446   int adam;
447   char *pattern;
448   char *name;
449
450   /* Action */
451   Hand hand;
452   Action action;
453
454   /* LinkedAction */
455   int color;
456   Object *object;
457   int divisions;
458   double angle, spin;
459   TrajectoryPtr balllink;
460   TrajectoryPtr handlink;
461
462   /* PThratch */
463   double cx; /* Moving juggler */
464   double x, y; /* current position */
465   double dx, dy; /* initial velocity */
466
467   /* Predictor */
468   Throwable type;
469   unsigned long start, finish;
470   Spline xp, yp;
471
472 #ifdef MEMTEST
473   char pad[1024];
474 #endif
475 } Trajectory;
476
477
478 /*******************
479  * Pattern Library *
480  *******************/
481
482 typedef struct {
483   const char * pattern;
484   const char * name;
485 } patternstruct;
486
487 /* List of popular patterns, in any order */
488 /* Patterns should be given in Adam notation so the generator can
489    concatenate them safely.  Null descriptions are ok.  Height
490    notation will be displayed automatically.  */
491 /* Can't const this because it is qsorted.  This *should* be reentrant,
492    I think... */
493 static /*const*/ patternstruct portfolio[] = {
494   {"[+2 1]", /* +3 1 */ "Typical 2 ball juggler"},
495   {"[2 0]", /* 4 0 */ "2 in 1 hand"},
496   {"[2 0 1]", /* 5 0 1 */},
497   {"[+2 0 +2 0 0]" /* +5 0 +5 0 0 */},
498   {"[+2 0 1 2 2]", /* +4 0 1 2 3 */},
499   {"[2 0 1 1]", /* 6 0 1 1 */},
500
501   {"[3]", /* 3 */ "3 cascade"},
502   {"[+3]", /* +3 */ "reverse 3 cascade"},
503   {"[=3]", /* =3 */ "cascade 3 under arm"},
504   {"[&3]", /* &3 */ "cascade 3 catching under arm"},
505   {"[_3]", /* _3 */ "bouncing 3 cascade"},
506   {"[+3 x3 =3]", /* +3 x3 =3 */ "Mill's mess"},
507   {"[3 2 1]", /* 5 3 1" */},
508   {"[3 3 1]", /* 4 4 1" */},
509   {"[3 1 2]", /* 6 1 2 */ "See-saw"},
510   {"[=3 3 1 2]", /* =4 5 1 2 */},
511   {"[=3 2 2 3 1 2]", /* =6 2 2 5 1 2 */ "=4 5 1 2 stretched"},
512   {"[+3 3 1 3]", /* +4 4 1 3 */ "anemic shower box"},
513   {"[3 3 1]", /* 4 4 1 */},
514   {"[+3 2 3]", /* +4 2 3 */},
515   {"[+3 1]", /* +5 1 */ "3 shower"},
516   {"[_3 1]", /* _5 1 */ "bouncing 3 shower"},
517   {"[3 0 3 0 3]", /* 5 0 5 0 5 */ "shake 3 out of 5"},
518   {"[3 3 3 0 0]", /* 5 5 5 0 0 */ "flash 3 out of 5"},
519   {"[3 3 0]", /* 4 5 0 */ "complete waste of a 5 ball juggler"},
520   {"[3 3 3 0 0 0 0]", /* 7 7 7 0 0 0 0 */ "3 flash"},
521   {"[+3 0 +3 0 +3 0 0]", /* +7 0 +7 0 +7 0 0 */},
522   {"[3 2 2 0 3 2 0 2 3 0 2 2 0]", /* 7 3 3 0 7 3 0 3 7 0 3 3 0 */},
523   {"[3 0 2 0]", /* 8 0 4 0 */},
524   {"[_3 2 1]", /* _5 3 1 */},
525   {"[_3 0 1]", /* _8 0 1 */},
526   {"[1 _3 1 _3 0 1 _3 0]", /* 1 _7 1 _7 0 1 _7 0 */},
527   {"[_3 2 1 _3 1 2 1]", /* _6 3 1 _6 1 3 1 */},
528
529   {"[4]", /* 4 */ "4 cascade"},
530   {"[+4 3]", /* +5 3 */ "4 ball half shower"},
531   {"[4 4 2]", /* 5 5 2 */},
532   {"[+4 4 4 +4]", /* +4 4 4 +4 */ "4 columns"},
533   {"[+4 3 +4]", /* +5 3 +4 */},
534   {"[4 3 4 4]", /* 5 3 4 4 */},
535   {"[4 3 3 4]", /* 6 3 3 4 */},
536   {"[4 3 2 4", /* 6 4 2 4 */},
537   {"[+4 1]", /* +7 1 */ "4 shower"},
538   {"[4 4 4 4 0]", /* 5 5 5 5 0 */ "learning 5"},
539   {"[+4 x4 =4]", /* +4 x4 =4 */ "Mill's mess for 4"},
540   {"[+4 2 1 3]", /* +9 3 1 3 */},
541   {"[4 4 1 4 1 4]", /* 6 6 1 5 1 5, by Allen Knutson */},
542   {"[_4 _4 _4 1 _4 1]", /* _5 _6 _6 1 _5 1 */},
543   {"[_4 3 3]", /* _6 3 3 */},
544   {"[_4 3 1]", /* _7 4 1 */},
545   {"[_4 2 1]", /* _8 3 1 */},
546   {"[_4 3 3 3 0]", /* _8 4 4 4 0 */},
547   {"[_4 1 3 1]", /* _9 1 5 1 */},
548   {"[_4 1 3 1 2]", /* _10 1 6 1 2 */},
549
550   {"[5]", /* 5 */ "5 cascade"},
551   {"[_5 _5 _5 _5 _5 5 5 5 5 5]", /* _5 _5 _5 _5 _5 5 5 5 5 5 */},
552   {"[+5 x5 =5]", /* +5 x5 =5 */ "Mill's mess for 5"},
553   {"[5 4 4]", /* 7 4 4 */},
554   {"[_5 4 4]", /* _7 4 4 */},
555   {"[1 2 3 4 5 5 5 5 5]", /* 1 2 3 4 5 6 7 8 9 */ "5 ramp"},
556   {"[5 4 5 3 1]", /* 8 5 7 4 1, by Allen Knutson */},
557   {"[_5 4 1 +4]", /* _9 5 1 5 */},
558   {"[_5 4 +4 +4]", /* _8 4 +4 +4 */},
559   {"[_5 4 4 4 1]", /* _9 5 5 5 1 */},
560   {"[_5 4 4 5 1]",},
561   {"[_5 4 4 +4 4 0]", /*_10 5 5 +5 5 0 */},
562
563   {"[6]", /* 6 */ "6 cascade"},
564   {"[+6 5]", /* +7 5 */},
565   {"[6 4]", /* 8 4 */},
566   {"[+6 3]", /* +9 3 */},
567   {"[6 5 4 4]", /* 9 7 4 4 */},
568   {"[+6 5 5 5]", /* +9 5 5 5 */},
569   {"[6 0 6]", /* 9 0 9 */},
570   {"[_6 0 _6]", /* _9 0 _9 */},
571
572   {"[_7]", /* _7 */ "bouncing 7 cascade"},
573   {"[7]", /* 7 */ "7 cascade"},
574   {"[7 6 6 6 6]", /* 11 6 6 6 6 */ "Gatto's High Throw"},
575
576 };
577
578
579
580 typedef struct { int start; int number; } PatternIndex;
581
582 struct patternindex {
583   int minballs;
584   int maxballs;
585   PatternIndex index[XtNumber(portfolio)];
586 };
587
588
589 /* Jugglestruct: per-screen global data.  The master Wander, Object
590  * and Trajectory lists are anchored here. */
591 typedef struct {
592   double        scale;
593   Wander       *wander;
594   double        cx;
595   double        Gr;
596   Trajectory   *head;
597   Arm           arm[2][2];
598   char         *pattern;
599   int           count;
600   int           num_balls;
601   time_t        begintime; /* should make 'time' usable for at least 48 days
602                                                         on a 32-bit machine */
603   unsigned long time; /* millisecond timer*/
604   ObjType       objtypes;
605   Object       *objects;
606   struct patternindex patternindex;
607   XFontStruct *mode_font;
608 } jugglestruct;
609
610 static jugglestruct *juggles = (jugglestruct *) NULL;
611
612 /*******************
613  * list management *
614  *******************/
615
616 #define DUP_OBJECT(n, t) { \
617   (n)->object = (t)->object; \
618   if((n)->object != NULL) (n)->object->count++; \
619 }
620
621 /* t must point to an existing element.  t must not be an
622    expression ending ->next or ->prev */
623 #define REMOVE(t) { \
624   (t)->next->prev = (t)->prev; \
625   (t)->prev->next = (t)->next; \
626   free(t); \
627 }
628
629 /* t receives element to be created and added to the list.  ot must
630    point to an existing element or be identical to t to start a new
631    list. Applicable to Trajectories, Objects and Traces. */
632 #define ADD_ELEMENT(type, t, ot) \
633   if (((t) = (type*)calloc(1,sizeof(type))) != NULL) { \
634     (t)->next = (ot)->next; \
635     (t)->prev = (ot); \
636     (ot)->next = (t); \
637     (t)->next->prev = (t); \
638   }
639
640 static void
641 object_destroy(Object* o)
642 {
643   if(o->trace != NULL) {
644         while(o->trace->next != o->trace) {
645           Trace *s = o->trace->next;
646           REMOVE(s); /* Don't eliminate 's' */
647         }
648         free(o->trace);
649   }
650   REMOVE(o);
651 }
652
653 static void
654 trajectory_destroy(Trajectory *t) {
655   if(t->name != NULL) free(t->name);
656   if(t->pattern != NULL) free(t->pattern);
657   /* Reduce object link count and call destructor if necessary */
658   if(t->object != NULL && --t->object->count < 1 && t->object->tracelen == 0) {
659         object_destroy(t->object);
660   }
661   REMOVE(t); /* Unlink and free */
662 }
663
664 ENTRYPOINT void
665 free_juggle(ModeInfo * mi) {
666   jugglestruct *sp = &juggles[MI_SCREEN(mi)];
667
668   if (sp->head != NULL) {
669         while (sp->head->next != sp->head) {
670           trajectory_destroy(sp->head->next);
671         }
672         free(sp->head);
673         sp->head = (Trajectory *) NULL;
674   }
675   if(sp->objects != NULL) {
676         while (sp->objects->next != sp->objects) {
677           object_destroy(sp->objects->next);
678         }
679         free(sp->objects);
680         sp->objects = (Object*)NULL;
681   }
682   if(sp->wander != NULL) {
683         while (sp->wander->next != sp->wander) {
684           Wander *w = sp->wander->next;
685           REMOVE(w);
686         }
687         free(sp->wander);
688         sp->wander = (Wander*)NULL;
689   }
690   if(sp->pattern != NULL) {
691         free(sp->pattern);
692         sp->pattern = NULL;
693   }
694   if (sp->mode_font!=None) {
695         XFreeFontInfo(NULL,sp->mode_font,1);
696         sp->mode_font = None;
697   }
698 }
699
700 static Bool
701 add_throw(ModeInfo *mi, char type, int h, Notation n, const char* name)
702 {
703   jugglestruct *sp = &juggles[MI_SCREEN(mi)];
704   Trajectory *t;
705
706   ADD_ELEMENT(Trajectory, t, sp->head->prev);
707   if(t == NULL){ /* Out of Memory */
708         free_juggle(mi);
709         return False;
710   }
711   t->object = NULL;
712   if(name != NULL)
713         t->name = strdup(name);
714   t->posn = type;
715   if (n == ADAM) {
716         t->adam = h;
717         t->height = 0;
718         t->status = ATCH;
719   } else {
720         t->height = h;
721         t->status = THRATCH;
722   }
723   return True;
724 }
725
726 /* add a Thratch to the performance */
727 static Bool
728 program(ModeInfo *mi, const char *patn, const char *name, int cycles)
729 {
730   const char *p;
731   int w, h, i, seen;
732   Notation notation;
733   char type;
734
735   if (MI_IS_VERBOSE(mi)) {
736         (void) fprintf(stderr, "juggle[%d]: Programmed: %s x %d\n",
737                                    MI_SCREEN(mi), (name == NULL) ? patn : name, cycles);
738   }
739
740   for(w=i=0; i < cycles; i++, w++) { /* repeat until at least "cycles" throws
741                                                                                 have been programmed */
742         /* title is the pattern name to be supplied to the first throw of
743            a sequence.  If no name if given, use an empty title so that
744            the sequences are still delimited. */
745         const char *title = (name != NULL)? name : "";
746         type=' ';
747         h = 0;
748         seen = 0;
749         notation = HEIGHT;
750         for(p=patn; *p; p++) {
751           if (*p >= '0' && *p <='9') {
752                 seen = 1;
753                 h = 10*h + (*p - '0');
754           } else {
755                 Notation nn = notation;
756                 switch (*p) {
757                 case '[':            /* begin Adam notation */
758                   notation = ADAM;
759                   break;
760                 case '-':            /* Inside throw */
761                   type = ' ';
762                   break;
763                 case '+':            /* Outside throw */
764                 case '=':            /* Cross throw */
765                 case '&':            /* Cross catch */
766                 case 'x':            /* Cross throw and catch */
767                 case '_':            /* Bounce */
768                 case 'k':            /* Kickup */
769                   type = *p;
770                   break;
771                 case '*':            /* Lose ball */
772                   seen = 1;
773                   h = -1;
774                   /* fall through */
775                 case ']':             /* end Adam notation */
776                   nn = HEIGHT;
777                   /* fall through */
778                 case ' ':
779                   if (seen) {
780                         i++;
781                         if (!add_throw(mi, type, h, notation, title))
782                                 return False;
783                         title = NULL;
784                         type=' ';
785                         h = 0;
786                         seen = 0;
787                   }
788                   notation = nn;
789                   break;
790                 default:
791                   if(w == 0) { /* Only warn on first pass */
792                         (void) fprintf(stderr,
793                                                    "juggle[%d]: Unexpected pattern instruction: '%c'\n",
794                                                    MI_SCREEN(mi), *p);
795                   }
796                   break;
797                 }
798           }
799         }
800         if (seen) { /* end of sequence */
801           if (!add_throw(mi, type, h, notation, title))
802                 return False;
803           title = NULL;
804         }
805   }
806   return True;
807 }
808
809 /*
810  ~~~~\~~~~~\~~~
811  \\~\\~\~\\\~~~
812  \\~\\\\~\\\~\~
813  \\\\\\\\\\\~\\
814
815 [ 3 3 1 3 4 2 3 1 3 3 4 0 2 1 ]
816
817 4 4 1 3 12 2 4 1 4 4 13 0 3 1
818
819 */
820 #define BOUNCEOVER 10
821 #define KICKMIN 7
822 #define THROWMAX 20
823
824 /* Convert Adam notation into heights */
825 static void
826 adam(jugglestruct *sp)
827 {
828   Trajectory *t, *p;
829   for(t = sp->head->next; t != sp->head; t = t->next) {
830         if (t->status == ATCH) {
831           int a = t->adam;
832           t->height = 0;
833           for(p = t->next; a > 0; p = p->next) {
834                 if(p == sp->head) {
835                   t->height = -9; /* Indicate end of processing for name() */
836                   return;
837                 }
838                 if (p->status != ATCH || p->adam < 0 || p->adam>= a) {
839                   a--;
840                 }
841                 t->height++;
842           }
843           if(t->height > BOUNCEOVER && t->posn == ' '){
844                 t->posn = '_'; /* high defaults can be bounced */
845           } else if(t->height < 3 && t->posn == '_') {
846                 t->posn = ' '; /* Can't bounce short throws. */
847           }
848           if(t->height < KICKMIN && t->posn == 'k'){
849                 t->posn = ' '; /* Can't kick short throws */
850           }
851           if(t->height > THROWMAX){
852                 t->posn = 'k'; /* Use kicks for ridiculously high throws */
853           }
854           t->status = THRATCH;
855         }
856   }
857 }
858
859 /* Discover converted heights and update the sequence title */
860 static void
861 name(jugglestruct *sp)
862 {
863   Trajectory *t, *p;
864   char buffer[BUFSIZ];
865   char *b;
866   for(t = sp->head->next; t != sp->head; t = t->next) {
867         if (t->status == THRATCH && t->name != NULL) {
868           b = buffer;
869           for(p = t; p == t || p->name == NULL; p = p->next) {
870                 if(p == sp->head || p->height < 0) { /* end of reliable data */
871                   return;
872                 }
873                 if(p->posn == ' ') {
874                   b += sprintf(b, " %d", p->height);
875                 } else {
876                   b += sprintf(b, " %c%d", p->posn, p->height);
877                 }
878                 if(b - buffer > 500) break; /* otherwise this could eventually
879                                                                            overflow.  It'll be too big to
880                                                                            display anyway. */
881           }
882           if(*t->name != 0) {
883                 (void) sprintf(b, ", %s", t->name);
884           }
885           free(t->name); /* Don't need name any more, it's been converted
886                                                 to pattern */
887           t->name = NULL;
888           if(t->pattern != NULL) free(t->pattern);
889           t->pattern = strdup(buffer);
890         }
891   }
892 }
893
894 /* Split Thratch notation into explicit throws and catches.
895    Usually Catch follows Throw in same hand, but take care of special
896    cases. */
897
898 /* ..n1.. -> .. LTn RT1 LC RC .. */
899 /* ..nm.. -> .. LTn LC RTm RC .. */
900
901 static Bool
902 part(ModeInfo *mi)
903 {
904   jugglestruct *sp = &juggles[MI_SCREEN(mi)];
905   Trajectory *t, *nt, *p;
906   Hand hand = (LRAND() & 1) ? RIGHT : LEFT;
907
908   for (t = sp->head->next; t != sp->head; t = t->next) {
909         if (t->status > THRATCH) {
910           hand = t->hand;
911         } else if (t->status == THRATCH) {
912           char posn = '=';
913
914           /* plausibility check */
915           if (t->height <= 2 && t->posn == '_') {
916                 t->posn = ' '; /* no short bounces */
917           }
918           if (t->height <= 1 && (t->posn == '=' || t->posn == '&')) {
919                 t->posn = ' '; /* 1's need close catches */
920           }
921
922           switch (t->posn) {
923                   /*         throw          catch    */
924           case ' ': posn = '-'; t->posn = '+'; break;
925           case '+': posn = '+'; t->posn = '-'; break;
926           case '=': posn = '='; t->posn = '+'; break;
927           case '&': posn = '+'; t->posn = '='; break;
928           case 'x': posn = '='; t->posn = '='; break;
929           case '_': posn = '_'; t->posn = '-'; break;
930           case 'k': posn = 'k'; t->posn = 'k'; break;
931           default:
932                 (void) fprintf(stderr, "juggle: unexpected posn %c\n", t->posn);
933                 break;
934           }
935           hand = (Hand) ((hand + 1) % 2);
936           t->status = ACTION;
937           t->hand = hand;
938           p = t->prev;
939
940           if (t->height == 1 && p != sp->head) {
941                 p = p->prev; /* '1's are thrown earlier than usual */
942           }
943
944
945
946           t->action = CATCH;
947           ADD_ELEMENT(Trajectory, nt, p);
948           if(nt == NULL){
949                 free_juggle(mi);
950                 return False;
951           }
952           nt->object = NULL;
953           nt->status = ACTION;
954           nt->action = THROW;
955           nt->height = t->height;
956           nt->hand = hand;
957           nt->posn = posn;
958
959         }
960   }
961   return True;
962 }
963
964 static ObjType
965 choose_object(void) {
966   ObjType o;
967   for (;;) {
968         o = (ObjType)NRAND((ObjType)NUM_OBJECT_TYPES);
969         if(balls && o == BALL) break;
970         if(clubs && o == CLUB) break;
971         if(torches && o == TORCH) break;
972         if(knives && o == KNIFE) break;
973         if(rings && o == RING) break;
974         if(bballs && o == BBALLS) break;
975   }
976   return o;
977 }
978
979 /* Connnect up throws and catches to figure out which ball goes where.
980    Do the same with the juggler's hands. */
981
982 static void
983 lob(ModeInfo *mi)
984 {
985   jugglestruct *sp = &juggles[MI_SCREEN(mi)];
986   Trajectory *t, *p;
987   int h;
988   for (t = sp->head->next; t != sp->head; t = t->next) {
989         if (t->status == ACTION) {
990           if (t->action == THROW) {
991                 if (t->type == Empty) {
992                   /* Create new Object */
993                   ADD_ELEMENT(Object, t->object, sp->objects);
994                   t->object->count = 1;
995                   t->object->tracelen = 0;
996                   t->object->active = False;
997                   /* Initialise object's circular trace list */
998                   ADD_ELEMENT(Trace, t->object->trace, t->object->trace);
999
1000                   if (MI_NPIXELS(mi) > 2) {
1001                         t->object->color = 1 + NRAND(MI_NPIXELS(mi) - 2);
1002                   } else {
1003 #ifdef STANDALONE
1004                         t->object->color = 1;
1005 #else
1006                         t->object->color = 0;
1007 #endif
1008                   }
1009
1010                   /* Small chance of picking a random object instead of the
1011                          current theme. */
1012                   if(NRAND(OBJMIXPROB) == 0) {
1013                         t->object->type = choose_object();
1014                   } else {
1015                         t->object->type = sp->objtypes;
1016                   }
1017
1018                   /* Check to see if we need trails for this object */
1019                   if(tail < ObjectDefs[t->object->type].mintrail) {
1020                         t->object->tail = ObjectDefs[t->object->type].mintrail;
1021                   } else {
1022                         t->object->tail = tail;
1023                   }
1024                 }
1025
1026                 /* Balls can change divisions at each throw */
1027                 t->divisions = 2 * (NRAND(2) + 1);
1028
1029                 /* search forward for next catch in this hand */
1030                 for (p = t->next; t->handlink == NULL; p = p->next) {
1031                   if(p->status < ACTION || p == sp->head) return;
1032                   if (p->action == CATCH) {
1033                         if (t->handlink == NULL && p->hand == t->hand) {
1034                           t->handlink = p;
1035                         }
1036                   }
1037                 }
1038
1039                 if (t->height > 0) {
1040                   h = t->height - 1;
1041
1042                   /* search forward for next ball catch */
1043                   for (p = t->next; t->balllink == NULL; p = p->next) {
1044                         if(p->status < ACTION || p == sp->head) {
1045                           t->handlink = NULL;
1046                           return;
1047                         }
1048                         if (p->action == CATCH) {
1049                           if (t->balllink == NULL && --h < 1) { /* caught */
1050                                 t->balllink = p; /* complete trajectory */
1051 # if 0
1052                                 if (p->type == Full) {
1053                                   (void) fprintf(stderr, "juggle[%d]: Dropped %d\n",
1054                                                   MI_SCREEN(mi), t->object->color);
1055                                 }
1056 #endif
1057                                 p->type = Full;
1058                                 DUP_OBJECT(p, t); /* accept catch */
1059                                 p->angle = t->angle;
1060                                 p->divisions = t->divisions;
1061                           }
1062                         }
1063                   }
1064                 }
1065                 t->type = Empty; /* thrown */
1066           } else if (t->action == CATCH) {
1067                 /* search forward for next throw from this hand */
1068                 for (p = t->next; t->handlink == NULL; p = p->next) {
1069                   if(p->status < ACTION || p == sp->head) return;
1070                   if (p->action == THROW && p->hand == t->hand) {
1071                         p->type = t->type; /* pass ball */
1072                         DUP_OBJECT(p, t); /* pass object */
1073                         p->divisions = t->divisions;
1074                         t->handlink = p;
1075                   }
1076                 }
1077           }
1078           t->status = LINKEDACTION;
1079         }
1080   }
1081 }
1082
1083 /* Clap when both hands are empty */
1084 static void
1085 clap(jugglestruct *sp)
1086 {
1087   Trajectory *t, *p;
1088   for (t = sp->head->next; t != sp->head; t = t->next) {
1089         if (t->status == LINKEDACTION &&
1090                 t->action == CATCH &&
1091                 t->type == Empty &&
1092                 t->handlink != NULL &&
1093                 t->handlink->height == 0) { /* Completely idle hand */
1094
1095           for (p = t->next; p != sp->head; p = p->next) {
1096                 if (p->status == LINKEDACTION &&
1097                         p->action == CATCH &&
1098                         p->hand != t->hand) { /* Next catch other hand */
1099                   if(p->type == Empty &&
1100                          p->handlink != NULL &&
1101                          p->handlink->height == 0) { /* Also completely idle */
1102
1103                         t->handlink->posn = '^'; /* Move first hand's empty throw */
1104                         p->posn = '^';           /* to meet second hand's empty
1105                                                                                 catch */
1106
1107                   }
1108                   break; /* Only need first catch */
1109                 }
1110           }
1111         }
1112   }
1113 }
1114
1115 #define CUBIC(s, t) ((((s).a * (t) + (s).b) * (t) + (s).c) * (t) + (s).d)
1116
1117 /* Compute single spline from x0 with velocity dx0 at time t0 to x1
1118    with velocity dx1 at time t1 */
1119 static Spline
1120 makeSpline(double x0, double dx0, int t0, double x1, double dx1, int t1)
1121 {
1122   Spline s;
1123   double a, b, c, d;
1124   double x10;
1125   double t10;
1126
1127   x10 = x1 - x0;
1128   t10 = t1 - t0;
1129   a = ((dx0 + dx1)*t10 - 2*x10) / (t10*t10*t10);
1130   b = (3*x10 - (2*dx0 + dx1)*t10) / (t10*t10);
1131   c = dx0;
1132   d = x0;
1133   s.a = a;
1134   s.b = -3*a*t0 + b;
1135   s.c = (3*a*t0 - 2*b)*t0 + c;
1136   s.d = ((-a*t0 + b)*t0 - c)*t0 +d;
1137   return s;
1138 }
1139
1140 /* Compute a pair of splines.  s1 goes from x0 vith velocity dx0 at
1141    time t0 to x1 at time t1.  s2 goes from x1 at time t1 to x2 with
1142    velocity dx2 at time t2.  The arrival and departure velocities at
1143    x1, t1 must be the same. */
1144 static double
1145 makeSplinePair(Spline *s1, Spline *s2,
1146                            double x0, double dx0, int t0,
1147                            double x1,             int t1,
1148                            double x2, double dx2, int t2)
1149 {
1150   double x10, x21, t21, t10, t20, dx1;
1151   x10 = x1 - x0;
1152   x21 = x2 - x1;
1153   t21 = t2 - t1;
1154   t10 = t1 - t0;
1155   t20 = t2 - t0;
1156   dx1 = (3*x10*t21*t21 + 3*x21*t10*t10 + 3*dx0*t10*t21*t21
1157                  - dx2*t10*t10*t21 - 4*dx0*t10*t21*t21) /
1158         (2*t10*t21*t20);
1159   *s1 = makeSpline(x0, dx0, t0, x1, dx1, t1);
1160   *s2 = makeSpline(x1, dx1, t1, x2, dx2, t2);
1161   return dx1;
1162 }
1163
1164 /* Compute a Ballistic path in a pair of degenerate splines.  sx goes
1165    from x at time t at constant velocity dx.  sy goes from y at time t
1166    with velocity dy and constant acceleration g. */
1167 static void
1168 makeParabola(Trajectory *n,
1169                          double x, double dx, double y, double dy, double g)
1170 {
1171   double t = (double)n->start;
1172   n->xp.a = 0;
1173   n->xp.b = 0;
1174   n->xp.c = dx;
1175   n->xp.d = -dx*t + x;
1176   n->yp.a = 0;
1177   n->yp.b = g/2;
1178   n->yp.c = -g*t + dy;
1179   n->yp.d = g/2*t*t - dy*t + y;
1180 }
1181
1182
1183
1184 /* Make juggler wander around the screen */
1185 static double wander(jugglestruct *sp, unsigned long time)
1186 {
1187   Wander *w = NULL;
1188   for (w = sp->wander->next; w != sp->wander; w = w->next) {
1189         if (w->finish < sp->time) { /* expired */
1190           Wander *ww = w;
1191           w = w->prev;
1192           REMOVE(ww);
1193         } else if(w->finish > time) {
1194           break;
1195         }
1196   }
1197   if(w == sp->wander) { /* Need a new one */
1198         ADD_ELEMENT(Wander, w, sp->wander->prev);
1199         if(w == NULL) { /* Memory problem */
1200           return 0.0;
1201         }
1202         w->finish = time + 3*THROW_CATCH_INTERVAL + NRAND(10*THROW_CATCH_INTERVAL);
1203         if(time == 0) {
1204           w->x = 0;
1205         } else {
1206           w->x = w->prev->x * 0.9 + NRAND(40) - 20;
1207         }
1208         w->s = makeSpline(w->prev->x, 0.0, w->prev->finish, w->x, 0.0, w->finish);
1209   }
1210   return CUBIC(w->s, time);
1211 }
1212
1213 #define SX 25 /* Shoulder Width */
1214
1215 /* Convert hand position symbols into actual time/space coordinates */
1216 static void
1217 positions(jugglestruct *sp)
1218 {
1219   Trajectory *t;
1220   unsigned long now = sp->time; /* Make sure we're not lost in the past */
1221   for (t = sp->head->next; t != sp->head; t = t->next) {
1222         if (t->status >= PTHRATCH) {
1223           now = t->start;
1224         } else if (t->status == ACTION || t->status == LINKEDACTION) {
1225           /* Allow ACTIONs to be annotated, but we won't mark them ready
1226                  for the next stage */
1227
1228           double xo = 0, yo;
1229           double sx = SX;
1230           double pose = SX/2;
1231
1232           /* time */
1233           if (t->action == CATCH) { /* Throw-to-catch */
1234                 if (t->type == Empty) {
1235                   now += (int) THROW_NULL_INTERVAL; /* failed catch is short */
1236                 } else {     /* successful catch */
1237                   now += (int)(THROW_CATCH_INTERVAL);
1238                 }
1239           } else { /* Catch-to-throw */
1240                 if(t->object != NULL) {
1241                   now += (int) (CATCH_THROW_INTERVAL *
1242                                                 ObjectDefs[t->object->type].weight);
1243                 } else {
1244                   now += (int) (CATCH_THROW_INTERVAL);
1245                 }
1246           }
1247
1248           if(t->start == 0)
1249                 t->start = now;
1250           else /* Concatenated performances may need clock resync */
1251                 now = t->start;
1252
1253           t->cx = wander(sp, t->start);
1254
1255           /* space */
1256           yo = 90;
1257
1258           /* Add room for the handle */
1259           if(t->action == CATCH && t->object != NULL)
1260                 yo -= ObjectDefs[t->object->type].handle;
1261
1262           switch (t->posn) {
1263           case '-': xo = sx - pose; break;
1264           case '_':
1265           case 'k':
1266           case '+': xo = sx + pose; break;
1267           case '~':
1268           case '=': xo = - sx - pose; yo += pose; break;
1269           case '^': xo = 0; yo += pose*2; break; /* clap */
1270           default:
1271                 (void) fprintf(stderr, "juggle: unexpected posn %c\n", t->posn);
1272                 break;
1273           }
1274
1275           t->angle = (((t->hand == LEFT) ^
1276                                    (t->posn == '+' || t->posn == '_' || t->posn == 'k' ))?
1277                                         -1 : 1) * M_PI/2;
1278
1279           t->x = t->cx + ((t->hand == LEFT) ? xo : -xo);
1280           t->y = yo;
1281
1282           /* Only mark complete if it was already linked */
1283           if(t->status == LINKEDACTION) {
1284                 t->status = PTHRATCH;
1285           }
1286         }
1287   }
1288 }
1289
1290
1291 /* Private physics functions */
1292
1293 /* Compute the spin-rate for a trajectory.  Different types of throw
1294    (eg, regular thows, bounces, kicks, etc) have different spin
1295    requirements.
1296
1297    type = type of object
1298    h = trajectory of throwing hand (throws), or next throwing hand (catches)
1299    old = earlier spin to consider
1300    dt = time span of this trajectory
1301    height = height of ball throw or 0 if based on old spin
1302    turns = full club turns required during this operation
1303    togo = partial club turns required to match hands
1304 */
1305 static double
1306 spinrate(ObjType type, Trajectory *h, double old, double dt,
1307                  int height, int turns, double togo)
1308 {
1309   const int dir = (h->hand == LEFT) ^ (h->posn == '+')? -1 : 1;
1310
1311   if(ObjectDefs[type].handle != 0) { /* Clubs */
1312         return (dir * turns * 2 * M_PI + togo) / dt;
1313   } else if(height == 0) { /* Balls already spinning */
1314         return old/2;
1315   } else { /* Balls */
1316         return dir * NRAND(height*10)/20/ObjectDefs[type].weight * 2 * M_PI / dt;
1317   }
1318 }
1319
1320
1321 /* compute the angle at the end of a spinning trajectory */
1322 static double
1323 end_spin(Trajectory *t)
1324 {
1325   return t->angle + t->spin * (t->finish - t->start);
1326 }
1327
1328 /* Sets the initial angle of the catch following hand movement t to
1329    the final angle of the throw n.  Also sets the angle of the
1330    subsequent throw to the same angle plus half a turn. */
1331 static void
1332 match_spins_on_catch(Trajectory *t, Trajectory *n)
1333 {
1334   if(ObjectDefs[t->balllink->object->type].handle == 0) {
1335         t->balllink->angle = end_spin(n);
1336         if(t->balllink->handlink != NULL) {
1337           t->balllink->handlink->angle = t->balllink->angle + M_PI;
1338         }
1339   }
1340 }
1341
1342 static double
1343 find_bounce(jugglestruct *sp,
1344                         double yo, double yf, double yc, double tc, double cor)
1345 {
1346   double tb, i, dy = 0;
1347   const double e = 1; /* permissible error in yc */
1348
1349   /*
1350         tb = time to bounce
1351         yt = height at catch time after one bounce
1352         one or three roots according to timing
1353         find one by interval bisection
1354   */
1355   tb = tc;
1356   for(i = tc / 2; i > 0.0001; i/=2){
1357         double dt, yt;
1358         if(tb == 0){
1359           (void) fprintf(stderr, "juggle: bounce div by zero!\n");
1360           break;
1361         }
1362         dy = (yf - yo)/tb + sp->Gr/2*tb;
1363         dt = tc - tb;
1364         yt = -cor*dy*dt + sp->Gr/2*dt*dt + yf;
1365         if(yt < yc + e){
1366           tb-=i;
1367         }else if(yt > yc - e){
1368           tb+=i;
1369         }else{
1370           break;
1371         }
1372   }
1373   if(dy*THROW_CATCH_INTERVAL < -200) { /* bounce too hard */
1374         tb = -1;
1375   }
1376   return tb;
1377 }
1378
1379 static Trajectory*
1380 new_predictor(const Trajectory *t, int start, int finish, double angle)
1381 {
1382   Trajectory *n;
1383   ADD_ELEMENT(Trajectory, n, t->prev);
1384   if(n == NULL){
1385         return NULL;
1386   }
1387   DUP_OBJECT(n, t);
1388   n->divisions = t->divisions;
1389   n->type = Ball;
1390   n->status = PREDICTOR;
1391
1392   n->start = start;
1393   n->finish = finish;
1394   n->angle = angle;
1395   return n;
1396 }
1397
1398 /* Turn abstract timings into physically appropriate object trajectories. */
1399 static Bool
1400 projectile(jugglestruct *sp)
1401 {
1402   Trajectory *t;
1403   const int yf = 0; /* Floor height */
1404
1405   for (t = sp->head->next; t != sp->head; t = t->next) {
1406         if (t->status != PTHRATCH || t->action != THROW) {
1407           continue;
1408         } else if (t->balllink == NULL) { /* Zero Throw */
1409           t->status = BPREDICTOR;
1410         } else if (t->balllink->handlink == NULL) { /* Incomplete */
1411           return True;
1412         } else if(t->balllink == t->handlink) {
1413           /* '2' height - hold on to ball.  Don't need to consider
1414                  flourishes, 'hands' will do that automatically anyway */
1415
1416           t->type = Full;
1417           /* Zero spin to avoid wrist injuries */
1418           t->spin = 0;
1419           match_spins_on_catch(t, t);
1420           t->dx = t->dy = 0;
1421           t->status = BPREDICTOR;
1422           continue;
1423         } else {
1424           if (t->posn == '_') { /* Bounce once */
1425
1426                 const int tb = t->start +
1427                   find_bounce(sp, t->y, (double) yf, t->balllink->y,
1428                                           (double) (t->balllink->start - t->start),
1429                                           ObjectDefs[t->object->type].cor);
1430
1431                 if(tb < t->start) { /* bounce too hard */
1432                   t->posn = '+'; /* Use regular throw */
1433                 } else {
1434                   Trajectory *n; /* First (throw) trajectory. */
1435                   double dt; /* Time span of a trajectory */
1436                   double dy; /* Distance span of a follow-on trajectory.
1437                                                 First trajectory uses t->dy */
1438                   /* dx is constant across both trajectories */
1439                   t->dx = (t->balllink->x - t->x) / (t->balllink->start - t->start);
1440
1441                   { /* ball follows parabola down */
1442                         n = new_predictor(t, t->start, tb, t->angle);
1443                         if(n == NULL) return False;
1444                         dt = n->finish - n->start;
1445                         /* Ball rate 4, no flight or matching club turns */
1446                         n->spin = spinrate(t->object->type, t, 0.0, dt, 4, 0, 0.0);
1447                         t->dy = (yf - t->y)/dt - sp->Gr/2*dt;
1448                         makeParabola(n, t->x, t->dx, t->y, t->dy, sp->Gr);
1449                   }
1450
1451                   { /* ball follows parabola up */
1452                         Trajectory *m = new_predictor(t, n->finish, t->balllink->start,
1453                                                                                   end_spin(n));
1454                         if(m == NULL) return False;
1455                         dt = m->finish - m->start;
1456                         /* Use previous ball rate, no flight club turns */
1457                         m->spin = spinrate(t->object->type, t, n->spin, dt, 0, 0,
1458                                                            t->balllink->angle - m->angle);
1459                         match_spins_on_catch(t, m);
1460                         dy = (t->balllink->y - yf)/dt - sp->Gr/2 * dt;
1461                         makeParabola(m, t->balllink->x - t->dx * dt,
1462                                                  t->dx, (double) yf, dy, sp->Gr);
1463                   }
1464
1465                   t->status = BPREDICTOR;
1466                   continue;
1467                 }
1468           } else if (t->posn == 'k') { /* Drop & Kick */
1469                 Trajectory *n; /* First (drop) trajectory. */
1470                 Trajectory *o; /* Second (rest) trajectory */
1471                 Trajectory *m; /* Third (kick) trajectory */
1472                 const int td = t->start + 2*THROW_CATCH_INTERVAL; /* Drop time */
1473                 const int tk = t->balllink->start - 5*THROW_CATCH_INTERVAL; /* Kick */
1474                 double dt, dy;
1475
1476                 { /* Fall to ground */
1477                   n = new_predictor(t, t->start, td, t->angle);
1478                   if(n == NULL) return False;
1479                   dt = n->finish - n->start;
1480                   /* Ball spin rate 4, no flight club turns */
1481                   n->spin = spinrate(t->object->type, t, 0.0, dt, 4, 0,
1482                                                          t->balllink->angle - n->angle);
1483                   t->dx = (t->balllink->x - t->x) / dt;
1484                   t->dy = (yf - t->y)/dt - sp->Gr/2*dt;
1485                   makeParabola(n, t->x, t->dx, t->y, t->dy, sp->Gr);
1486                 }
1487
1488                 { /* Rest on ground */
1489                   o = new_predictor(t, n->finish, tk, end_spin(n));
1490                   if(o == NULL) return False;
1491                   o->spin = 0;
1492                   makeParabola(o, t->balllink->x, 0.0, (double) yf, 0.0, 0.0);
1493                 }
1494
1495                 /* Kick up */
1496                 {
1497                   m = new_predictor(t, o->finish, t->balllink->start, end_spin(o));
1498                   if(m == NULL) return False;
1499                   dt = m->finish - m->start;
1500                   /* Match receiving hand, ball rate 4, one flight club turn */
1501                   m->spin = spinrate(t->object->type, t->balllink->handlink, 0.0, dt,
1502                                                          4, 1, t->balllink->angle - m->angle);
1503                   match_spins_on_catch(t, m);
1504                   dy = (t->balllink->y - yf)/dt - sp->Gr/2 * dt;
1505                   makeParabola(m, t->balllink->x, 0.0, (double) yf, dy, sp->Gr);
1506                 }
1507
1508                 t->status = BPREDICTOR;
1509                 continue;
1510           }
1511
1512           /* Regular flight, no bounce */
1513           { /* ball follows parabola */
1514                 double dt;
1515                 Trajectory *n = new_predictor(t, t->start,
1516                                                                           t->balllink->start, t->angle);
1517                 if(n == NULL) return False;
1518                 dt = t->balllink->start - t->start;
1519                 /* Regular spin */
1520                 n->spin = spinrate(t->object->type, t, 0.0, dt, t->height, t->height/2,
1521                                                    t->balllink->angle - n->angle);
1522                 match_spins_on_catch(t, n);
1523                 t->dx = (t->balllink->x - t->x) / dt;
1524                 t->dy = (t->balllink->y - t->y) / dt - sp->Gr/2 * dt;
1525                 makeParabola(n, t->x, t->dx, t->y, t->dy, sp->Gr);
1526           }
1527
1528           t->status = BPREDICTOR;
1529         }
1530   }
1531   return True;
1532 }
1533
1534 /* Turn abstract hand motions into cubic splines. */
1535 static void
1536 hands(jugglestruct *sp)
1537 {
1538   Trajectory *t, *u, *v;
1539
1540   for (t = sp->head->next; t != sp->head; t = t->next) {
1541         /* no throw => no velocity */
1542         if (t->status != BPREDICTOR) {
1543           continue;
1544         }
1545
1546         u = t->handlink;
1547         if (u == NULL) { /* no next catch */
1548           continue;
1549         }
1550         v = u->handlink;
1551         if (v == NULL) { /* no next throw */
1552           continue;
1553         }
1554
1555         /* double spline takes hand from throw, thru catch, to
1556            next throw */
1557
1558         t->finish = u->start;
1559         t->status = PREDICTOR;
1560
1561         u->finish = v->start;
1562         u->status = PREDICTOR;
1563
1564
1565         /* FIXME: These adjustments leave a small glitch when alternating
1566            balls and clubs.  Just hope no-one notices.  :-) */
1567
1568         /* make sure empty hand spin matches the thrown object in case it
1569            had a handle */
1570
1571         t->spin = ((t->hand == LEFT)? -1 : 1 ) *
1572           fabs((u->angle - t->angle)/(u->start - t->start));
1573
1574         u->spin = ((v->hand == LEFT) ^ (v->posn == '+')? -1 : 1 ) *
1575           fabs((v->angle - u->angle)/(v->start - u->start));
1576
1577         (void) makeSplinePair(&t->xp, &u->xp,
1578                                                   t->x, t->dx, t->start,
1579                                                   u->x, u->start,
1580                                                   v->x, v->dx, v->start);
1581         (void) makeSplinePair(&t->yp, &u->yp,
1582                                                   t->y, t->dy, t->start,
1583                                                   u->y, u->start,
1584                                                   v->y, v->dy, v->start);
1585
1586         t->status = PREDICTOR;
1587   }
1588 }
1589
1590 /* Given target x, y find_elbow puts hand at target if possible,
1591  * otherwise makes hand point to the target */
1592 static void
1593 find_elbow(int armlength, DXPoint *h, DXPoint *e, DXPoint *p, DXPoint *s,
1594                    int z)
1595 {
1596   double r, h2, t;
1597   double x = p->x - s->x;
1598   double y = p->y - s->y;
1599   h2 = x*x + y*y + z*z;
1600   if (h2 > 4 * armlength * armlength) {
1601         t = armlength/sqrt(h2);
1602         e->x = t*x + s->x;
1603         e->y = t*y + s->y;
1604         h->x = 2 * t * x + s->x;
1605         h->y = 2 * t * y + s->y;
1606   } else {
1607         r = sqrt((double)(x*x + z*z));
1608         t = sqrt(4 * armlength * armlength / h2 - 1);
1609         e->x = x*(1 + y*t/r)/2 + s->x;
1610         e->y = (y - r*t)/2 + s->y;
1611         h->x = x + s->x;
1612         h->y = y + s->y;
1613   }
1614 }
1615
1616
1617 /* NOTE: returned x, y adjusted for arm reach */
1618 static void
1619 reach_arm(ModeInfo * mi, Hand side, DXPoint *p)
1620 {
1621   jugglestruct *sp = &juggles[MI_SCREEN(mi)];
1622   DXPoint h, e;
1623   find_elbow(40, &h, &e, p, &sp->arm[1][side][SHOULDER], 25);
1624   *p = sp->arm[1][side][HAND] = h;
1625   sp->arm[1][side][ELBOW] = e;
1626 }
1627
1628 #if DEBUG
1629 /* dumps a human-readable rendition of the current state of the juggle
1630    pipeline to stderr for debugging */
1631 static void
1632 dump(jugglestruct *sp)
1633 {
1634   Trajectory *t;
1635   for (t = sp->head->next; t != sp->head; t = t->next) {
1636         switch (t->status) {
1637         case ATCH:
1638           (void) fprintf(stderr, "%p a %c%d\n", (void*)t, t->posn, t->adam);
1639           break;
1640         case THRATCH:
1641           (void) fprintf(stderr, "%p T %c%d %s\n", (void*)t, t->posn, t->height,
1642                                          t->pattern == NULL?"":t->pattern);
1643           break;
1644         case ACTION:
1645           if (t->action == CATCH)
1646             (void) fprintf(stderr, "%p A %c%cC\n",
1647                                          (void*)t, t->posn,
1648                                          t->hand ? 'R' : 'L');
1649           else
1650             (void) fprintf(stderr, "%p A %c%c%c%d\n",
1651                                          (void*)t, t->posn,
1652                                          t->hand ? 'R' : 'L',
1653                                          (t->action == THROW)?'T':'N',
1654                                          t->height);
1655           break;
1656         case LINKEDACTION:
1657           (void) fprintf(stderr, "%p L %c%c%c%d %d %p %p\n",
1658                                          (void*)t, t->posn,
1659                                          t->hand?'R':'L',
1660                                          (t->action == THROW)?'T':(t->action == CATCH?'C':'N'),
1661                                          t->height, t->object == NULL?0:t->object->color,
1662                                          (void*)t->handlink, (void*)t->balllink);
1663           break;
1664         case PTHRATCH:
1665           (void) fprintf(stderr, "%p O %c%c%c%d %d %2d %6lu %6lu\n",
1666                                          (void*)t, t->posn,
1667                                          t->hand?'R':'L',
1668                                          (t->action == THROW)?'T':(t->action == CATCH?'C':'N'),
1669                                          t->height, t->type, t->object == NULL?0:t->object->color,
1670                                          t->start, t->finish);
1671           break;
1672         case BPREDICTOR:
1673           (void) fprintf(stderr, "%p B %c      %2d %6lu %6lu %g\n",
1674                                          (void*)t, t->type == Ball?'b':t->type == Empty?'e':'f',
1675                                          t->object == NULL?0:t->object->color,
1676                                          t->start, t->finish, t->yp.c);
1677           break;
1678         case PREDICTOR:
1679           (void) fprintf(stderr, "%p P %c      %2d %6lu %6lu %g\n",
1680                                          (void*)t, t->type == Ball?'b':t->type == Empty?'e':'f',
1681                                          t->object == NULL?0:t->object->color,
1682                                          t->start, t->finish, t->yp.c);
1683           break;
1684         default:
1685           (void) fprintf(stderr, "%p: status %d not implemented\n",
1686                                          (void*)t, t->status);
1687           break;
1688         }
1689   }
1690   (void) fprintf(stderr, "---\n");
1691 }
1692 #endif
1693
1694 static int get_num_balls(const char *j)
1695 {
1696   int balls = 0;
1697   const char *p;
1698   int h = 0;
1699   if (!j) abort();
1700   for (p = j; *p; p++) {
1701         if (*p >= '0' && *p <='9') { /* digit */
1702           h = 10*h + (*p - '0');
1703         } else {
1704           if (h > balls) {
1705                 balls = h;
1706           }
1707           h = 0;
1708         }
1709   }
1710   return balls;
1711 }
1712
1713 #ifdef __cplusplus
1714 extern "C" {
1715 #endif
1716
1717 static int
1718 compare_num_balls(const void *p1, const void *p2)
1719 {
1720   int i, j;
1721   i = get_num_balls(((patternstruct*)p1)->pattern);
1722   j = get_num_balls(((patternstruct*)p2)->pattern);
1723   if (i > j) {
1724         return (1);
1725   } else if (i < j) {
1726         return (-1);
1727   } else {
1728         return (0);
1729   }
1730 }
1731
1732 #ifdef __cplusplus
1733 }
1734 #endif
1735
1736
1737 /**************************************************************************
1738  *                        Rendering Functions                             *
1739  *                                                                        *
1740  **************************************************************************/
1741
1742 static void
1743 show_arms(ModeInfo * mi, unsigned long color)
1744 {
1745   jugglestruct *sp = &juggles[MI_SCREEN(mi)];
1746   unsigned int i, j;
1747   Hand side;
1748   XPoint a[XtNumber(sp->arm[0][0])];
1749   if(color == MI_BLACK_PIXEL(mi)) {
1750         j = 0;
1751   } else {
1752         j = 1;
1753   }
1754   XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi),
1755                                          ARMWIDTH, LineSolid, CapRound, JoinRound);
1756   XSetForeground(MI_DISPLAY(mi), MI_GC(mi), color);
1757   for(side = LEFT; side <= RIGHT; side = (Hand)((int)side + 1)) {
1758         /* Translate into device coords */
1759         for(i = 0; i < XtNumber(a); i++) {
1760           a[i].x = (short)(MI_WIDTH(mi)/2 + sp->arm[j][side][i].x*sp->scale);
1761           a[i].y = (short)(MI_HEIGHT(mi)  - sp->arm[j][side][i].y*sp->scale);
1762           if(j == 1)
1763                 sp->arm[0][side][i] = sp->arm[1][side][i];
1764         }
1765         XDrawLines(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
1766                            a, XtNumber(a), CoordModeOrigin);
1767   }
1768 }
1769
1770 static void
1771 show_figure(ModeInfo * mi, unsigned long color, Bool init)
1772 {
1773   jugglestruct *sp = &juggles[MI_SCREEN(mi)];
1774   XPoint p[7];
1775   unsigned int i;
1776
1777   /*      +-----+ 9
1778           |  6  |
1779        10 +--+--+
1780        2 +---+---+ 3
1781           \  5  /
1782            \   /
1783             \ /
1784            1 +
1785             / \
1786            /   \
1787         0 +-----+ 4
1788           |     |
1789           |     |
1790           |     |
1791         7 +     + 8
1792   */
1793
1794   static const XPoint figure[] = {
1795         { 15,  70}, /* 0  Left Hip */
1796         {  0,  90}, /* 1  Waist */
1797         { SX, 130}, /* 2  Left Shoulder */
1798         {-SX, 130}, /* 3  Right Shoulder */
1799         {-15,  70}, /* 4  Right Hip */
1800         {  0, 130}, /* 5  Neck */
1801         {  0, 140}, /* 6  Chin */
1802         { SX,   0}, /* 7  Left Foot */
1803         {-SX,   0}, /* 8  Right Foot */
1804         {-17, 174}, /* 9  Head1 */
1805         { 17, 140}, /* 10 Head2 */
1806   };
1807   XPoint a[XtNumber(figure)];
1808
1809   /* Translate into device coords */
1810   for(i = 0; i < XtNumber(figure); i++) {
1811         a[i].x = (short)(MI_WIDTH(mi)/2 + (sp->cx + figure[i].x)*sp->scale);
1812         a[i].y = (short)(MI_HEIGHT(mi) - figure[i].y*sp->scale);
1813   }
1814
1815   XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi),
1816                 ARMWIDTH, LineSolid, CapRound, JoinRound);
1817   XSetForeground(MI_DISPLAY(mi), MI_GC(mi), color);
1818
1819   i = 0; /* Body */
1820   p[i++] = a[0]; p[i++] = a[1]; p[i++] = a[2];
1821   p[i++] = a[3]; p[i++] = a[1]; p[i++] = a[4];
1822   XDrawLines(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
1823                          p, i, CoordModeOrigin);
1824
1825   i = 0;  /* Legs */
1826   p[i++] = a[7]; p[i++] = a[0]; p[i++] = a[4]; p[i++] = a[8];
1827   XDrawLines(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
1828                          p, i, CoordModeOrigin);
1829
1830   i = 0;  /* Neck */
1831   p[i++] = a[5]; p[i++] = a[6];
1832   XDrawLines(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
1833                          p, i, CoordModeOrigin);
1834
1835   /* Head */
1836   XDrawArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
1837                    a[9].x, a[9].y,
1838                    a[10].x - a[9].x, a[10].y - a[9].y, 0, 64*360);
1839   sp->arm[1][LEFT][SHOULDER].x = sp->cx + figure[2].x;
1840   sp->arm[1][RIGHT][SHOULDER].x = sp->cx + figure[3].x;
1841   if(init) {
1842         /* Initialise arms */
1843         unsigned int i;
1844         for(i = 0; i < 2; i++){
1845           sp->arm[i][LEFT][SHOULDER].y = figure[2].y;
1846           sp->arm[i][LEFT][ELBOW].x = figure[2].x;
1847           sp->arm[i][LEFT][ELBOW].y = figure[1].y;
1848           sp->arm[i][LEFT][HAND].x = figure[0].x;
1849           sp->arm[i][LEFT][HAND].y = figure[1].y;
1850           sp->arm[i][RIGHT][SHOULDER].y = figure[3].y;
1851           sp->arm[i][RIGHT][ELBOW].x = figure[3].x;
1852           sp->arm[i][RIGHT][ELBOW].y = figure[1].y;
1853           sp->arm[i][RIGHT][HAND].x = figure[4].x;
1854           sp->arm[i][RIGHT][HAND].y = figure[1].y;
1855         }
1856   }
1857 }
1858
1859 static void
1860 show_ball(ModeInfo *mi, unsigned long color, Trace *s)
1861 {
1862   jugglestruct *sp = &juggles[MI_SCREEN(mi)];
1863   int offset = (int)(s->angle*64*180/M_PI);
1864   short x = (short)(MI_WIDTH(mi)/2 + s->x * sp->scale);
1865   short y = (short)(MI_HEIGHT(mi) - s->y * sp->scale);
1866
1867   /* Avoid wrapping */
1868   if(s->y*sp->scale >  MI_HEIGHT(mi) * 2) return;
1869
1870   XSetForeground(MI_DISPLAY(mi), MI_GC(mi), color);
1871   if (s->divisions == 0 || color == MI_BLACK_PIXEL(mi))  {
1872         XFillArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
1873                          x - BALLRADIUS, y - BALLRADIUS,
1874                          2*BALLRADIUS, 2*BALLRADIUS,
1875                          0, 23040);
1876   } else if (s->divisions == 4) { /* 90 degree divisions */
1877         XFillArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
1878                          x - BALLRADIUS, y - BALLRADIUS,
1879                          2*BALLRADIUS, 2*BALLRADIUS,
1880                          offset % 23040, 5760);
1881         XFillArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
1882                          x - BALLRADIUS, y - BALLRADIUS,
1883                          2*BALLRADIUS, 2*BALLRADIUS,
1884                          (offset + 11520) % 23040, 5760);
1885
1886         XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi));
1887         XFillArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
1888                          x - BALLRADIUS, y - BALLRADIUS,
1889                          2*BALLRADIUS, 2*BALLRADIUS,
1890                          (offset + 5760) % 23040, 5760);
1891         XFillArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
1892                          x - BALLRADIUS, y - BALLRADIUS,
1893                          2*BALLRADIUS, 2*BALLRADIUS,
1894                          (offset + 17280) % 23040, 5760);
1895   } else if (s->divisions == 2)  { /* 180 degree divisions */
1896         XFillArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
1897                          x - BALLRADIUS, y - BALLRADIUS,
1898                          2*BALLRADIUS, 2*BALLRADIUS,
1899                          offset % 23040, 11520);
1900
1901         XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi));
1902         XFillArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
1903                          x - BALLRADIUS, y - BALLRADIUS,
1904                          2*BALLRADIUS, 2*BALLRADIUS,
1905                          (offset + 11520) % 23040, 11520);
1906   } else {
1907         (void) fprintf(stderr, "juggle[%d]: unexpected divisions: %d\n",
1908                         MI_SCREEN(mi), s->divisions);
1909   }
1910 }
1911
1912 static void
1913 show_europeanclub(ModeInfo *mi, unsigned long color, Trace *s)
1914 {
1915         jugglestruct *sp = &juggles[MI_SCREEN(mi)];
1916         XPoint p[4];
1917         const double sa = sin(s->angle);
1918         const double ca = cos(s->angle);
1919         unsigned int i;
1920
1921         /*  6   6
1922          +-+
1923         /   \
1924      4 +-----+ 7
1925       ////////\
1926    3 +---------+ 8
1927    2 +---------+ 9
1928       |///////|
1929     1 +-------+ 10
1930        |     |
1931        |     |
1932         |   |
1933         |   |
1934          | |
1935          | |
1936          +-+
1937         0  11   */
1938
1939         static const XPoint club[] = {
1940           {-24, 2}, /* 0 */
1941           {-10, 3}, /* 1 */
1942           {  1, 6}, /* 2 */
1943           {  8, 6}, /* 3 */
1944           { 14, 4}, /* 4 */
1945           { 16, 3}, /* 5 */
1946           { 16,-3}, /* 6 */
1947           { 14,-4}, /* 7 */
1948           {  8,-6}, /* 8 */
1949           {  1,-6}, /* 9 */
1950           {-10,-3}, /* 10 */
1951           {-24,-2}, /* 11 */
1952           {-24, 2}, /* 0 close boundary */
1953         };
1954         XPoint a[XtNumber(club)];
1955
1956         /* Avoid wrapping */
1957         if(s->y*sp->scale >  MI_HEIGHT(mi) * 2) return;
1958
1959         /* Translate and fake perspective */
1960         for(i = 0; i < XtNumber(club); i++) {
1961           a[i].x = (short)(MI_WIDTH(mi)/2 +
1962                                            (s->x + club[i].x*PERSPEC*sa)*sp->scale -
1963                                            club[i].y*sqrt(sp->scale)*ca);
1964           a[i].y = (short)(MI_HEIGHT(mi) - (s->y - club[i].x*ca)*sp->scale +
1965                                            club[i].y*sa*sqrt(sp->scale));
1966         }
1967
1968         if(color != MI_BLACK_PIXEL(mi)) {
1969           /* Outline in black */
1970           XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
1971           XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 2,
1972                                                  LineSolid, CapRound, JoinRound);
1973           XDrawLines(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
1974                                  a, XtNumber(a), CoordModeOrigin);
1975         }
1976
1977         XSetForeground(MI_DISPLAY(mi), MI_GC(mi), color);
1978
1979         /* Don't be tempted to optimize erase by drawing all the black in
1980            one X operation.  It must use the same ops as the colours to
1981            guarantee a clean erase. */
1982
1983         i = 0; /* Colored stripes */
1984         p[i++] = a[1]; p[i++] = a[2];
1985         p[i++] = a[9]; p[i++] = a[10];
1986         XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
1987                                  p, i, Convex, CoordModeOrigin);
1988         i = 0;
1989         p[i++] = a[3]; p[i++] = a[4];
1990         p[i++] = a[7]; p[i++] = a[8];
1991         XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
1992                                  p, i, Convex, CoordModeOrigin);
1993
1994         if(color != MI_BLACK_PIXEL(mi)) {
1995           XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi));
1996         }
1997
1998         i = 0; /* White center band */
1999         p[i++] = a[2]; p[i++] = a[3]; p[i++] = a[8]; p[i++] = a[9];
2000         XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
2001                                  p, i, Convex, CoordModeOrigin);
2002
2003         i = 0; /* White handle */
2004         p[i++] = a[0]; p[i++] = a[1]; p[i++] = a[10]; p[i++] = a[11];
2005         XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
2006                                  p, i, Convex, CoordModeOrigin);
2007
2008         i = 0; /* White tip */
2009         p[i++] = a[4]; p[i++] = a[5]; p[i++] = a[6]; p[i++] = a[7];
2010         XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
2011                                  p, i, Convex, CoordModeOrigin);
2012 }
2013
2014 #if 0
2015 static void
2016 show_jugglebugclub(ModeInfo *mi, unsigned long color, Trace *s)
2017 {
2018         jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2019         XPoint p[6];
2020         const double sa = sin(s->angle);
2021         const double ca = cos(s->angle);
2022         unsigned int i;
2023
2024         /*  4   5
2025          +-+
2026         /   \
2027      3 +-----+ 6
2028       ////////\
2029    2 +/////////+ 7
2030       |///////|
2031     1 +-------+ 8
2032        |     |
2033        |     |
2034         |   |
2035         |   |
2036          | |
2037          | |
2038          +-+
2039         0  9    */
2040
2041         static const XPoint club[] = {
2042           {-24, 2}, /* 0 */
2043           { -9, 3}, /* 1 */
2044           {  5, 6}, /* 2 */
2045           { 11, 4}, /* 3 */
2046           { 16, 3}, /* 4 */
2047           { 16,-3}, /* 5 */
2048           { 11,-4}, /* 6 */
2049           {  5,-6}, /* 7 */
2050           { -9,-3}, /* 8 */
2051           {-24,-2}, /* 9 */
2052           {-24, 2}, /* 0 close boundary */
2053         };
2054         XPoint a[XtNumber(club)];
2055
2056         /* Avoid wrapping */
2057         if(s->y*sp->scale >  MI_HEIGHT(mi) * 2) return;
2058
2059         /* Translate and fake perspective */
2060         for(i = 0; i < XtNumber(club); i++) {
2061           a[i].x = (short)(MI_WIDTH(mi)/2 +
2062                                            (s->x + club[i].x*PERSPEC*sa)*sp->scale -
2063                                            club[i].y*sqrt(sp->scale)*ca);
2064           a[i].y = (short)(MI_HEIGHT(mi) - (s->y - club[i].x*ca)*sp->scale +
2065                                            club[i].y*sa*sqrt(sp->scale));
2066         }
2067
2068         if(color != MI_BLACK_PIXEL(mi)) {
2069           /* Outline in black */
2070           XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
2071           XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 2,
2072                                                  LineSolid, CapRound, JoinRound);
2073           XDrawLines(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
2074                                  a, XtNumber(a), CoordModeOrigin);
2075         }
2076
2077         XSetForeground(MI_DISPLAY(mi), MI_GC(mi), color);
2078
2079         /* Don't be tempted to optimize erase by drawing all the black in
2080            one X operation.  It must use the same ops as the colours to
2081            guarantee a clean erase. */
2082
2083         i = 0; /* Coloured center band */
2084         p[i++] = a[1]; p[i++] = a[2]; p[i++] = a[3];
2085         p[i++] = a[6]; p[i++] = a[7]; p[i++] = a[8];
2086         XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
2087                                  p, i, Convex, CoordModeOrigin);
2088
2089         if(color != MI_BLACK_PIXEL(mi)) {
2090           XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi));
2091         }
2092
2093         i = 0; /* White handle */
2094         p[i++] = a[0]; p[i++] = a[1]; p[i++] = a[8]; p[i++] = a[9];
2095         XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
2096                                  p, i, Convex, CoordModeOrigin);
2097
2098         i = 0; /* White tip */
2099         p[i++] = a[3]; p[i++] = a[4]; p[i++] = a[5]; p[i++] = a[6];
2100         XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
2101                                  p, i, Convex, CoordModeOrigin);
2102 }
2103 #endif
2104
2105 static void
2106 show_torch(ModeInfo *mi, unsigned long color, Trace *s)
2107 {
2108         jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2109         XPoint head, tail, last;
2110         DXPoint dhead, dlast;
2111         const double sa = sin(s->angle);
2112         const double ca = cos(s->angle);
2113
2114         const double TailLen = -24;
2115         const double HeadLen = 16;
2116         const short Width   = (short)(5 * sqrt(sp->scale));
2117
2118         /*
2119       +///+ head
2120     last  |
2121           |
2122           |
2123           |
2124           |
2125           + tail
2126         */
2127
2128         dhead.x = s->x + HeadLen * PERSPEC * sa;
2129         dhead.y = s->y - HeadLen * ca;
2130
2131         if(color == MI_BLACK_PIXEL(mi)) { /* Use 'last' when erasing */
2132           dlast = s->dlast;
2133         } else { /* Store 'last' so we can use it later when s->prev has
2134                                 gone */
2135           if(s->prev != s->next) {
2136                 dlast.x = s->prev->x + HeadLen * PERSPEC * sin(s->prev->angle);
2137                 dlast.y = s->prev->y - HeadLen * cos(s->prev->angle);
2138           } else {
2139                 dlast = dhead;
2140           }
2141           s->dlast = dlast;
2142         }
2143
2144         /* Avoid wrapping (after last is stored) */
2145         if(s->y*sp->scale >  MI_HEIGHT(mi) * 2) return;
2146
2147         head.x = (short)(MI_WIDTH(mi)/2 + dhead.x*sp->scale);
2148         head.y = (short)(MI_HEIGHT(mi) - dhead.y*sp->scale);
2149
2150         last.x = (short)(MI_WIDTH(mi)/2 + dlast.x*sp->scale);
2151         last.y = (short)(MI_HEIGHT(mi) - dlast.y*sp->scale);
2152
2153         tail.x = (short)(MI_WIDTH(mi)/2 +
2154                                          (s->x + TailLen * PERSPEC * sa)*sp->scale );
2155         tail.y = (short)(MI_HEIGHT(mi) - (s->y - TailLen * ca)*sp->scale );
2156
2157         if(color != MI_BLACK_PIXEL(mi)) {
2158           XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
2159           XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi),
2160                                                  Width, LineSolid, CapRound, JoinRound);
2161           XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
2162                                 head.x, head.y, tail.x, tail.y);
2163         }
2164         XSetForeground(MI_DISPLAY(mi), MI_GC(mi), color);
2165         XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi),
2166                                            Width * 2, LineSolid, CapRound, JoinRound);
2167
2168         XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
2169                           head.x, head.y, last.x, last.y);
2170
2171 }
2172
2173 static void
2174 show_knife(ModeInfo *mi, unsigned long color, Trace *s)
2175 {
2176         jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2177         unsigned int i;
2178         const double sa = sin(s->angle);
2179         const double ca = cos(s->angle);
2180
2181         /*
2182         2 +
2183           |+ 3
2184           ||
2185         1 +++ 5
2186           |4|
2187           | |
2188            + 0
2189         */
2190         static const XPoint knife[] = {
2191           {-24, 0}, /* 0 */
2192           { -5,-3}, /* 1 */
2193           { 16,-3}, /* 2 */
2194           { 12, 0}, /* 3 */
2195           { -5, 0}, /* 4 */
2196           { -5, 3}, /* 5 */
2197         };
2198         XPoint a[XtNumber(knife)], p[5];
2199
2200         /* Avoid wrapping */
2201         if(s->y*sp->scale >  MI_HEIGHT(mi) * 2) return;
2202
2203         /* Translate and fake perspective */
2204         for(i = 0; i < XtNumber(knife); i++) {
2205           a[i].x = (short)(MI_WIDTH(mi)/2 +
2206                                            (s->x + knife[i].x*PERSPEC*sa)*sp->scale -
2207                                            knife[i].y*sqrt(sp->scale)*ca*PERSPEC);
2208           a[i].y = (short)(MI_HEIGHT(mi) - (s->y - knife[i].x*ca)*sp->scale +
2209                                            knife[i].y*sa*sqrt(sp->scale));
2210         }
2211
2212         /* Handle */
2213         XSetForeground(MI_DISPLAY(mi), MI_GC(mi), color);
2214         XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), (short)(4*sqrt(sp->scale)),
2215                                            LineSolid, CapRound, JoinRound);
2216         XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
2217                           a[0].x, a[0].y, a[4].x, a[4].y);
2218
2219         /* Blade */
2220         if(color != MI_BLACK_PIXEL(mi)) {
2221           XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi));
2222         }
2223         i = 0;
2224         p[i++] = a[1]; p[i++] = a[2]; p[i++] = a[3]; p[i++] = a[5];
2225         XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
2226                                  p, i, Convex, CoordModeOrigin);
2227 }
2228
2229 static void
2230 show_ring(ModeInfo *mi, unsigned long color, Trace *s)
2231 {
2232         jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2233         short x = (short)(MI_WIDTH(mi)/2 + s->x * sp->scale);
2234         short y = (short)(MI_HEIGHT(mi) - s->y * sp->scale);
2235         double radius = 15 * sp->scale;
2236         short thickness = (short)(8 * sqrt(sp->scale));
2237
2238         /* Avoid wrapping */
2239         if(s->y*sp->scale >  MI_HEIGHT(mi) * 2) return;
2240
2241         XSetForeground(MI_DISPLAY(mi), MI_GC(mi), color);
2242         XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi),
2243                                            thickness, LineSolid, CapRound, JoinRound);
2244
2245         XDrawArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
2246                          (short)(x - radius*PERSPEC), (short)(y - radius),
2247                          (short)(2*radius*PERSPEC), (short)(2*radius),
2248                          0, 23040);
2249 }
2250
2251
2252 static void
2253 show_bball(ModeInfo *mi, unsigned long color, Trace *s)
2254 {
2255         jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2256         short x = (short)(MI_WIDTH(mi)/2 + s->x * sp->scale);
2257         short y = (short)(MI_HEIGHT(mi) - s->y * sp->scale);
2258         double radius = 12 * sp->scale;
2259         int offset = (int)(s->angle*64*180/M_PI);
2260         int holesize = (int)(3.0*sqrt(sp->scale));
2261
2262         /* Avoid wrapping */
2263         if(s->y*sp->scale >  MI_HEIGHT(mi) * 2) return;
2264
2265         XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
2266         XFillArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
2267                          (short)(x - radius), (short)(y - radius),
2268                          (short)(2*radius), (short)(2*radius),
2269                          0, 23040);
2270         XSetForeground(MI_DISPLAY(mi), MI_GC(mi), color);
2271         XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 2,
2272                                            LineSolid, CapRound, JoinRound);
2273         XDrawArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
2274                          (short)(x - radius), (short)(y - radius),
2275                          (short)(2*radius), (short)(2*radius),
2276                          0, 23040);
2277
2278         /* Draw finger holes */
2279         XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), holesize,
2280                                            LineSolid, CapRound, JoinRound);
2281
2282         XDrawArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
2283                          (short)(x - radius*0.5), (short)(y - radius*0.5),
2284                          (short)(2*radius*0.5), (short)(2*radius*0.5),
2285                          (offset + 960) % 23040, 0);
2286         XDrawArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
2287                          (short)(x - radius*0.7), (short)(y - radius*0.7),
2288                          (short)(2*radius*0.7), (short)(2*radius*0.7),
2289                          (offset + 1920) % 23040, 0);
2290         XDrawArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
2291                          (short)(x - radius*0.7), (short)(y - radius*0.7),
2292                          (short)(2*radius*0.7), (short)(2*radius*0.7),
2293                          offset % 23040, 0);
2294 }
2295
2296 /**************************************************************************
2297  *                    Public Functions                                    *
2298  *                                                                        *
2299  **************************************************************************/
2300
2301
2302 /* FIXME: refill_juggle currently just appends new throws to the
2303  * programme.  This is fine if the programme is empty, but if there
2304  * are still some trajectories left then it really should take these
2305  * into account */
2306
2307 static void
2308 refill_juggle(ModeInfo * mi)
2309 {
2310   jugglestruct *sp = NULL;
2311   int i;
2312
2313   if (juggles == NULL)
2314         return;
2315   sp = &juggles[MI_SCREEN(mi)];
2316
2317   /* generate pattern */
2318   if (pattern == NULL) {
2319
2320 #define MAXPAT 10
2321 #define MAXREPEAT 300
2322 #define CHANGE_BIAS 8 /* larger makes num_ball changes less likely */
2323 #define POSITION_BIAS 20 /* larger makes hand movements less likely */
2324
2325         int count = 0;
2326         while (count < MI_CYCLES(mi)) {
2327           char buf[MAXPAT * 3 + 3], *b = buf;
2328           int maxseen = 0;
2329           int l = NRAND(MAXPAT) + 1;
2330           int t = NRAND(MIN(MAXREPEAT, (MI_CYCLES(mi) - count))) + 1;
2331
2332           { /* vary number of balls */
2333                 int new_balls = sp->num_balls;
2334                 int change;
2335
2336                 if (new_balls == 2) /* Do not juggle 2 that often */
2337                   change = NRAND(2 + CHANGE_BIAS / 4);
2338                 else
2339                   change = NRAND(2 + CHANGE_BIAS);
2340                 switch (change) {
2341                 case 0:
2342                   new_balls++;
2343                   break;
2344                 case 1:
2345                   new_balls--;
2346                   break;
2347                 default:
2348                   break; /* NO-OP */
2349                 }
2350                 if (new_balls < sp->patternindex.minballs) {
2351                   new_balls += 2;
2352                 }
2353                 if (new_balls > sp->patternindex.maxballs) {
2354                   new_balls -= 2;
2355                 }
2356                 if (new_balls < sp->num_balls) {
2357                   if (!program(mi, "[*]", NULL, 1)) /* lose ball */
2358                         return;
2359                 }
2360                 sp->num_balls = new_balls;
2361           }
2362
2363           count += t;
2364           if (NRAND(2) && sp->patternindex.index[sp->num_balls].number) {
2365                 /* Pick from PortFolio */
2366                 int p = sp->patternindex.index[sp->num_balls].start +
2367                   NRAND(sp->patternindex.index[sp->num_balls].number);
2368                 if (!program(mi, portfolio[p].pattern, portfolio[p].name, t))
2369                   return;
2370           } else {
2371                 /* Invent a new pattern */
2372                 *b++='[';
2373                 for(i = 0; i < l; i++){
2374                   int n, m;
2375                   do { /* Triangular Distribution => high values more likely */
2376                         m = NRAND(sp->num_balls + 1);
2377                         n = NRAND(sp->num_balls + 1);
2378                   } while(m >= n);
2379                   if (n == sp->num_balls) {
2380                         maxseen = 1;
2381                   }
2382                   switch(NRAND(5 + POSITION_BIAS)){
2383                   case 0:            /* Outside throw */
2384                         *b++ = '+'; break;
2385                   case 1:            /* Cross throw */
2386                         *b++ = '='; break;
2387                   case 2:            /* Cross catch */
2388                         *b++ = '&'; break;
2389                   case 3:            /* Cross throw and catch */
2390                         *b++ = 'x'; break;
2391                   case 4:            /* Bounce */
2392                         *b++ = '_'; break;
2393                   default:
2394                         break;             /* Inside throw (default) */
2395                   }
2396
2397                   *b++ = n + '0';
2398                   *b++ = ' ';
2399                 }
2400                 *b++ = ']';
2401                 *b = '\0';
2402                 if (maxseen) {
2403                   if (!program(mi, buf, NULL, t))
2404                         return;
2405                 }
2406           }
2407         }
2408   } else { /* pattern supplied in height or 'a' notation */
2409         if (!program(mi, pattern, NULL, MI_CYCLES(mi)))
2410           return;
2411   }
2412
2413   adam(sp);
2414
2415   name(sp);
2416
2417   if (!part(mi))
2418         return;
2419
2420   lob(mi);
2421
2422   clap(sp);
2423
2424   positions(sp);
2425
2426   if (!projectile(sp)) {
2427         free_juggle(mi);
2428         return;
2429   }
2430
2431   hands(sp);
2432 #ifdef DEBUG
2433   if(MI_IS_DEBUG(mi)) dump(sp);
2434 #endif
2435 }
2436
2437 static void
2438 change_juggle(ModeInfo * mi)
2439 {
2440   jugglestruct *sp = NULL;
2441   Trajectory *t;
2442
2443   if (juggles == NULL)
2444         return;
2445   sp = &juggles[MI_SCREEN(mi)];
2446
2447   /* Strip pending trajectories */
2448   for (t = sp->head->next; t != sp->head; t = t->next) {
2449         if(t->start > sp->time || t->finish < sp->time) {
2450           Trajectory *n = t;
2451           t=t->prev;
2452           trajectory_destroy(n);
2453         }
2454   }
2455
2456   /* Pick the current object theme */
2457   sp->objtypes = choose_object();
2458
2459   refill_juggle(mi);
2460
2461   /* Clean up the Screen.  Don't use MI_CLEARWINDOW(mi), since we
2462          don't all those special effects. */
2463   XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
2464
2465   show_figure(mi, MI_WHITE_PIXEL(mi), True);
2466
2467 }
2468
2469 ENTRYPOINT void
2470 init_juggle (ModeInfo * mi)
2471 {
2472   jugglestruct *sp = 0;
2473   int i;
2474
2475   MI_INIT (mi, juggles);
2476   sp = &juggles[MI_SCREEN(mi)];
2477
2478   if (only && *only && strcmp(only, " ")) {
2479     balls = clubs = torches = knives = rings = bballs = False;
2480     if (!strcasecmp (only, "balls"))   balls   = True;
2481     else if (!strcasecmp (only, "clubs"))   clubs   = True;
2482     else if (!strcasecmp (only, "torches")) torches = True;
2483     else if (!strcasecmp (only, "knives"))  knives  = True;
2484     else if (!strcasecmp (only, "rings"))   rings   = True;
2485     else if (!strcasecmp (only, "bballs"))  bballs  = True;
2486     else {
2487       (void) fprintf (stderr,
2488                "Juggle: -only must be one of: balls, clubs, torches, knives,\n"
2489                "\t rings, or bballs (not \"%s\")\n", only);
2490 #ifdef STANDALONE /* xlock mustn't exit merely because of a bad argument */
2491       exit (1);
2492 #endif
2493     }
2494   }
2495
2496   if (sp->head == 0) {  /* first time initializing this juggler */
2497
2498         sp->count = ABS(MI_COUNT(mi));
2499         if (sp->count == 0)
2500           sp->count = 200;
2501
2502         /* record start time */
2503         sp->begintime = time(NULL);
2504         if(sp->patternindex.maxballs > 0) {
2505           sp->num_balls = sp->patternindex.minballs +
2506                 NRAND(sp->patternindex.maxballs - sp->patternindex.minballs);
2507         }
2508
2509         show_figure(mi, MI_WHITE_PIXEL(mi), True); /* Draw figure.  Also discovers
2510                                                           information about the juggler's
2511                                                           proportions */
2512
2513         /* "7" should be about three times the height of the juggler's
2514            shoulders */
2515         sp->Gr = -GRAVITY(3 * sp->arm[0][RIGHT][SHOULDER].y,
2516                                           7 * THROW_CATCH_INTERVAL);
2517
2518         if(!balls && !clubs && !torches && !knives && !rings && !bballs)
2519           balls = True; /* Have to juggle something! */
2520
2521         /* create circular trajectory list */
2522         ADD_ELEMENT(Trajectory, sp->head, sp->head);
2523         if(sp->head == NULL){
2524           free_juggle(mi);
2525           return;
2526         }
2527
2528         /* create circular object list */
2529         ADD_ELEMENT(Object, sp->objects, sp->objects);
2530         if(sp->objects == NULL){
2531           free_juggle(mi);
2532           return;
2533         }
2534
2535         /* create circular wander list */
2536         ADD_ELEMENT(Wander, sp->wander, sp->wander);
2537         if(sp->wander == NULL){
2538           free_juggle(mi);
2539           return;
2540         }
2541         (void)wander(sp, 0); /* Initialize wander */
2542
2543         sp->pattern =  strdup(""); /* Initialise saved pattern with
2544                                                                   free-able memory */
2545   }
2546
2547   sp = &juggles[MI_SCREEN(mi)];
2548
2549   if (pattern &&
2550       (!*pattern ||
2551        !strcasecmp (pattern, ".") ||
2552        !strcasecmp (pattern, "random")))
2553         pattern = NULL;
2554
2555   if (pattern == NULL && sp->patternindex.maxballs == 0) {
2556         /* pattern list needs indexing */
2557         int nelements = XtNumber(portfolio);
2558         int numpat = 0;
2559
2560         /* sort according to number of balls */
2561         qsort((void*)portfolio, nelements,
2562                   sizeof(portfolio[1]), compare_num_balls);
2563
2564         /* last pattern has most balls */
2565         sp->patternindex.maxballs = get_num_balls(portfolio[nelements - 1].pattern);
2566         /* run through sorted list, indexing start of each group
2567            and number in group */
2568         sp->patternindex.maxballs = 1;
2569         for (i = 0; i < nelements; i++) {
2570           int b = get_num_balls(portfolio[i].pattern);
2571           if (b > sp->patternindex.maxballs) {
2572                 sp->patternindex.index[sp->patternindex.maxballs].number = numpat;
2573                 if(numpat == 0) sp->patternindex.minballs = b;
2574                 sp->patternindex.maxballs = b;
2575                 numpat = 1;
2576                 sp->patternindex.index[sp->patternindex.maxballs].start = i;
2577           } else {
2578                 numpat++;
2579           }
2580         }
2581         sp->patternindex.index[sp->patternindex.maxballs].number = numpat;
2582   }
2583
2584   /* Set up programme */
2585   change_juggle(mi);
2586
2587   /* Clean up the Screen.  Don't use MI_CLEARWINDOW(mi), since we may
2588          only be resizing and then we won't all those special effects. */
2589   XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
2590
2591   /* Only put things here that won't interrupt the programme during
2592          a window resize */
2593
2594   /* Use MIN so that users can resize in interesting ways, eg
2595          narrow windows for tall patterns, etc */
2596   sp->scale = MIN(MI_HEIGHT(mi)/480.0, MI_WIDTH(mi)/160.0);
2597
2598   if(describe && !sp->mode_font) { /* Check to see if there's room to describe patterns. */
2599     char *font = get_string_resource (MI_DISPLAY(mi), "font", "Font");
2600         sp->mode_font = XLoadQueryFont(MI_DISPLAY(mi), font);
2601   }
2602 }
2603
2604 ENTRYPOINT void
2605 draw_juggle (ModeInfo * mi)
2606 {
2607   Trajectory *traj = NULL;
2608   Object *o = NULL;
2609   unsigned long future = 0;
2610   jugglestruct *sp = NULL;
2611   char *pattern = NULL;
2612   double cx;
2613
2614   if (juggles == NULL)
2615         return;
2616   sp = &juggles[MI_SCREEN(mi)];
2617
2618   MI_IS_DRAWN(mi) = True;
2619
2620 #ifdef HAVE_JWXYZ
2621   /* Don't worry about flicker, trust Quartz's double-buffering.
2622      This is a fast fix for the pixel-turds I can't track down...
2623    */
2624   XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi));
2625 #endif
2626
2627   /* Update timer */
2628   if (real) {
2629         struct timeval tv;
2630         (void)gettimeofday(&tv, NULL);
2631         sp->time = (int) ((tv.tv_sec - sp->begintime)*1000 + tv.tv_usec/1000);
2632   } else {
2633         sp->time += MI_DELAY(mi) / 1000;
2634   }
2635
2636   /* First pass: Move arms and strip out expired elements */
2637   for (traj = sp->head->next; traj != sp->head; traj = traj->next) {
2638         if (traj->status != PREDICTOR) {
2639           /* Skip any elements that need further processing */
2640           /* We could remove them, but there shoudn't be many and they
2641                  would be needed if we ever got the pattern refiller
2642                  working */
2643           continue;
2644         }
2645         if (traj->start > future) { /* Lookahead to the end of the show */
2646           future = traj->start;
2647         }
2648         if (sp->time < traj->start) { /* early */
2649           continue;
2650         } else if (sp->time < traj->finish) { /* working */
2651
2652           /* Look for pattern name */
2653           if(traj->pattern != NULL) {
2654                 pattern=traj->pattern;
2655           }
2656
2657           if (traj->type == Empty || traj->type == Full) {
2658                 /* Only interested in hands on this pass */
2659                 double angle = traj->angle + traj->spin * (sp->time - traj->start);
2660                 double xd = 0, yd = 0;
2661                 DXPoint p;
2662
2663                 /* Find the catching offset */
2664                 if(traj->object != NULL) {
2665                   if(ObjectDefs[traj->object->type].handle > 0) {
2666                         /* Handles Need to be oriented */
2667                         xd = ObjectDefs[traj->object->type].handle *
2668                           PERSPEC * sin(angle);
2669                         yd = ObjectDefs[traj->object->type].handle *
2670                           cos(angle);
2671                   } else {
2672                         /* Balls are always caught at the bottom */
2673                         xd = 0;
2674                         yd = -4;
2675                   }
2676                 }
2677                 p.x = (CUBIC(traj->xp, sp->time) - xd);
2678                 p.y = (CUBIC(traj->yp, sp->time) + yd);
2679                 reach_arm(mi, traj->hand, &p);
2680
2681                 /* Store updated hand position */
2682                 traj->x = p.x + xd;
2683                 traj->y = p.y - yd;
2684           }
2685           if (traj->type == Ball || traj->type == Full) {
2686                 /* Only interested in objects on this pass */
2687                 double x, y;
2688                 Trace *s;
2689
2690                 if(traj->type == Full) {
2691                   /* Adjusted these in the first pass */
2692                   x = traj->x;
2693                   y = traj->y;
2694                 } else {
2695                   x = CUBIC(traj->xp, sp->time);
2696                   y = CUBIC(traj->yp, sp->time);
2697                 }
2698
2699                 ADD_ELEMENT(Trace, s, traj->object->trace->prev);
2700                 s->x = x;
2701                 s->y = y;
2702                 s->angle = traj->angle + traj->spin * (sp->time - traj->start);
2703                 s->divisions = traj->divisions;
2704                 traj->object->tracelen++;
2705                 traj->object->active = True;
2706           }
2707         } else { /* expired */
2708           Trajectory *n = traj;
2709           traj=traj->prev;
2710           trajectory_destroy(n);
2711         }
2712   }
2713
2714   /* Erase end of trails */
2715   for (o = sp->objects->next; o != sp->objects; o = o->next) {
2716         Trace *s;
2717         for (s = o->trace->next;
2718                  o->trace->next != o->trace &&
2719                    (o->count == 0 || o->tracelen > o->tail);
2720                  s = o->trace->next) {
2721           ObjectDefs[o->type].draw(mi, MI_BLACK_PIXEL(mi), s);
2722           REMOVE(s);
2723           o->tracelen--;
2724           if(o->count <= 0 && o->tracelen <= 0) {
2725                 /* Object no longer in use and trail gone */
2726                 Object *n = o;
2727                 o = o->prev;
2728                 object_destroy(n);
2729           }
2730           if(o->count <= 0) break; /* Allow loop for catch-up, but not clean-up */
2731         }
2732   }
2733
2734   show_arms(mi, MI_BLACK_PIXEL(mi));
2735   cx = wander(sp, sp->time);
2736   /* Reduce flicker by only permitting movements of more than a pixel */
2737   if(fabs((sp->cx - cx))*sp->scale >= 2.0 ) {
2738         show_figure(mi, MI_BLACK_PIXEL(mi), False);
2739         sp->cx = cx;
2740   }
2741
2742   show_figure(mi, MI_WHITE_PIXEL(mi), False);
2743
2744   show_arms(mi, MI_WHITE_PIXEL(mi));
2745
2746   /* Draw Objects */
2747   for (o = sp->objects->next; o != sp->objects; o = o->next) {
2748         if(o->active) {
2749           ObjectDefs[o->type].draw(mi,MI_PIXEL(mi, o->color), o->trace->prev);
2750           o->active = False;
2751         }
2752   }
2753
2754
2755   /* Save pattern name so we can erase it when it changes */
2756   if(pattern != NULL && strcmp(sp->pattern, pattern) != 0 ) {
2757         /* Erase old name */
2758         XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
2759 # if 0
2760         XDrawString(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
2761                                 0, 20, sp->pattern, strlen(sp->pattern));
2762 # else
2763     XFillRectangle(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
2764                    0, 0, MI_WIDTH(mi), 25);
2765 # endif
2766         free(sp->pattern);
2767         sp->pattern = strdup(pattern);
2768
2769         if (MI_IS_VERBOSE(mi)) {
2770           (void) fprintf(stderr, "Juggle[%d]: Running: %s\n",
2771                                          MI_SCREEN(mi), sp->pattern);
2772         }
2773   }
2774   if(sp->mode_font != None &&
2775          XTextWidth(sp->mode_font, sp->pattern, strlen(sp->pattern)) < MI_WIDTH(mi)) {
2776         /* Redraw once a cycle, in case it's obscured or it changed */
2777         XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi));
2778         XDrawImageString(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi),
2779                      0, 20, sp->pattern, strlen(sp->pattern));
2780   }
2781
2782 #ifdef MEMTEST
2783   if((int)(sp->time/10) % 1000 == 0)
2784         (void) fprintf(stderr, "sbrk: %d\n", (int)sbrk(0));
2785 #endif
2786
2787   if (future < sp->time + 100 * THROW_CATCH_INTERVAL) {
2788         refill_juggle(mi);
2789   } else if (sp->time > 1<<30) { /* Hard Reset before the clock wraps */
2790         init_juggle(mi);
2791   }
2792 }
2793
2794 XSCREENSAVER_MODULE ("Juggle", juggle)
2795
2796 #endif /* MODE_juggle */