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