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