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