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