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