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