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