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