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