1 /* juggle, Copyright (c) 1996-2009 Tim Auckland <tda10.geo@yahoo.com>
2 * and Jamie Zawinski <jwz@jwz.org>
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.
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.
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.
22 * 09-Aug-2009: jwz: converted from Xlib to OpenGL.
23 * 13-Dec-2004: [TDA] Use -cycles and -count in a rational manner.
24 * Add -rings, -bballs. Add -describe. Finally made
25 * live pattern updates possible. Add refill_juggle(),
26 * change_juggle() and reshape_juggle(). Make
27 * init_juggle() non-destructive. Reorder erase/draw
28 * operations. Update xscreensaver xml and manpage.
29 * 15-Nov-2004: [TDA] Fix all memory leaks.
30 * 12-Nov-2004: [TDA] Add -torches and another new trail
31 * implementation, so that different objects can have
32 * different length trails.
33 * 11-Nov-2004: [TDA] Clap when all the balls are in the air.
34 * 10-Nov-2004: [TDA] Display pattern name converted to hight
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
45 * Implement the anonymously promised -uni option.
50 * Notes on Adam Chalcraft Juggling Notation (used by permission)
51 * a-> Adam's notation s-> Site swap (Cambridge) notation
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.
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.
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]
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.
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
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)
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.
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
102 * ...55555555551551551551551...
104 * Now convert back to s-notation, to get
106 * ...55555566771771771771771...
108 * So the answer is to do two 6 throws and then go straight into
109 * (771). Coming back down of course,
111 * ...5515515515515515555555555...
115 * ...7717717717716615555555555...
117 * so the answer is to do a single 661 and then drop straight down to
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
126 /* This code uses so many linked lists it's worth having a built-in
130 # define DEFAULTS "*delay: 10000 \n" \
134 "*titleFont: -*-helvetica-bold-r-normal-*-*-180-*-*-*-*-*-*\n" \
135 "*showFPS: False \n" \
136 "*wireframe: False \n" \
138 # define release_juggle 0
140 #define countof(x) (sizeof((x))/sizeof((*x)))
142 #include "xlockmore.h"
146 #include "gltrackball.h"
150 #ifdef USE_GL /* whole file */
152 #define DEF_PATTERN "random" /* All patterns */
153 #define DEF_TAIL "1" /* No trace */
155 /* Maybe a ROLA BOLA would be at a better angle for viewing */
156 #define DEF_UNI "False" /* No unicycle */ /* Not implemented yet */
158 #define DEF_REAL "True"
159 #define DEF_DESCRIBE "True"
161 #define DEF_BALLS "True" /* Use Balls */
162 #define DEF_CLUBS "True" /* Use Clubs */
163 #define DEF_TORCHES "True" /* Use Torches */
164 #define DEF_KNIVES "True" /* Use Knives */
165 #define DEF_RINGS "True" /* Use Rings */
166 #define DEF_BBALLS "True" /* Use Bowling Balls */
168 static char *pattern;
174 static Bool describe;
183 static XrmOptionDescRec opts[] = {
184 {"-pattern", ".juggle.pattern", XrmoptionSepArg, NULL },
185 {"-tail", ".juggle.tail", XrmoptionSepArg, NULL },
187 {"-uni", ".juggle.uni", XrmoptionNoArg, "on" },
188 {"+uni", ".juggle.uni", XrmoptionNoArg, "off" },
190 {"-real", ".juggle.real", XrmoptionNoArg, "on" },
191 {"+real", ".juggle.real", XrmoptionNoArg, "off" },
192 {"-describe", ".juggle.describe", XrmoptionNoArg, "on" },
193 {"+describe", ".juggle.describe", XrmoptionNoArg, "off" },
194 {"-balls", ".juggle.balls", XrmoptionNoArg, "on" },
195 {"+balls", ".juggle.balls", XrmoptionNoArg, "off" },
196 {"-clubs", ".juggle.clubs", XrmoptionNoArg, "on" },
197 {"+clubs", ".juggle.clubs", XrmoptionNoArg, "off" },
198 {"-torches", ".juggle.torches", XrmoptionNoArg, "on" },
199 {"+torches", ".juggle.torches", XrmoptionNoArg, "off" },
200 {"-knives", ".juggle.knives", XrmoptionNoArg, "on" },
201 {"+knives", ".juggle.knives", XrmoptionNoArg, "off" },
202 {"-rings", ".juggle.rings", XrmoptionNoArg, "on" },
203 {"+rings", ".juggle.rings", XrmoptionNoArg, "off" },
204 {"-bballs", ".juggle.bballs", XrmoptionNoArg, "on" },
205 {"+bballs", ".juggle.bballs", XrmoptionNoArg, "off" },
206 {"-only", ".juggle.only", XrmoptionSepArg, NULL },
209 static argtype vars[] = {
210 { &pattern, "pattern", "Pattern", DEF_PATTERN, t_String },
211 { &tail, "tail", "Tail", DEF_TAIL, t_Int },
213 { &uni, "uni", "Uni", DEF_UNI, t_Bool },
215 { &real, "real", "Real", DEF_REAL, t_Bool },
216 { &describe, "describe", "Describe", DEF_DESCRIBE, t_Bool },
217 { &balls, "balls", "Clubs", DEF_BALLS, t_Bool },
218 { &clubs, "clubs", "Clubs", DEF_CLUBS, t_Bool },
219 { &torches, "torches", "Torches", DEF_TORCHES, t_Bool },
220 { &knives, "knives", "Knives", DEF_KNIVES, t_Bool },
221 { &rings, "rings", "Rings", DEF_RINGS, t_Bool },
222 { &bballs, "bballs", "BBalls", DEF_BBALLS, t_Bool },
223 { &only, "only", "BBalls", " ", t_String },
226 static OptionStruct desc[] =
228 { "-pattern string", "Cambridge Juggling Pattern" },
229 { "-tail num", "Trace Juggling Patterns" },
231 { "-/+uni", "Unicycle" },
233 { "-/+real", "Real-time" },
234 { "-/+describe", "turn on/off pattern descriptions." },
235 { "-/+balls", "turn on/off Balls." },
236 { "-/+clubs", "turn on/off Clubs." },
237 { "-/+torches", "turn on/off Flaming Torches." },
238 { "-/+knives", "turn on/off Knives." },
239 { "-/+rings", "turn on/off Rings." },
240 { "-/+bballs", "turn on/off Bowling Balls." },
241 { "-only", "Turn off all objects but the named one." },
244 ENTRYPOINT ModeSpecOpt juggle_opts =
245 {countof(opts), opts, countof(vars), vars, desc};
248 /* Note: All "lengths" are scaled by sp->scale = MI_HEIGHT/480. All
249 "thicknesses" are scaled by sqrt(sp->scale) so that they are
250 proportionally thicker for smaller windows. Objects spinning out
251 of the plane (such as clubs) fake perspective by compressing their
252 horizontal coordinates by PERSPEC */
256 #define ARMWIDTH ((int) (8.0 * sqrt(sp->scale)))
258 #define BALLRADIUS ARMWIDTH
260 /* build all the models assuming a 480px high scene */
261 #define SCENE_HEIGHT 480
262 #define SCENE_WIDTH ((int)(SCENE_HEIGHT*(MI_WIDTH(mi)/(float)MI_HEIGHT(mi))))
264 /*#define PERSPEC 0.4*/
267 #define GRAVITY(h, t) 4*(double)(h)/((t)*(t))
269 /* Timing based on count. Units are milliseconds. Juggles per second
270 is: 2000 / THROW_CATCH_INTERVAL + CATCH_THROW_INTERVAL */
272 #define THROW_CATCH_INTERVAL (sp->count)
273 #define THROW_NULL_INTERVAL (sp->count * 0.5)
274 #define CATCH_THROW_INTERVAL (sp->count * 0.2)
276 /********************************************************************
277 * Trace Definitions *
279 * These record rendering data so that a drawn object can be erased *
280 * later. Each object has its own Trace list. *
282 ********************************************************************/
284 typedef struct {double x, y; } DXPoint;
285 typedef struct trace *TracePtr;
286 typedef struct trace {
297 /*******************************************************************
298 * Object Definitions *
300 * These describe the various types of Object that can be juggled *
302 *******************************************************************/
303 typedef int (DrawProc)(ModeInfo*, unsigned long, Trace *);
305 static DrawProc show_ball, show_europeanclub, show_torch, show_knife;
306 static DrawProc show_ring, show_bball;
308 typedef enum {BALL, CLUB, TORCH, KNIFE, RING, BBALLS,
309 NUM_OBJECT_TYPES} ObjType;
311 #define OBJMIXPROB 20 /* inverse of the chances of using an odd
312 object in the pattern */
314 static const GLfloat body_color_1[4] = { 0.9, 0.7, 0.5, 1 };
315 static const GLfloat body_color_2[4] = { 0.6, 0.4, 0.2, 1 };
317 static const struct {
318 DrawProc *draw; /* Object Rendering function */
319 int handle; /* Length of object's handle */
320 int mintrail; /* Minimum trail length */
321 double cor; /* Coefficient of Restitution. perfect bounce = 1 */
322 double weight; /* Heavier objects don't get thrown as high */
335 0.55, /* Clubs don't bounce too well */
341 20, /* Torches need flames */
342 0, /* Torches don't bounce -- fire risk! */
349 0, /* Knives don't bounce */
368 /**************************
369 * Trajectory definitions *
370 **************************/
372 typedef enum {HEIGHT, ADAM} Notation;
373 typedef enum {Empty, Full, Ball} Throwable;
374 typedef enum {LEFT, RIGHT} Hand;
375 typedef enum {THROW, CATCH} Action;
376 typedef enum {HAND, ELBOW, SHOULDER} Joint;
377 typedef enum {ATCH, THRATCH, ACTION, LINKEDACTION,
378 PTHRATCH, BPREDICTOR, PREDICTOR} TrajectoryStatus;
379 typedef struct {double a, b, c, d; } Spline;
380 typedef DXPoint Arm[3];
383 /* Object is an arbitrary object being juggled. Each Trajectory
384 * references an Object ("count" tracks this), and each Object is also
385 * linked into a global Objects list. Objects may include a Trace
386 * list for tracking erasures. */
387 typedef struct object *ObjectPtr;
388 typedef struct object {
389 ObjectPtr next, prev;
393 int count; /* reference count */
394 Bool active; /* Object is in use */
404 /* Trajectory is a segment of juggling action. A list of Trajectories
405 * defines the juggling performance. The Trajectory list goes through
406 * multiple processing steps to convert it from basic juggling
407 * notation into rendering data. */
409 typedef struct trajectory *TrajectoryPtr;
410 typedef struct trajectory {
411 TrajectoryPtr prev, next; /* for building list */
412 TrajectoryStatus status;
430 TrajectoryPtr balllink;
431 TrajectoryPtr handlink;
434 double cx; /* Moving juggler */
435 double x, y; /* current position */
436 double dx, dy; /* initial velocity */
440 unsigned long start, finish;
454 const char * pattern;
458 /* List of popular patterns, in any order */
459 /* Patterns should be given in Adam notation so the generator can
460 concatenate them safely. Null descriptions are ok. Height
461 notation will be displayed automatically. */
462 /* Can't const this because it is qsorted. This *should* be reentrant,
464 static /*const*/ patternstruct portfolio[] = {
465 {"[+2 1]", /* +3 1 */ "Typical 2 ball juggler"},
466 {"[2 0]", /* 4 0 */ "2 in 1 hand"},
467 {"[2 0 1]", /* 5 0 1 */},
468 {"[+2 0 +2 0 0]" /* +5 0 +5 0 0 */},
469 {"[+2 0 1 2 2]", /* +4 0 1 2 3 */},
470 {"[2 0 1 1]", /* 6 0 1 1 */},
472 {"[3]", /* 3 */ "3 cascade"},
473 {"[+3]", /* +3 */ "reverse 3 cascade"},
474 {"[=3]", /* =3 */ "cascade 3 under arm"},
475 {"[&3]", /* &3 */ "cascade 3 catching under arm"},
476 {"[_3]", /* _3 */ "bouncing 3 cascade"},
477 {"[+3 x3 =3]", /* +3 x3 =3 */ "Mill's mess"},
478 {"[3 2 1]", /* 5 3 1" */},
479 {"[3 3 1]", /* 4 4 1" */},
480 {"[3 1 2]", /* 6 1 2 */ "See-saw"},
481 {"[=3 3 1 2]", /* =4 5 1 2 */},
482 {"[=3 2 2 3 1 2]", /* =6 2 2 5 1 2 */ "=4 5 1 2 stretched"},
483 {"[+3 3 1 3]", /* +4 4 1 3 */ "anemic shower box"},
484 {"[3 3 1]", /* 4 4 1 */},
485 {"[+3 2 3]", /* +4 2 3 */},
486 {"[+3 1]", /* +5 1 */ "3 shower"},
487 {"[_3 1]", /* _5 1 */ "bouncing 3 shower"},
488 {"[3 0 3 0 3]", /* 5 0 5 0 5 */ "shake 3 out of 5"},
489 {"[3 3 3 0 0]", /* 5 5 5 0 0 */ "flash 3 out of 5"},
490 {"[3 3 0]", /* 4 5 0 */ "complete waste of a 5 ball juggler"},
491 {"[3 3 3 0 0 0 0]", /* 7 7 7 0 0 0 0 */ "3 flash"},
492 {"[+3 0 +3 0 +3 0 0]", /* +7 0 +7 0 +7 0 0 */},
493 {"[3 2 2 0 3 2 0 2 3 0 2 2 0]", /* 7 3 3 0 7 3 0 3 7 0 3 3 0 */},
494 {"[3 0 2 0]", /* 8 0 4 0 */},
495 {"[_3 2 1]", /* _5 3 1 */},
496 {"[_3 0 1]", /* _8 0 1 */},
497 {"[1 _3 1 _3 0 1 _3 0]", /* 1 _7 1 _7 0 1 _7 0 */},
498 {"[_3 2 1 _3 1 2 1]", /* _6 3 1 _6 1 3 1 */},
500 {"[4]", /* 4 */ "4 cascade"},
501 {"[+4 3]", /* +5 3 */ "4 ball half shower"},
502 {"[4 4 2]", /* 5 5 2 */},
503 {"[+4 4 4 +4]", /* +4 4 4 +4 */ "4 columns"},
504 {"[+4 3 +4]", /* +5 3 +4 */},
505 {"[4 3 4 4]", /* 5 3 4 4 */},
506 {"[4 3 3 4]", /* 6 3 3 4 */},
507 {"[4 3 2 4", /* 6 4 2 4 */},
508 {"[+4 1]", /* +7 1 */ "4 shower"},
509 {"[4 4 4 4 0]", /* 5 5 5 5 0 */ "learning 5"},
510 {"[+4 x4 =4]", /* +4 x4 =4 */ "Mill's mess for 4"},
511 {"[+4 2 1 3]", /* +9 3 1 3 */},
512 {"[4 4 1 4 1 4]", /* 6 6 1 5 1 5, by Allen Knutson */},
513 {"[_4 _4 _4 1 _4 1]", /* _5 _6 _6 1 _5 1 */},
514 {"[_4 3 3]", /* _6 3 3 */},
515 {"[_4 3 1]", /* _7 4 1 */},
516 {"[_4 2 1]", /* _8 3 1 */},
517 {"[_4 3 3 3 0]", /* _8 4 4 4 0 */},
518 {"[_4 1 3 1]", /* _9 1 5 1 */},
519 {"[_4 1 3 1 2]", /* _10 1 6 1 2 */},
521 {"[5]", /* 5 */ "5 cascade"},
522 {"[_5 _5 _5 _5 _5 5 5 5 5 5]", /* _5 _5 _5 _5 _5 5 5 5 5 5 */},
523 {"[+5 x5 =5]", /* +5 x5 =5 */ "Mill's mess for 5"},
524 {"[5 4 4]", /* 7 4 4 */},
525 {"[_5 4 4]", /* _7 4 4 */},
526 {"[1 2 3 4 5 5 5 5 5]", /* 1 2 3 4 5 6 7 8 9 */ "5 ramp"},
527 {"[5 4 5 3 1]", /* 8 5 7 4 1, by Allen Knutson */},
528 {"[_5 4 1 +4]", /* _9 5 1 5 */},
529 {"[_5 4 +4 +4]", /* _8 4 +4 +4 */},
530 {"[_5 4 4 4 1]", /* _9 5 5 5 1 */},
532 {"[_5 4 4 +4 4 0]", /*_10 5 5 +5 5 0 */},
534 {"[6]", /* 6 */ "6 cascade"},
535 {"[+6 5]", /* +7 5 */},
536 {"[6 4]", /* 8 4 */},
537 {"[+6 3]", /* +9 3 */},
538 {"[6 5 4 4]", /* 9 7 4 4 */},
539 {"[+6 5 5 5]", /* +9 5 5 5 */},
540 {"[6 0 6]", /* 9 0 9 */},
541 {"[_6 0 _6]", /* _9 0 _9 */},
543 {"[_7]", /* _7 */ "bouncing 7 cascade"},
544 {"[7]", /* 7 */ "7 cascade"},
545 {"[7 6 6 6 6]", /* 11 6 6 6 6 */ "Gatto's High Throw"},
551 typedef struct { int start; int number; } PatternIndex;
553 struct patternindex {
556 PatternIndex index[countof(portfolio)];
560 /* Jugglestruct: per-screen global data. The master Object
561 * and Trajectory lists are anchored here. */
563 GLXContext *glx_context;
565 trackball_state *trackball;
576 time_t begintime; /* should make 'time' usable for at least 48 days
577 on a 32-bit machine */
578 unsigned long time; /* millisecond timer*/
581 struct patternindex patternindex;
582 texture_font_data *font_data;
585 static jugglestruct *juggles = (jugglestruct *) NULL;
591 #define DUP_OBJECT(n, t) { \
592 (n)->object = (t)->object; \
593 if((n)->object != NULL) (n)->object->count++; \
596 /* t must point to an existing element. t must not be an
597 expression ending ->next or ->prev */
598 #define REMOVE(t) { \
599 (t)->next->prev = (t)->prev; \
600 (t)->prev->next = (t)->next; \
604 /* t receives element to be created and added to the list. ot must
605 point to an existing element or be identical to t to start a new
606 list. Applicable to Trajectories, Objects and Traces. */
607 #define ADD_ELEMENT(type, t, ot) \
608 if (((t) = (type*)calloc(1,sizeof(type))) != NULL) { \
609 (t)->next = (ot)->next; \
612 (t)->next->prev = (t); \
616 object_destroy(Object* o)
618 if(o->trace != NULL) {
619 while(o->trace->next != o->trace) {
620 Trace *s = o->trace->next;
621 REMOVE(s); /* Don't eliminate 's' */
629 trajectory_destroy(Trajectory *t) {
630 if(t->name != NULL) free(t->name);
631 if(t->pattern != NULL) free(t->pattern);
632 /* Reduce object link count and call destructor if necessary */
633 if(t->object != NULL && --t->object->count < 1 && t->object->tracelen == 0) {
634 object_destroy(t->object);
636 REMOVE(t); /* Unlink and free */
640 free_juggle(ModeInfo *mi) {
641 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
643 if (sp->head != NULL) {
644 while (sp->head->next != sp->head) {
645 trajectory_destroy(sp->head->next);
648 sp->head = (Trajectory *) NULL;
650 if(sp->objects != NULL) {
651 while (sp->objects->next != sp->objects) {
652 object_destroy(sp->objects->next);
655 sp->objects = (Object*)NULL;
657 if(sp->pattern != NULL) {
664 add_throw(ModeInfo *mi, char type, int h, Notation n, const char* name)
666 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
669 ADD_ELEMENT(Trajectory, t, sp->head->prev);
670 if(t == NULL){ /* Out of Memory */
676 t->name = strdup(name);
689 /* add a Thratch to the performance */
691 program(ModeInfo *mi, const char *patn, const char *name, int cycles)
698 if (MI_IS_VERBOSE(mi)) {
699 (void) fprintf(stderr, "juggle[%d]: Programmed: %s x %d\n",
700 MI_SCREEN(mi), (name == NULL) ? patn : name, cycles);
703 for(w=i=0; i < cycles; i++, w++) { /* repeat until at least "cycles" throws
704 have been programmed */
705 /* title is the pattern name to be supplied to the first throw of
706 a sequence. If no name if given, use an empty title so that
707 the sequences are still delimited. */
708 const char *title = (name != NULL)? name : "";
713 for(p=patn; *p; p++) {
714 if (*p >= '0' && *p <='9') {
716 h = 10*h + (*p - '0');
718 Notation nn = notation;
720 case '[': /* begin Adam notation */
723 case '-': /* Inside throw */
726 case '+': /* Outside throw */
727 case '=': /* Cross throw */
728 case '&': /* Cross catch */
729 case 'x': /* Cross throw and catch */
730 case '_': /* Bounce */
731 case 'k': /* Kickup */
734 case '*': /* Lose ball */
738 case ']': /* end Adam notation */
744 if (!add_throw(mi, type, h, notation, title))
754 if(w == 0) { /* Only warn on first pass */
755 (void) fprintf(stderr,
756 "juggle[%d]: Unexpected pattern instruction: '%c'\n",
763 if (seen) { /* end of sequence */
764 if (!add_throw(mi, type, h, notation, title))
778 [ 3 3 1 3 4 2 3 1 3 3 4 0 2 1 ]
780 4 4 1 3 12 2 4 1 4 4 13 0 3 1
783 #define BOUNCEOVER 10
787 /* Convert Adam notation into heights */
789 adam(jugglestruct *sp)
792 for(t = sp->head->next; t != sp->head; t = t->next) {
793 if (t->status == ATCH) {
796 for(p = t->next; a > 0; p = p->next) {
798 t->height = -9; /* Indicate end of processing for name() */
801 if (p->status != ATCH || p->adam < 0 || p->adam>= a) {
806 if(t->height > BOUNCEOVER && t->posn == ' '){
807 t->posn = '_'; /* high defaults can be bounced */
808 } else if(t->height < 3 && t->posn == '_') {
809 t->posn = ' '; /* Can't bounce short throws. */
811 if(t->height < KICKMIN && t->posn == 'k'){
812 t->posn = ' '; /* Can't kick short throws */
814 if(t->height > THROWMAX){
815 t->posn = 'k'; /* Use kicks for ridiculously high throws */
822 /* Discover converted heights and update the sequence title */
824 name(jugglestruct *sp)
829 for(t = sp->head->next; t != sp->head; t = t->next) {
830 if (t->status == THRATCH && t->name != NULL) {
832 for(p = t; p == t || p->name == NULL; p = p->next) {
833 if(p == sp->head || p->height < 0) { /* end of reliable data */
837 b += sprintf(b, " %d", p->height);
839 b += sprintf(b, " %c%d", p->posn, p->height);
841 if(b - buffer > 500) break; /* otherwise this could eventually
842 overflow. It'll be too big to
846 (void) sprintf(b, ", %s", t->name);
848 free(t->name); /* Don't need name any more, it's been converted
851 if(t->pattern != NULL) free(t->pattern);
852 t->pattern = strdup(buffer);
857 /* Split Thratch notation into explicit throws and catches.
858 Usually Catch follows Throw in same hand, but take care of special
861 /* ..n1.. -> .. LTn RT1 LC RC .. */
862 /* ..nm.. -> .. LTn LC RTm RC .. */
867 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
868 Trajectory *t, *nt, *p;
869 Hand hand = (LRAND() & 1) ? RIGHT : LEFT;
871 for (t = sp->head->next; t != sp->head; t = t->next) {
872 if (t->status > THRATCH) {
874 } else if (t->status == THRATCH) {
877 /* plausibility check */
878 if (t->height <= 2 && t->posn == '_') {
879 t->posn = ' '; /* no short bounces */
881 if (t->height <= 1 && (t->posn == '=' || t->posn == '&')) {
882 t->posn = ' '; /* 1's need close catches */
887 case ' ': posn = '-'; t->posn = '+'; break;
888 case '+': posn = '+'; t->posn = '-'; break;
889 case '=': posn = '='; t->posn = '+'; break;
890 case '&': posn = '+'; t->posn = '='; break;
891 case 'x': posn = '='; t->posn = '='; break;
892 case '_': posn = '_'; t->posn = '-'; break;
893 case 'k': posn = 'k'; t->posn = 'k'; break;
895 (void) fprintf(stderr, "juggle: unexpected posn %c\n", t->posn);
898 hand = (Hand) ((hand + 1) % 2);
903 if (t->height == 1 && p != sp->head) {
904 p = p->prev; /* '1's are thrown earlier than usual */
910 ADD_ELEMENT(Trajectory, nt, p);
918 nt->height = t->height;
928 choose_object(void) {
931 o = (ObjType)NRAND((ObjType)NUM_OBJECT_TYPES);
932 if(balls && o == BALL) break;
933 if(clubs && o == CLUB) break;
934 if(torches && o == TORCH) break;
935 if(knives && o == KNIFE) break;
936 if(rings && o == RING) break;
937 if(bballs && o == BBALLS) break;
942 /* Connnect up throws and catches to figure out which ball goes where.
943 Do the same with the juggler's hands. */
948 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
951 for (t = sp->head->next; t != sp->head; t = t->next) {
952 if (t->status == ACTION) {
953 if (t->action == THROW) {
954 if (t->type == Empty) {
955 /* Create new Object */
956 ADD_ELEMENT(Object, t->object, sp->objects);
957 t->object->count = 1;
958 t->object->tracelen = 0;
959 t->object->active = False;
960 /* Initialise object's circular trace list */
961 ADD_ELEMENT(Trace, t->object->trace, t->object->trace);
963 if (MI_NPIXELS(mi) > 2) {
964 t->object->color = 1 + NRAND(MI_NPIXELS(mi) - 2);
967 t->object->color = 1;
969 t->object->color = 0;
973 /* Small chance of picking a random object instead of the
975 if(NRAND(OBJMIXPROB) == 0) {
976 t->object->type = choose_object();
978 t->object->type = sp->objtypes;
981 /* Check to see if we need trails for this object */
982 if(tail < ObjectDefs[t->object->type].mintrail) {
983 t->object->tail = ObjectDefs[t->object->type].mintrail;
985 t->object->tail = tail;
989 /* Balls can change divisions at each throw */
990 /* no, that looks stupid. -jwz */
991 if (t->divisions < 1)
992 t->divisions = 2 * (NRAND(2) + 1);
994 /* search forward for next catch in this hand */
995 for (p = t->next; t->handlink == NULL; p = p->next) {
996 if(p->status < ACTION || p == sp->head) return;
997 if (p->action == CATCH) {
998 if (t->handlink == NULL && p->hand == t->hand) {
1004 if (t->height > 0) {
1007 /* search forward for next ball catch */
1008 for (p = t->next; t->balllink == NULL; p = p->next) {
1009 if(p->status < ACTION || p == sp->head) {
1013 if (p->action == CATCH) {
1014 if (t->balllink == NULL && --h < 1) { /* caught */
1015 t->balllink = p; /* complete trajectory */
1017 if (p->type == Full) {
1018 (void) fprintf(stderr, "juggle[%d]: Dropped %d\n",
1019 MI_SCREEN(mi), t->object->color);
1023 DUP_OBJECT(p, t); /* accept catch */
1024 p->angle = t->angle;
1025 p->divisions = t->divisions;
1030 t->type = Empty; /* thrown */
1031 } else if (t->action == CATCH) {
1032 /* search forward for next throw from this hand */
1033 for (p = t->next; t->handlink == NULL; p = p->next) {
1034 if(p->status < ACTION || p == sp->head) return;
1035 if (p->action == THROW && p->hand == t->hand) {
1036 p->type = t->type; /* pass ball */
1037 DUP_OBJECT(p, t); /* pass object */
1038 p->divisions = t->divisions;
1043 t->status = LINKEDACTION;
1048 /* Clap when both hands are empty */
1050 clap(jugglestruct *sp)
1053 for (t = sp->head->next; t != sp->head; t = t->next) {
1054 if (t->status == LINKEDACTION &&
1055 t->action == CATCH &&
1057 t->handlink != NULL &&
1058 t->handlink->height == 0) { /* Completely idle hand */
1060 for (p = t->next; p != sp->head; p = p->next) {
1061 if (p->status == LINKEDACTION &&
1062 p->action == CATCH &&
1063 p->hand != t->hand) { /* Next catch other hand */
1064 if(p->type == Empty &&
1065 p->handlink != NULL &&
1066 p->handlink->height == 0) { /* Also completely idle */
1068 t->handlink->posn = '^'; /* Move first hand's empty throw */
1069 p->posn = '^'; /* to meet second hand's empty
1073 break; /* Only need first catch */
1080 #define CUBIC(s, t) ((((s).a * (t) + (s).b) * (t) + (s).c) * (t) + (s).d)
1082 /* Compute single spline from x0 with velocity dx0 at time t0 to x1
1083 with velocity dx1 at time t1 */
1085 makeSpline(double x0, double dx0, int t0, double x1, double dx1, int t1)
1094 a = ((dx0 + dx1)*t10 - 2*x10) / (t10*t10*t10);
1095 b = (3*x10 - (2*dx0 + dx1)*t10) / (t10*t10);
1100 s.c = (3*a*t0 - 2*b)*t0 + c;
1101 s.d = ((-a*t0 + b)*t0 - c)*t0 +d;
1105 /* Compute a pair of splines. s1 goes from x0 vith velocity dx0 at
1106 time t0 to x1 at time t1. s2 goes from x1 at time t1 to x2 with
1107 velocity dx2 at time t2. The arrival and departure velocities at
1108 x1, t1 must be the same. */
1110 makeSplinePair(Spline *s1, Spline *s2,
1111 double x0, double dx0, int t0,
1113 double x2, double dx2, int t2)
1115 double x10, x21, t21, t10, t20, dx1;
1121 dx1 = (3*x10*t21*t21 + 3*x21*t10*t10 + 3*dx0*t10*t21*t21
1122 - dx2*t10*t10*t21 - 4*dx0*t10*t21*t21) /
1124 *s1 = makeSpline(x0, dx0, t0, x1, dx1, t1);
1125 *s2 = makeSpline(x1, dx1, t1, x2, dx2, t2);
1129 /* Compute a Ballistic path in a pair of degenerate splines. sx goes
1130 from x at time t at constant velocity dx. sy goes from y at time t
1131 with velocity dy and constant acceleration g. */
1133 makeParabola(Trajectory *n,
1134 double x, double dx, double y, double dy, double g)
1136 double t = (double)n->start;
1140 n->xp.d = -dx*t + x;
1143 n->yp.c = -g*t + dy;
1144 n->yp.d = g/2*t*t - dy*t + y;
1150 #define SX 25 /* Shoulder Width */
1152 /* Convert hand position symbols into actual time/space coordinates */
1154 positions(jugglestruct *sp)
1157 unsigned long now = sp->time; /* Make sure we're not lost in the past */
1158 for (t = sp->head->next; t != sp->head; t = t->next) {
1159 if (t->status >= PTHRATCH) {
1161 } else if (t->status == ACTION || t->status == LINKEDACTION) {
1162 /* Allow ACTIONs to be annotated, but we won't mark them ready
1163 for the next stage */
1170 if (t->action == CATCH) { /* Throw-to-catch */
1171 if (t->type == Empty) {
1172 now += (int) THROW_NULL_INTERVAL; /* failed catch is short */
1173 } else { /* successful catch */
1174 now += (int)(THROW_CATCH_INTERVAL);
1176 } else { /* Catch-to-throw */
1177 if(t->object != NULL) {
1178 now += (int) (CATCH_THROW_INTERVAL *
1179 ObjectDefs[t->object->type].weight);
1181 now += (int) (CATCH_THROW_INTERVAL);
1187 else /* Concatenated performances may need clock resync */
1195 /* Add room for the handle */
1196 if(t->action == CATCH && t->object != NULL)
1197 yo -= ObjectDefs[t->object->type].handle;
1200 case '-': xo = sx - pose; break;
1203 case '+': xo = sx + pose; break;
1205 case '=': xo = - sx - pose; yo += pose; break;
1206 case '^': xo = 0; yo += pose*2; break; /* clap */
1208 (void) fprintf(stderr, "juggle: unexpected posn %c\n", t->posn);
1212 #ifdef _2DSpinsDontWorkIn3D
1213 t->angle = (((t->hand == LEFT) ^
1214 (t->posn == '+' || t->posn == '_' || t->posn == 'k' ))?
1220 t->x = t->cx + ((t->hand == LEFT) ? xo : -xo);
1223 /* Only mark complete if it was already linked */
1224 if(t->status == LINKEDACTION) {
1225 t->status = PTHRATCH;
1232 /* Private physics functions */
1234 /* Compute the spin-rate for a trajectory. Different types of throw
1235 (eg, regular thows, bounces, kicks, etc) have different spin
1238 type = type of object
1239 h = trajectory of throwing hand (throws), or next throwing hand (catches)
1240 old = earlier spin to consider
1241 dt = time span of this trajectory
1242 height = height of ball throw or 0 if based on old spin
1243 turns = full club turns required during this operation
1244 togo = partial club turns required to match hands
1247 spinrate(ObjType type, Trajectory *h, double old, double dt,
1248 int height, int turns, double togo)
1250 #ifdef _2DSpinsDontWorkIn3D
1251 const int dir = (h->hand == LEFT) ^ (h->posn == '+')? -1 : 1;
1256 if(ObjectDefs[type].handle != 0) { /* Clubs */
1257 return (dir * turns * 2 * M_PI + togo) / dt;
1258 } else if(height == 0) { /* Balls already spinning */
1260 } else { /* Balls */
1261 return dir * NRAND(height*10)/20/ObjectDefs[type].weight * 2 * M_PI / dt;
1266 /* compute the angle at the end of a spinning trajectory */
1268 end_spin(Trajectory *t)
1270 return t->angle + t->spin * (t->finish - t->start);
1273 /* Sets the initial angle of the catch following hand movement t to
1274 the final angle of the throw n. Also sets the angle of the
1275 subsequent throw to the same angle plus half a turn. */
1277 match_spins_on_catch(Trajectory *t, Trajectory *n)
1279 if(ObjectDefs[t->balllink->object->type].handle == 0) {
1280 t->balllink->angle = end_spin(n);
1281 if(t->balllink->handlink != NULL) {
1282 #ifdef _2DSpinsDontWorkIn3D
1283 t->balllink->handlink->angle = t->balllink->angle + M_PI;
1285 t->balllink->handlink->angle = t->balllink->angle;
1292 find_bounce(jugglestruct *sp,
1293 double yo, double yf, double yc, double tc, double cor)
1295 double tb, i, dy = 0;
1296 const double e = 1; /* permissible error in yc */
1300 yt = height at catch time after one bounce
1301 one or three roots according to timing
1302 find one by interval bisection
1305 for(i = tc / 2; i > 0.0001; i/=2){
1308 (void) fprintf(stderr, "juggle: bounce div by zero!\n");
1311 dy = (yf - yo)/tb + sp->Gr/2*tb;
1313 yt = -cor*dy*dt + sp->Gr/2*dt*dt + yf;
1316 }else if(yt > yc - e){
1322 if(dy*THROW_CATCH_INTERVAL < -200) { /* bounce too hard */
1329 new_predictor(const Trajectory *t, int start, int finish, double angle)
1332 ADD_ELEMENT(Trajectory, n, t->prev);
1337 n->divisions = t->divisions;
1339 n->status = PREDICTOR;
1347 /* Turn abstract timings into physically appropriate object trajectories. */
1349 projectile(jugglestruct *sp)
1352 const int yf = 0; /* Floor height */
1354 for (t = sp->head->next; t != sp->head; t = t->next) {
1355 if (t->status != PTHRATCH || t->action != THROW) {
1357 } else if (t->balllink == NULL) { /* Zero Throw */
1358 t->status = BPREDICTOR;
1359 } else if (t->balllink->handlink == NULL) { /* Incomplete */
1361 } else if(t->balllink == t->handlink) {
1362 /* '2' height - hold on to ball. Don't need to consider
1363 flourishes, 'hands' will do that automatically anyway */
1366 /* Zero spin to avoid wrist injuries */
1368 match_spins_on_catch(t, t);
1370 t->status = BPREDICTOR;
1373 if (t->posn == '_') { /* Bounce once */
1375 const int tb = t->start +
1376 find_bounce(sp, t->y, (double) yf, t->balllink->y,
1377 (double) (t->balllink->start - t->start),
1378 ObjectDefs[t->object->type].cor);
1380 if(tb < t->start) { /* bounce too hard */
1381 t->posn = '+'; /* Use regular throw */
1383 Trajectory *n; /* First (throw) trajectory. */
1384 double dt; /* Time span of a trajectory */
1385 double dy; /* Distance span of a follow-on trajectory.
1386 First trajectory uses t->dy */
1387 /* dx is constant across both trajectories */
1388 t->dx = (t->balllink->x - t->x) / (t->balllink->start - t->start);
1390 { /* ball follows parabola down */
1391 n = new_predictor(t, t->start, tb, t->angle);
1392 if(n == NULL) return False;
1393 dt = n->finish - n->start;
1394 /* Ball rate 4, no flight or matching club turns */
1395 n->spin = spinrate(t->object->type, t, 0.0, dt, 4, 0, 0.0);
1396 t->dy = (yf - t->y)/dt - sp->Gr/2*dt;
1397 makeParabola(n, t->x, t->dx, t->y, t->dy, sp->Gr);
1400 { /* ball follows parabola up */
1401 Trajectory *m = new_predictor(t, n->finish, t->balllink->start,
1403 if(m == NULL) return False;
1404 dt = m->finish - m->start;
1405 /* Use previous ball rate, no flight club turns */
1406 m->spin = spinrate(t->object->type, t, n->spin, dt, 0, 0,
1407 t->balllink->angle - m->angle);
1408 match_spins_on_catch(t, m);
1409 dy = (t->balllink->y - yf)/dt - sp->Gr/2 * dt;
1410 makeParabola(m, t->balllink->x - t->dx * dt,
1411 t->dx, (double) yf, dy, sp->Gr);
1414 t->status = BPREDICTOR;
1417 } else if (t->posn == 'k') { /* Drop & Kick */
1418 Trajectory *n; /* First (drop) trajectory. */
1419 Trajectory *o; /* Second (rest) trajectory */
1420 Trajectory *m; /* Third (kick) trajectory */
1421 const int td = t->start + 2*THROW_CATCH_INTERVAL; /* Drop time */
1422 const int tk = t->balllink->start - 5*THROW_CATCH_INTERVAL; /* Kick */
1425 { /* Fall to ground */
1426 n = new_predictor(t, t->start, td, t->angle);
1427 if(n == NULL) return False;
1428 dt = n->finish - n->start;
1429 /* Ball spin rate 4, no flight club turns */
1430 n->spin = spinrate(t->object->type, t, 0.0, dt, 4, 0,
1431 t->balllink->angle - n->angle);
1432 t->dx = (t->balllink->x - t->x) / dt;
1433 t->dy = (yf - t->y)/dt - sp->Gr/2*dt;
1434 makeParabola(n, t->x, t->dx, t->y, t->dy, sp->Gr);
1437 { /* Rest on ground */
1438 o = new_predictor(t, n->finish, tk, end_spin(n));
1439 if(o == NULL) return False;
1441 makeParabola(o, t->balllink->x, 0.0, (double) yf, 0.0, 0.0);
1446 m = new_predictor(t, o->finish, t->balllink->start, end_spin(o));
1447 if(m == NULL) return False;
1448 dt = m->finish - m->start;
1449 /* Match receiving hand, ball rate 4, one flight club turn */
1450 m->spin = spinrate(t->object->type, t->balllink->handlink, 0.0, dt,
1451 4, 1, t->balllink->angle - m->angle);
1452 match_spins_on_catch(t, m);
1453 dy = (t->balllink->y - yf)/dt - sp->Gr/2 * dt;
1454 makeParabola(m, t->balllink->x, 0.0, (double) yf, dy, sp->Gr);
1457 t->status = BPREDICTOR;
1461 /* Regular flight, no bounce */
1462 { /* ball follows parabola */
1464 Trajectory *n = new_predictor(t, t->start,
1465 t->balllink->start, t->angle);
1466 if(n == NULL) return False;
1467 dt = t->balllink->start - t->start;
1469 n->spin = spinrate(t->object->type, t, 0.0, dt, t->height, t->height/2,
1470 t->balllink->angle - n->angle);
1471 match_spins_on_catch(t, n);
1472 t->dx = (t->balllink->x - t->x) / dt;
1473 t->dy = (t->balllink->y - t->y) / dt - sp->Gr/2 * dt;
1474 makeParabola(n, t->x, t->dx, t->y, t->dy, sp->Gr);
1477 t->status = BPREDICTOR;
1483 /* Turn abstract hand motions into cubic splines. */
1485 hands(jugglestruct *sp)
1487 Trajectory *t, *u, *v;
1489 for (t = sp->head->next; t != sp->head; t = t->next) {
1490 /* no throw => no velocity */
1491 if (t->status != BPREDICTOR) {
1496 if (u == NULL) { /* no next catch */
1500 if (v == NULL) { /* no next throw */
1504 /* double spline takes hand from throw, thru catch, to
1507 t->finish = u->start;
1508 t->status = PREDICTOR;
1510 u->finish = v->start;
1511 u->status = PREDICTOR;
1514 /* FIXME: These adjustments leave a small glitch when alternating
1515 balls and clubs. Just hope no-one notices. :-) */
1517 /* make sure empty hand spin matches the thrown object in case it
1520 t->spin = ((t->hand == LEFT)? -1 : 1 ) *
1521 fabs((u->angle - t->angle)/(u->start - t->start));
1523 u->spin = ((v->hand == LEFT) ^ (v->posn == '+')? -1 : 1 ) *
1524 fabs((v->angle - u->angle)/(v->start - u->start));
1526 (void) makeSplinePair(&t->xp, &u->xp,
1527 t->x, t->dx, t->start,
1529 v->x, v->dx, v->start);
1530 (void) makeSplinePair(&t->yp, &u->yp,
1531 t->y, t->dy, t->start,
1533 v->y, v->dy, v->start);
1535 t->status = PREDICTOR;
1539 /* Given target x, y find_elbow puts hand at target if possible,
1540 * otherwise makes hand point to the target */
1542 find_elbow(int armlength, DXPoint *h, DXPoint *e, DXPoint *p, DXPoint *s,
1546 double x = p->x - s->x;
1547 double y = p->y - s->y;
1548 h2 = x*x + y*y + z*z;
1549 if (h2 > 4 * armlength * armlength) {
1550 t = armlength/sqrt(h2);
1553 h->x = 2 * t * x + s->x;
1554 h->y = 2 * t * y + s->y;
1556 r = sqrt((double)(x*x + z*z));
1557 t = sqrt(4 * armlength * armlength / h2 - 1);
1558 e->x = x*(1 + y*t/r)/2 + s->x;
1559 e->y = (y - r*t)/2 + s->y;
1566 /* NOTE: returned x, y adjusted for arm reach */
1568 reach_arm(ModeInfo * mi, Hand side, DXPoint *p)
1570 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
1572 find_elbow(40, &h, &e, p, &sp->arm[1][side][SHOULDER], 25);
1573 *p = sp->arm[1][side][HAND] = h;
1574 sp->arm[1][side][ELBOW] = e;
1578 /* dumps a human-readable rendition of the current state of the juggle
1579 pipeline to stderr for debugging */
1581 dump(jugglestruct *sp)
1584 for (t = sp->head->next; t != sp->head; t = t->next) {
1585 switch (t->status) {
1587 (void) fprintf(stderr, "%p a %c%d\n", (void*)t, t->posn, t->adam);
1590 (void) fprintf(stderr, "%p T %c%d %s\n", (void*)t, t->posn, t->height,
1591 t->pattern == NULL?"":t->pattern);
1594 if (t->action == CATCH)
1595 (void) fprintf(stderr, "%p A %c%cC\n",
1597 t->hand ? 'R' : 'L');
1599 (void) fprintf(stderr, "%p A %c%c%c%d\n",
1601 t->hand ? 'R' : 'L',
1602 (t->action == THROW)?'T':'N',
1606 (void) fprintf(stderr, "%p L %c%c%c%d %d %p %p\n",
1609 (t->action == THROW)?'T':(t->action == CATCH?'C':'N'),
1610 t->height, t->object == NULL?0:t->object->color,
1611 (void*)t->handlink, (void*)t->balllink);
1614 (void) fprintf(stderr, "%p O %c%c%c%d %d %2d %6lu %6lu\n",
1617 (t->action == THROW)?'T':(t->action == CATCH?'C':'N'),
1618 t->height, t->type, t->object == NULL?0:t->object->color,
1619 t->start, t->finish);
1622 (void) fprintf(stderr, "%p B %c %2d %6lu %6lu %g\n",
1623 (void*)t, t->type == Ball?'b':t->type == Empty?'e':'f',
1624 t->object == NULL?0:t->object->color,
1625 t->start, t->finish, t->yp.c);
1628 (void) fprintf(stderr, "%p P %c %2d %6lu %6lu %g\n",
1629 (void*)t, t->type == Ball?'b':t->type == Empty?'e':'f',
1630 t->object == NULL?0:t->object->color,
1631 t->start, t->finish, t->yp.c);
1634 (void) fprintf(stderr, "%p: status %d not implemented\n",
1635 (void*)t, t->status);
1639 (void) fprintf(stderr, "---\n");
1643 static int get_num_balls(const char *j)
1649 for (p = j; *p; p++) {
1650 if (*p >= '0' && *p <='9') { /* digit */
1651 h = 10*h + (*p - '0');
1663 compare_num_balls(const void *p1, const void *p2)
1666 i = get_num_balls(((patternstruct*)p1)->pattern);
1667 j = get_num_balls(((patternstruct*)p2)->pattern);
1678 /**************************************************************************
1679 * Rendering Functions *
1681 **************************************************************************/
1684 show_arms(ModeInfo * mi)
1687 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
1690 XPoint a[countof(sp->arm[0][0])];
1696 glFrontFace(GL_CCW);
1699 for(side = LEFT; side <= RIGHT; side = (Hand)((int)side + 1)) {
1700 /* Translate into device coords */
1701 for(i = 0; i < countof(a); i++) {
1702 a[i].x = (short)(SCENE_WIDTH/2 + sp->arm[j][side][i].x*sp->scale);
1703 a[i].y = (short)(SCENE_HEIGHT - sp->arm[j][side][i].y*sp->scale);
1705 sp->arm[0][side][i] = sp->arm[1][side][i];
1708 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1711 polys += tube (a[2].x - (side == LEFT ? soffx : -soffx), a[2].y + soffy, 0,
1712 a[1].x, a[1].y, ARMLENGTH/2,
1713 thickness, 0, slices,
1714 True, True, MI_IS_WIREFRAME(mi));
1717 polys += tube (a[1].x, a[1].y, ARMLENGTH/2,
1718 a[0].x, a[0].y, ARMLENGTH,
1719 thickness * 0.8, 0, slices,
1720 True, True, MI_IS_WIREFRAME(mi));
1722 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
1726 glTranslatef (a[2].x - (side == LEFT ? soffx : -soffx),
1730 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1735 glTranslatef (a[1].x, a[1].y, ARMLENGTH/2);
1737 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1742 glTranslatef (a[0].x, a[0].y, ARMLENGTH);
1744 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1752 show_figure(ModeInfo * mi, Bool init)
1755 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
1776 /* #### most of this is unused now */
1777 static const XPoint figure[] = {
1778 { 15, 70}, /* 0 Left Hip */
1779 { 0, 90}, /* 1 Waist */
1780 { SX, 130}, /* 2 Left Shoulder */
1781 {-SX, 130}, /* 3 Right Shoulder */
1782 {-15, 70}, /* 4 Right Hip */
1783 { 0, 130}, /* 5 Neck */
1784 { 0, 140}, /* 6 Chin */
1785 { SX, 0}, /* 7 Left Foot */
1786 {-SX, 0}, /* 8 Right Foot */
1787 {-17, 174}, /* 9 Head1 */
1788 { 17, 140}, /* 10 Head2 */
1790 XPoint a[countof(figure)];
1791 GLfloat gcolor[4] = { 1, 1, 1, 1 };
1793 /* Translate into device coords */
1794 for(i = 0; i < countof(figure); i++) {
1795 a[i].x = (short)(SCENE_WIDTH/2 + (sp->cx + figure[i].x)*sp->scale);
1796 a[i].y = (short)(SCENE_HEIGHT - figure[i].y*sp->scale);
1799 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor);
1801 glFrontFace(GL_CCW);
1804 GLfloat scale = ((GLfloat) a[10].x - a[9].x) / 2;
1809 glTranslatef(a[6].x, a[6].y - scale, 0);
1810 glScalef(scale, scale, scale);
1813 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1816 glScalef(scale, scale, scale);
1817 glTranslatef(0, 0.3, 0);
1819 glTranslatef(0, 0, 0.35);
1820 polys += tube (0, 0, 0,
1823 slices, True, True, MI_IS_WIREFRAME(mi));
1825 glScalef(0.9, 0.9, 1);
1826 polys += unit_sphere(2*slices, 2*slices, MI_IS_WIREFRAME(mi));
1830 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
1831 glTranslatef(0, 1.1, 0);
1834 glScalef(scale, scale, scale);
1835 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1839 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1840 glTranslatef(0, 1.1, 0);
1842 glScalef(0.9, 1.0, 0.9);
1843 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1847 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
1848 glTranslatef(0, 1.0, 0);
1851 glScalef(scale, scale, scale);
1852 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1856 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1857 glTranslatef(0, 0.8, 0);
1860 glScalef(scale, scale, scale);
1861 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1866 glTranslatef(0, 0.7, 0);
1868 for (i = -1; i <= 1; i += 2) {
1871 glRotatef (i*10, 0, 0, 1);
1872 glTranslatef(-i*0.65, 0, 0);
1875 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
1877 glScalef(scale, scale, scale);
1878 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1881 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1883 glTranslatef(0, 0.6, 0);
1884 polys += tube (0, 0, 0,
1887 slices, True, True, MI_IS_WIREFRAME(mi));
1891 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
1893 glTranslatef(0, 4.4, 0);
1895 glScalef(scale, scale, scale);
1896 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1900 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1902 glTranslatef(0, 4.7, 0);
1903 polys += tube (0, 0, 0,
1906 slices, True, True, MI_IS_WIREFRAME(mi));
1910 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
1912 glTranslatef(0, 9.7, 0);
1914 glScalef(scale, scale, scale);
1915 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1919 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1921 glRotatef (-i*10, 0, 0, 1);
1922 glTranslatef(-i*1.75, 9.7, 0.9);
1924 glScalef (0.4, 1, 1);
1925 polys += tube (0, 0, 0,
1928 slices*4, True, True, MI_IS_WIREFRAME(mi));
1937 sp->arm[1][LEFT][SHOULDER].x = sp->cx + figure[2].x;
1938 sp->arm[1][RIGHT][SHOULDER].x = sp->cx + figure[3].x;
1940 /* Initialise arms */
1942 for(i = 0; i < 2; i++){
1943 sp->arm[i][LEFT][SHOULDER].y = figure[2].y;
1944 sp->arm[i][LEFT][ELBOW].x = figure[2].x;
1945 sp->arm[i][LEFT][ELBOW].y = figure[1].y;
1946 sp->arm[i][LEFT][HAND].x = figure[0].x;
1947 sp->arm[i][LEFT][HAND].y = figure[1].y;
1948 sp->arm[i][RIGHT][SHOULDER].y = figure[3].y;
1949 sp->arm[i][RIGHT][ELBOW].x = figure[3].x;
1950 sp->arm[i][RIGHT][ELBOW].y = figure[1].y;
1951 sp->arm[i][RIGHT][HAND].x = figure[4].x;
1952 sp->arm[i][RIGHT][HAND].y = figure[1].y;
1958 typedef struct { GLfloat x, y, z; } XYZ;
1960 /* lifted from sphere.c */
1962 striped_unit_sphere (int stacks, int slices,
1964 GLfloat *color1, GLfloat *color2,
1969 double theta1, theta2, theta3;
1971 XYZ la = { 0, 0, 0 }, lb = { 0, 0, 0 };
1972 XYZ c = {0, 0, 0}; /* center */
1973 double r = 1.0; /* radius */
1974 int stacks2 = stacks * 2;
1981 if (slices < 4 || stacks < 2 || r <= 0)
1983 glBegin (GL_POINTS);
1984 glVertex3f (c.x, c.y, c.z);
1991 for (j = 0; j < stacks; j++)
1993 theta1 = j * (M_PI+M_PI) / stacks2 - M_PI_2;
1994 theta2 = (j + 1) * (M_PI+M_PI) / stacks2 - M_PI_2;
1996 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
1997 ((j == 0 || j == stacks-1 ||
1998 j % (stacks / (stripes+1)))
1999 ? color1 : color2));
2001 glBegin (wire_p ? GL_LINE_LOOP : GL_TRIANGLE_STRIP);
2002 for (i = 0; i <= slices; i++)
2004 theta3 = i * (M_PI+M_PI) / slices;
2006 if (wire_p && i != 0)
2008 glVertex3f (lb.x, lb.y, lb.z);
2009 glVertex3f (la.x, la.y, la.z);
2012 e.x = cos (theta2) * cos(theta3);
2014 e.z = cos (theta2) * sin(theta3);
2015 p.x = c.x + r * e.x;
2016 p.y = c.y + r * e.y;
2017 p.z = c.z + r * e.z;
2019 glNormal3f (e.x, e.y, e.z);
2020 glTexCoord2f (i / (double)slices,
2021 2*(j+1) / (double)stacks2);
2022 glVertex3f (p.x, p.y, p.z);
2025 e.x = cos(theta1) * cos(theta3);
2027 e.z = cos(theta1) * sin(theta3);
2028 p.x = c.x + r * e.x;
2029 p.y = c.y + r * e.y;
2030 p.z = c.z + r * e.z;
2032 glNormal3f (e.x, e.y, e.z);
2033 glTexCoord2f (i / (double)slices,
2034 2*j / (double)stacks2);
2035 glVertex3f (p.x, p.y, p.z);
2047 show_ball(ModeInfo *mi, unsigned long color, Trace *s)
2050 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2051 /*int offset = (int)(s->angle*64*180/M_PI);*/
2052 short x = (short)(SCENE_WIDTH/2 + s->x * sp->scale);
2053 short y = (short)(SCENE_HEIGHT - s->y * sp->scale);
2054 GLfloat gcolor1[4] = { 0, 0, 0, 1 };
2055 GLfloat gcolor2[4] = { 0, 0, 0, 1 };
2058 /* Avoid wrapping */
2059 if(s->y*sp->scale > SCENE_HEIGHT * 2) return 0;
2061 gcolor1[0] = mi->colors[color].red / 65536.0;
2062 gcolor1[1] = mi->colors[color].green / 65536.0;
2063 gcolor1[2] = mi->colors[color].blue / 65536.0;
2065 gcolor2[0] = gcolor1[0] / 3;
2066 gcolor2[1] = gcolor1[1] / 3;
2067 gcolor2[2] = gcolor1[2] / 3;
2069 glFrontFace(GL_CCW);
2072 GLfloat scale = BALLRADIUS;
2074 glTranslatef(x, y, 0);
2075 glScalef(scale, scale, scale);
2077 glRotatef (s->angle / M_PI*180, 1, 1, 0);
2079 polys += striped_unit_sphere (slices, slices, s->divisions,
2080 gcolor1, gcolor2, MI_IS_WIREFRAME(mi));
2087 show_europeanclub(ModeInfo *mi, unsigned long color, Trace *s)
2090 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2091 /*int offset = (int)(s->angle*64*180/M_PI);*/
2092 short x = (short)(SCENE_WIDTH/2 + s->x * sp->scale);
2093 short y = (short)(SCENE_HEIGHT - s->y * sp->scale);
2094 double radius = 12 * sp->scale;
2095 GLfloat gcolor1[4] = { 0, 0, 0, 1 };
2096 GLfloat gcolor2[4] = { 1, 1, 1, 1 };
2118 /* Avoid wrapping */
2119 if(s->y*sp->scale > SCENE_HEIGHT * 2) return 0;
2121 gcolor1[0] = mi->colors[color].red / 65536.0;
2122 gcolor1[1] = mi->colors[color].green / 65536.0;
2123 gcolor1[2] = mi->colors[color].blue / 65536.0;
2125 glFrontFace(GL_CCW);
2128 GLfloat scale = radius;
2130 glTranslatef(x, y, 0);
2131 glScalef(scale, scale, scale);
2133 glTranslatef (0, 0, 2); /* put end of handle in hand */
2135 glRotatef (s->angle / M_PI*180, 1, 0, 0);
2138 glScalef (0.5, 1, 0.5);
2139 polys += striped_unit_sphere (slices, slices, divs, gcolor2, gcolor1,
2140 MI_IS_WIREFRAME(mi));
2142 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor2);
2143 polys += tube (0, 0, 0,
2146 slices, True, True, MI_IS_WIREFRAME(mi));
2148 glTranslatef (0, 2, 0);
2149 glScalef (0.25, 0.25, 0.25);
2150 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
2159 show_torch(ModeInfo *mi, unsigned long color, Trace *s)
2163 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2164 XPoint head, tail, last;
2165 DXPoint dhead, dlast;
2166 const double sa = sin(s->angle);
2167 const double ca = cos(s->angle);
2169 const double TailLen = -24;
2170 const double HeadLen = 16;
2171 const short Width = (short)(5 * sqrt(sp->scale));
2183 dhead.x = s->x + HeadLen * PERSPEC * sa;
2184 dhead.y = s->y - HeadLen * ca;
2186 if(color == MI_BLACK_PIXEL(mi)) { /* Use 'last' when erasing */
2188 } else { /* Store 'last' so we can use it later when s->prev has
2190 if(s->prev != s->next) {
2191 dlast.x = s->prev->x + HeadLen * PERSPEC * sin(s->prev->angle);
2192 dlast.y = s->prev->y - HeadLen * cos(s->prev->angle);
2199 /* Avoid wrapping (after last is stored) */
2200 if(s->y*sp->scale > SCENE_HEIGHT * 2) return 0;
2202 head.x = (short)(SCENE_WIDTH/2 + dhead.x*sp->scale);
2203 head.y = (short)(SCENE_HEIGHT - dhead.y*sp->scale);
2205 last.x = (short)(SCENE_WIDTH/2 + dlast.x*sp->scale);
2206 last.y = (short)(SCENE_HEIGHT - dlast.y*sp->scale);
2208 tail.x = (short)(SCENE_WIDTH/2 +
2209 (s->x + TailLen * PERSPEC * sa)*sp->scale );
2210 tail.y = (short)(SCENE_HEIGHT - (s->y - TailLen * ca)*sp->scale );
2212 if(color != MI_BLACK_PIXEL(mi)) {
2213 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
2214 XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi),
2215 Width, LineSolid, CapRound, JoinRound);
2216 draw_line(mi, head.x, head.y, tail.x, tail.y);
2218 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), color);
2219 XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi),
2220 Width * 2, LineSolid, CapRound, JoinRound);
2222 draw_line(mi, head.x, head.y, last.x, last.y);
2230 show_knife(ModeInfo *mi, unsigned long color, Trace *s)
2233 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2234 /*int offset = (int)(s->angle*64*180/M_PI);*/
2235 short x = (short)(SCENE_WIDTH/2 + s->x * sp->scale);
2236 short y = (short)(SCENE_HEIGHT - s->y * sp->scale);
2237 GLfloat gcolor1[4] = { 0, 0, 0, 1 };
2238 GLfloat gcolor2[4] = { 1, 1, 1, 1 };
2241 /* Avoid wrapping */
2242 if(s->y*sp->scale > SCENE_HEIGHT * 2) return 0;
2244 gcolor1[0] = mi->colors[color].red / 65536.0;
2245 gcolor1[1] = mi->colors[color].green / 65536.0;
2246 gcolor1[2] = mi->colors[color].blue / 65536.0;
2248 glFrontFace(GL_CCW);
2251 glTranslatef(x, y, 0);
2254 glTranslatef (0, 0, 2); /* put end of handle in hand */
2255 glRotatef (s->angle / M_PI*180, 1, 0, 0);
2257 glScalef (0.3, 1, 1); /* flatten blade */
2259 glTranslatef(0, 6, 0);
2260 glRotatef (180, 1, 0, 0);
2262 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor1);
2263 polys += tube (0, 0, 0,
2266 slices, True, True, MI_IS_WIREFRAME(mi));
2268 glTranslatef (0, 12, 0);
2269 glScalef (0.7, 10, 0.7);
2270 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor2);
2271 polys += unit_sphere (slices, slices, MI_IS_WIREFRAME(mi));
2279 show_ring(ModeInfo *mi, unsigned long color, Trace *s)
2282 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2283 /*int offset = (int)(s->angle*64*180/M_PI);*/
2284 short x = (short)(SCENE_WIDTH/2 + s->x * sp->scale);
2285 short y = (short)(SCENE_HEIGHT - s->y * sp->scale);
2286 double radius = 12 * sp->scale;
2287 GLfloat gcolor1[4] = { 0, 0, 0, 1 };
2288 GLfloat gcolor2[4] = { 0, 0, 0, 1 };
2291 int wire_p = MI_IS_WIREFRAME(mi);
2292 GLfloat width = M_PI * 2 / slices;
2295 GLfloat thickness = 0.15;
2297 /* Avoid wrapping */
2298 if(s->y*sp->scale > SCENE_HEIGHT * 2) return 0;
2300 gcolor1[0] = mi->colors[color].red / 65536.0;
2301 gcolor1[1] = mi->colors[color].green / 65536.0;
2302 gcolor1[2] = mi->colors[color].blue / 65536.0;
2304 gcolor2[0] = gcolor1[0] / 3;
2305 gcolor2[1] = gcolor1[1] / 3;
2306 gcolor2[2] = gcolor1[2] / 3;
2308 glFrontFace(GL_CCW);
2311 glTranslatef(0, 0, 12); /* back of ring in hand */
2313 glTranslatef(x, y, 0);
2314 glScalef(radius, radius, radius);
2316 glRotatef (90, 0, 1, 0);
2317 glRotatef (s->angle / M_PI*180, 0, 0, 1);
2319 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor1);
2322 for (j = -1; j <= 1; j += 2)
2324 GLfloat z = j * thickness/2;
2325 glFrontFace (j < 0 ? GL_CCW : GL_CW);
2326 glNormal3f (0, 0, j*1);
2327 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
2328 for (i = 0; i < slices + (wire_p ? 0 : 1); i++) {
2329 GLfloat th, cth, sth;
2330 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
2331 (i % (slices/3) ? gcolor1 : gcolor2));
2335 glVertex3f (cth * ra, sth * ra, z);
2336 glVertex3f (cth * rb, sth * rb, z);
2343 glFrontFace (GL_CCW);
2344 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
2345 for (i = 0; i < slices + (wire_p ? 0 : 1); i++)
2347 GLfloat th = i * width;
2348 GLfloat cth = cos(th);
2349 GLfloat sth = sin(th);
2350 glNormal3f (cth, sth, 0);
2351 glVertex3f (cth * ra, sth * ra, thickness/2);
2352 glVertex3f (cth * ra, sth * ra, -thickness/2);
2358 glFrontFace (GL_CW);
2359 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
2360 for (i = 0; i < slices + (wire_p ? 0 : 1); i++)
2362 GLfloat th = i * width;
2363 GLfloat cth = cos(th);
2364 GLfloat sth = sin(th);
2365 glNormal3f (-cth, -sth, 0);
2366 glVertex3f (cth * rb, sth * ra, thickness/2);
2367 glVertex3f (cth * rb, sth * ra, -thickness/2);
2372 glFrontFace (GL_CCW);
2379 show_bball(ModeInfo *mi, unsigned long color, Trace *s)
2382 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2383 /*int offset = (int)(s->angle*64*180/M_PI);*/
2384 short x = (short)(SCENE_WIDTH/2 + s->x * sp->scale);
2385 short y = (short)(SCENE_HEIGHT - s->y * sp->scale);
2386 double radius = 12 * sp->scale;
2387 GLfloat gcolor1[4] = { 0, 0, 0, 1 };
2388 GLfloat gcolor2[4] = { 0, 0, 0, 1 };
2392 /* Avoid wrapping */
2393 if(s->y*sp->scale > SCENE_HEIGHT * 2) return 0;
2395 gcolor1[0] = mi->colors[color].red / 65536.0;
2396 gcolor1[1] = mi->colors[color].green / 65536.0;
2397 gcolor1[2] = mi->colors[color].blue / 65536.0;
2399 glFrontFace(GL_CCW);
2402 GLfloat scale = radius;
2405 glTranslatef(0, -6, 5); /* position on top of hand */
2407 glTranslatef(x, y, 0);
2408 glScalef(scale, scale, scale);
2409 glRotatef (s->angle / M_PI*180, 1, 0, 1);
2411 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor1);
2412 polys += unit_sphere (slices, slices, MI_IS_WIREFRAME(mi));
2414 glRotatef (90, 0, 0, 1);
2415 glTranslatef (0, 0, 0.81);
2416 glScalef(0.15, 0.15, 0.15);
2417 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor2);
2418 for (i = 0; i < 3; i++) {
2420 glTranslatef (0, 0, 1);
2421 glRotatef (360 * i / 3, 0, 0, 1);
2422 glTranslatef (2, 0, 0);
2423 glRotatef (18, 0, 1, 0);
2424 glBegin (MI_IS_WIREFRAME(mi) ? GL_LINE_LOOP : GL_TRIANGLE_FAN);
2425 glVertex3f (0, 0, 0);
2426 for (j = slices; j >= 0; j--) {
2427 GLfloat th = j * M_PI*2 / slices;
2428 glVertex3f (cos(th), sin(th), 0);
2441 /**************************************************************************
2442 * Public Functions *
2444 **************************************************************************/
2447 /* FIXME: refill_juggle currently just appends new throws to the
2448 * programme. This is fine if the programme is empty, but if there
2449 * are still some trajectories left then it really should take these
2453 refill_juggle(ModeInfo * mi)
2455 jugglestruct *sp = NULL;
2458 if (juggles == NULL)
2460 sp = &juggles[MI_SCREEN(mi)];
2462 /* generate pattern */
2464 if (pattern == NULL) {
2467 #define MAXREPEAT 300
2468 #define CHANGE_BIAS 8 /* larger makes num_ball changes less likely */
2469 #define POSITION_BIAS 20 /* larger makes hand movements less likely */
2472 while (count < MI_CYCLES(mi)) {
2473 char buf[MAXPAT * 3 + 3], *b = buf;
2475 int l = NRAND(MAXPAT) + 1;
2476 int t = NRAND(MIN(MAXREPEAT, (MI_CYCLES(mi) - count))) + 1;
2478 { /* vary number of balls */
2479 int new_balls = sp->num_balls;
2482 if (new_balls == 2) /* Do not juggle 2 that often */
2483 change = NRAND(2 + CHANGE_BIAS / 4);
2485 change = NRAND(2 + CHANGE_BIAS);
2496 if (new_balls < sp->patternindex.minballs) {
2499 if (new_balls > sp->patternindex.maxballs) {
2502 if (new_balls < sp->num_balls) {
2503 if (!program(mi, "[*]", NULL, 1)) /* lose ball */
2506 sp->num_balls = new_balls;
2510 if (NRAND(2) && sp->patternindex.index[sp->num_balls].number) {
2511 /* Pick from PortFolio */
2512 int p = sp->patternindex.index[sp->num_balls].start +
2513 NRAND(sp->patternindex.index[sp->num_balls].number);
2514 if (!program(mi, portfolio[p].pattern, portfolio[p].name, t))
2517 /* Invent a new pattern */
2519 for(i = 0; i < l; i++){
2521 do { /* Triangular Distribution => high values more likely */
2522 m = NRAND(sp->num_balls + 1);
2523 n = NRAND(sp->num_balls + 1);
2525 if (n == sp->num_balls) {
2528 switch(NRAND(5 + POSITION_BIAS)){
2529 case 0: /* Outside throw */
2531 case 1: /* Cross throw */
2533 case 2: /* Cross catch */
2535 case 3: /* Cross throw and catch */
2537 case 4: /* Bounce */
2540 break; /* Inside throw (default) */
2549 if (!program(mi, buf, NULL, t))
2554 } else { /* pattern supplied in height or 'a' notation */
2555 if (!program(mi, pattern, NULL, MI_CYCLES(mi)))
2572 if (!projectile(sp)) {
2579 if(MI_IS_DEBUG(mi)) dump(sp);
2584 change_juggle(ModeInfo * mi)
2586 jugglestruct *sp = NULL;
2589 if (juggles == NULL)
2591 sp = &juggles[MI_SCREEN(mi)];
2593 /* Strip pending trajectories */
2594 for (t = sp->head->next; t != sp->head; t = t->next) {
2595 if(t->start > sp->time || t->finish < sp->time) {
2598 trajectory_destroy(n);
2602 /* Pick the current object theme */
2603 sp->objtypes = choose_object();
2607 mi->polygon_count += show_figure(mi, True);
2612 reshape_juggle (ModeInfo *mi, int width, int height)
2614 GLfloat h = (GLfloat) height / (GLfloat) width;
2616 glViewport (0, 0, (GLint) width, (GLint) height);
2618 glMatrixMode(GL_PROJECTION);
2620 gluPerspective (30.0, 1/h, 1.0, 100.0);
2622 glMatrixMode(GL_MODELVIEW);
2624 gluLookAt( 0.0, 0.0, 30.0,
2628 glClear(GL_COLOR_BUFFER_BIT);
2633 init_juggle (ModeInfo * mi)
2635 jugglestruct *sp = 0;
2636 int wire = MI_IS_WIREFRAME(mi);
2638 MI_INIT (mi, juggles);
2640 sp = &juggles[MI_SCREEN(mi)];
2642 if (!sp->glx_context) /* re-initting breaks print_texture_label */
2643 sp->glx_context = init_GL(mi);
2645 sp->font_data = load_texture_font (mi->dpy, "titleFont");
2647 reshape_juggle (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
2648 clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
2652 GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
2653 GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
2654 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
2655 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
2657 glEnable(GL_LIGHTING);
2658 glEnable(GL_LIGHT0);
2659 glEnable(GL_DEPTH_TEST);
2660 glEnable(GL_CULL_FACE);
2662 glLightfv(GL_LIGHT0, GL_POSITION, pos);
2663 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
2664 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
2665 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
2668 make_random_colormap (0, 0, 0,
2669 mi->colors, &MI_NPIXELS(mi),
2670 True, False, 0, False);
2673 double spin_speed = 0.05;
2674 double wander_speed = 0.001;
2675 double spin_accel = 0.05;
2676 sp->rot = make_rotator (0, spin_speed, 0,
2677 spin_accel, wander_speed, False);
2678 sp->trackball = gltrackball_init (False);
2681 if (only && *only && strcmp(only, " ")) {
2682 balls = clubs = torches = knives = rings = bballs = False;
2683 if (!strcasecmp (only, "balls")) balls = True;
2684 else if (!strcasecmp (only, "clubs")) clubs = True;
2685 else if (!strcasecmp (only, "torches")) torches = True;
2686 else if (!strcasecmp (only, "knives")) knives = True;
2687 else if (!strcasecmp (only, "rings")) rings = True;
2688 else if (!strcasecmp (only, "bballs")) bballs = True;
2690 (void) fprintf (stderr,
2691 "Juggle: -only must be one of: balls, clubs, torches, knives,\n"
2692 "\t rings, or bballs (not \"%s\")\n", only);
2693 #ifdef STANDALONE /* xlock mustn't exit merely because of a bad argument */
2699 /* #### hard to make this look good in OpenGL... */
2703 if (sp->head == 0) { /* first time initializing this juggler */
2705 sp->count = ABS(MI_COUNT(mi));
2709 /* record start time */
2710 sp->begintime = time(NULL);
2711 if(sp->patternindex.maxballs > 0) {
2712 sp->num_balls = sp->patternindex.minballs +
2713 NRAND(sp->patternindex.maxballs - sp->patternindex.minballs);
2716 mi->polygon_count +=
2717 show_figure(mi, True); /* Draw figure. Also discovers
2718 information about the juggler's
2721 /* "7" should be about three times the height of the juggler's
2723 sp->Gr = -GRAVITY(3 * sp->arm[0][RIGHT][SHOULDER].y,
2724 7 * THROW_CATCH_INTERVAL);
2726 if(!balls && !clubs && !torches && !knives && !rings && !bballs)
2727 balls = True; /* Have to juggle something! */
2729 /* create circular trajectory list */
2730 ADD_ELEMENT(Trajectory, sp->head, sp->head);
2731 if(sp->head == NULL){
2736 /* create circular object list */
2737 ADD_ELEMENT(Object, sp->objects, sp->objects);
2738 if(sp->objects == NULL){
2743 sp->pattern = strdup(""); /* Initialise saved pattern with
2747 sp = &juggles[MI_SCREEN(mi)];
2751 !strcasecmp (pattern, ".") ||
2752 !strcasecmp (pattern, "random")))
2755 if (pattern == NULL && sp->patternindex.maxballs == 0) {
2756 /* pattern list needs indexing */
2757 int nelements = countof(portfolio);
2761 /* sort according to number of balls */
2762 qsort((void*)portfolio, nelements,
2763 sizeof(portfolio[1]), compare_num_balls);
2765 /* last pattern has most balls */
2766 sp->patternindex.maxballs = get_num_balls(portfolio[nelements - 1].pattern);
2767 /* run through sorted list, indexing start of each group
2768 and number in group */
2769 sp->patternindex.maxballs = 1;
2770 for (i = 0; i < nelements; i++) {
2771 int b = get_num_balls(portfolio[i].pattern);
2772 if (b > sp->patternindex.maxballs) {
2773 sp->patternindex.index[sp->patternindex.maxballs].number = numpat;
2774 if(numpat == 0) sp->patternindex.minballs = b;
2775 sp->patternindex.maxballs = b;
2777 sp->patternindex.index[sp->patternindex.maxballs].start = i;
2782 sp->patternindex.index[sp->patternindex.maxballs].number = numpat;
2785 /* Set up programme */
2788 /* Only put things here that won't interrupt the programme during
2791 /* Use MIN so that users can resize in interesting ways, eg
2792 narrow windows for tall patterns, etc */
2793 sp->scale = MIN(SCENE_HEIGHT/480.0, SCENE_WIDTH/160.0);
2798 juggle_handle_event (ModeInfo *mi, XEvent *event)
2800 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2802 if (gltrackball_event_handler (event, sp->trackball,
2803 MI_WIDTH (mi), MI_HEIGHT (mi),
2804 &sp->button_down_p))
2806 else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
2817 draw_juggle (ModeInfo *mi)
2819 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2820 Display *dpy = MI_DISPLAY(mi);
2821 Window window = MI_WINDOW(mi);
2823 Trajectory *traj = NULL;
2825 unsigned long future = 0;
2826 char *pattern = NULL;
2828 if (!sp->glx_context)
2831 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sp->glx_context));
2833 glShadeModel(GL_SMOOTH);
2835 glEnable(GL_DEPTH_TEST);
2836 glEnable(GL_NORMALIZE);
2837 glEnable(GL_CULL_FACE);
2839 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
2842 glRotatef(current_device_rotation(), 0, 0, 1);
2844 glTranslatef(0,-3,0);
2848 get_position (sp->rot, &x, &y, &z, !sp->button_down_p);
2849 glTranslatef((x - 0.5) * 8,
2853 gltrackball_rotate (sp->trackball);
2855 get_rotation (sp->rot, &x, &y, &z, !sp->button_down_p);
2857 if (y < 0.8) y = 0.8 - (y - 0.8); /* always face forward */
2858 if (y > 1.2) y = 1.2 - (y - 1.2);
2860 glRotatef (x * 360, 1.0, 0.0, 0.0);
2861 glRotatef (y * 360, 0.0, 1.0, 0.0);
2862 glRotatef (z * 360, 0.0, 0.0, 1.0);
2866 GLfloat scale = 20.0 / SCENE_HEIGHT;
2867 glScalef(scale, scale, scale);
2870 glRotatef (180, 0, 0, 1);
2871 glTranslatef(-SCENE_WIDTH/2, -SCENE_HEIGHT/2, 0);
2872 glTranslatef(0, -150, 0);
2874 mi->polygon_count = 0;
2879 (void)gettimeofday(&tv, NULL);
2880 sp->time = (int) ((tv.tv_sec - sp->begintime)*1000 + tv.tv_usec/1000);
2882 sp->time += MI_DELAY(mi) / 1000;
2885 /* First pass: Move arms and strip out expired elements */
2886 for (traj = sp->head->next; traj != sp->head; traj = traj->next) {
2887 if (traj->status != PREDICTOR) {
2888 /* Skip any elements that need further processing */
2889 /* We could remove them, but there shoudn't be many and they
2890 would be needed if we ever got the pattern refiller
2894 if (traj->start > future) { /* Lookahead to the end of the show */
2895 future = traj->start;
2897 if (sp->time < traj->start) { /* early */
2899 } else if (sp->time < traj->finish) { /* working */
2901 /* Look for pattern name */
2902 if(traj->pattern != NULL) {
2903 pattern=traj->pattern;
2906 if (traj->type == Empty || traj->type == Full) {
2907 /* Only interested in hands on this pass */
2908 /* double angle = traj->angle + traj->spin * (sp->time - traj->start);*/
2909 double xd = 0, yd = 0;
2912 /* Find the catching offset */
2913 if(traj->object != NULL) {
2915 /* #### not sure what this is doing, but I'm guessing
2916 that the use of PERSPEC means this isn't needed
2917 in the OpenGL version? -jwz
2919 if(ObjectDefs[traj->object->type].handle > 0) {
2920 /* Handles Need to be oriented */
2921 xd = ObjectDefs[traj->object->type].handle *
2922 PERSPEC * sin(angle);
2923 yd = ObjectDefs[traj->object->type].handle *
2928 /* Balls are always caught at the bottom */
2933 p.x = (CUBIC(traj->xp, sp->time) - xd);
2934 p.y = (CUBIC(traj->yp, sp->time) + yd);
2935 reach_arm(mi, traj->hand, &p);
2937 /* Store updated hand position */
2941 if (traj->type == Ball || traj->type == Full) {
2942 /* Only interested in objects on this pass */
2946 if(traj->type == Full) {
2947 /* Adjusted these in the first pass */
2951 x = CUBIC(traj->xp, sp->time);
2952 y = CUBIC(traj->yp, sp->time);
2955 ADD_ELEMENT(Trace, s, traj->object->trace->prev);
2958 s->angle = traj->angle + traj->spin * (sp->time - traj->start);
2959 s->divisions = traj->divisions;
2960 traj->object->tracelen++;
2961 traj->object->active = True;
2963 } else { /* expired */
2964 Trajectory *n = traj;
2966 trajectory_destroy(n);
2971 mi->polygon_count += show_figure(mi, False);
2972 mi->polygon_count += show_arms(mi);
2975 glTranslatef(0, 0, ARMLENGTH);
2976 for (o = sp->objects->next; o != sp->objects; o = o->next) {
2978 mi->polygon_count += ObjectDefs[o->type].draw(mi, o->color,
2985 /* Save pattern name so we can erase it when it changes */
2986 if(pattern != NULL && strcmp(sp->pattern, pattern) != 0 ) {
2988 sp->pattern = strdup(pattern);
2990 if (MI_IS_VERBOSE(mi)) {
2991 (void) fprintf(stderr, "Juggle[%d]: Running: %s\n",
2992 MI_SCREEN(mi), sp->pattern);
2996 glColor3f (1, 1, 0);
2997 print_texture_label (mi->dpy, sp->font_data,
2998 mi->xgwa.width, mi->xgwa.height,
3002 if((int)(sp->time/10) % 1000 == 0)
3003 (void) fprintf(stderr, "sbrk: %d\n", (int)sbrk(0));
3006 if (future < sp->time + 100 * THROW_CATCH_INTERVAL) {
3008 } else if (sp->time > 1<<30) { /* Hard Reset before the clock wraps */
3014 if (mi->fps_p) do_fps (mi);
3017 glXSwapBuffers(dpy, window);
3020 XSCREENSAVER_MODULE_2 ("Juggler3D", juggler3d, juggle)