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