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