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 refresh_juggle 0
140 #define countof(x) (sizeof((x))/sizeof((*x)))
142 #include "xlockmore.h"
146 #include "gltrackball.h"
147 #include "glxfonts.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;
583 XFontStruct *mode_font;
587 static jugglestruct *juggles = (jugglestruct *) NULL;
593 #define DUP_OBJECT(n, t) { \
594 (n)->object = (t)->object; \
595 if((n)->object != NULL) (n)->object->count++; \
598 /* t must point to an existing element. t must not be an
599 expression ending ->next or ->prev */
600 #define REMOVE(t) { \
601 (t)->next->prev = (t)->prev; \
602 (t)->prev->next = (t)->next; \
606 /* t receives element to be created and added to the list. ot must
607 point to an existing element or be identical to t to start a new
608 list. Applicable to Trajectories, Objects and Traces. */
609 #define ADD_ELEMENT(type, t, ot) \
610 if (((t) = (type*)calloc(1,sizeof(type))) != NULL) { \
611 (t)->next = (ot)->next; \
614 (t)->next->prev = (t); \
618 object_destroy(Object* o)
620 if(o->trace != NULL) {
621 while(o->trace->next != o->trace) {
622 Trace *s = o->trace->next;
623 REMOVE(s); /* Don't eliminate 's' */
631 trajectory_destroy(Trajectory *t) {
632 if(t->name != NULL) free(t->name);
633 if(t->pattern != NULL) free(t->pattern);
634 /* Reduce object link count and call destructor if necessary */
635 if(t->object != NULL && --t->object->count < 1 && t->object->tracelen == 0) {
636 object_destroy(t->object);
638 REMOVE(t); /* Unlink and free */
642 free_juggle(jugglestruct *sp) {
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) {
661 if (sp->mode_font!=None) {
662 XFreeFontInfo(NULL,sp->mode_font,1);
663 sp->mode_font = None;
668 add_throw(jugglestruct *sp, char type, int h, Notation n, const char* name)
672 ADD_ELEMENT(Trajectory, t, sp->head->prev);
673 if(t == NULL){ /* Out of Memory */
679 t->name = strdup(name);
692 /* add a Thratch to the performance */
694 program(ModeInfo *mi, const char *patn, const char *name, int cycles)
696 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
702 if (MI_IS_VERBOSE(mi)) {
703 (void) fprintf(stderr, "juggle[%d]: Programmed: %s x %d\n",
704 MI_SCREEN(mi), (name == NULL) ? patn : name, cycles);
707 for(w=i=0; i < cycles; i++, w++) { /* repeat until at least "cycles" throws
708 have been programmed */
709 /* title is the pattern name to be supplied to the first throw of
710 a sequence. If no name if given, use an empty title so that
711 the sequences are still delimited. */
712 const char *title = (name != NULL)? name : "";
717 for(p=patn; *p; p++) {
718 if (*p >= '0' && *p <='9') {
720 h = 10*h + (*p - '0');
722 Notation nn = notation;
724 case '[': /* begin Adam notation */
727 case '-': /* Inside throw */
730 case '+': /* Outside throw */
731 case '=': /* Cross throw */
732 case '&': /* Cross catch */
733 case 'x': /* Cross throw and catch */
734 case '_': /* Bounce */
735 case 'k': /* Kickup */
738 case '*': /* Lose ball */
742 case ']': /* end Adam notation */
748 if (!add_throw(sp, type, h, notation, title))
758 if(w == 0) { /* Only warn on first pass */
759 (void) fprintf(stderr,
760 "juggle[%d]: Unexpected pattern instruction: '%c'\n",
767 if (seen) { /* end of sequence */
768 if (!add_throw(sp, type, h, notation, title))
782 [ 3 3 1 3 4 2 3 1 3 3 4 0 2 1 ]
784 4 4 1 3 12 2 4 1 4 4 13 0 3 1
787 #define BOUNCEOVER 10
791 /* Convert Adam notation into heights */
793 adam(jugglestruct *sp)
796 for(t = sp->head->next; t != sp->head; t = t->next) {
797 if (t->status == ATCH) {
800 for(p = t->next; a > 0; p = p->next) {
802 t->height = -9; /* Indicate end of processing for name() */
805 if (p->status != ATCH || p->adam < 0 || p->adam>= a) {
810 if(t->height > BOUNCEOVER && t->posn == ' '){
811 t->posn = '_'; /* high defaults can be bounced */
812 } else if(t->height < 3 && t->posn == '_') {
813 t->posn = ' '; /* Can't bounce short throws. */
815 if(t->height < KICKMIN && t->posn == 'k'){
816 t->posn = ' '; /* Can't kick short throws */
818 if(t->height > THROWMAX){
819 t->posn = 'k'; /* Use kicks for ridiculously high throws */
826 /* Discover converted heights and update the sequence title */
828 name(jugglestruct *sp)
833 for(t = sp->head->next; t != sp->head; t = t->next) {
834 if (t->status == THRATCH && t->name != NULL) {
836 for(p = t; p == t || p->name == NULL; p = p->next) {
837 if(p == sp->head || p->height < 0) { /* end of reliable data */
841 b += sprintf(b, " %d", p->height);
843 b += sprintf(b, " %c%d", p->posn, p->height);
845 if(b - buffer > 500) break; /* otherwise this could eventually
846 overflow. It'll be too big to
850 (void) sprintf(b, ", %s", t->name);
852 free(t->name); /* Don't need name any more, it's been converted
855 if(t->pattern != NULL) free(t->pattern);
856 t->pattern = strdup(buffer);
861 /* Split Thratch notation into explicit throws and catches.
862 Usually Catch follows Throw in same hand, but take care of special
865 /* ..n1.. -> .. LTn RT1 LC RC .. */
866 /* ..nm.. -> .. LTn LC RTm RC .. */
869 part(jugglestruct *sp)
871 Trajectory *t, *nt, *p;
872 Hand hand = (LRAND() & 1) ? RIGHT : LEFT;
874 for (t = sp->head->next; t != sp->head; t = t->next) {
875 if (t->status > THRATCH) {
877 } else if (t->status == THRATCH) {
880 /* plausibility check */
881 if (t->height <= 2 && t->posn == '_') {
882 t->posn = ' '; /* no short bounces */
884 if (t->height <= 1 && (t->posn == '=' || t->posn == '&')) {
885 t->posn = ' '; /* 1's need close catches */
890 case ' ': posn = '-'; t->posn = '+'; break;
891 case '+': posn = '+'; t->posn = '-'; break;
892 case '=': posn = '='; t->posn = '+'; break;
893 case '&': posn = '+'; t->posn = '='; break;
894 case 'x': posn = '='; t->posn = '='; break;
895 case '_': posn = '_'; t->posn = '-'; break;
896 case 'k': posn = 'k'; t->posn = 'k'; break;
898 (void) fprintf(stderr, "juggle: unexpected posn %c\n", t->posn);
901 hand = (Hand) ((hand + 1) % 2);
906 if (t->height == 1 && p != sp->head) {
907 p = p->prev; /* '1's are thrown earlier than usual */
913 ADD_ELEMENT(Trajectory, nt, p);
921 nt->height = t->height;
931 choose_object(void) {
934 o = (ObjType)NRAND((ObjType)NUM_OBJECT_TYPES);
935 if(balls && o == BALL) break;
936 if(clubs && o == CLUB) break;
937 if(torches && o == TORCH) break;
938 if(knives && o == KNIFE) break;
939 if(rings && o == RING) break;
940 if(bballs && o == BBALLS) break;
945 /* Connnect up throws and catches to figure out which ball goes where.
946 Do the same with the juggler's hands. */
951 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
954 for (t = sp->head->next; t != sp->head; t = t->next) {
955 if (t->status == ACTION) {
956 if (t->action == THROW) {
957 if (t->type == Empty) {
958 /* Create new Object */
959 ADD_ELEMENT(Object, t->object, sp->objects);
960 t->object->count = 1;
961 t->object->tracelen = 0;
962 t->object->active = False;
963 /* Initialise object's circular trace list */
964 ADD_ELEMENT(Trace, t->object->trace, t->object->trace);
966 if (MI_NPIXELS(mi) > 2) {
967 t->object->color = 1 + NRAND(MI_NPIXELS(mi) - 2);
970 t->object->color = 1;
972 t->object->color = 0;
976 /* Small chance of picking a random object instead of the
978 if(NRAND(OBJMIXPROB) == 0) {
979 t->object->type = choose_object();
981 t->object->type = sp->objtypes;
984 /* Check to see if we need trails for this object */
985 if(tail < ObjectDefs[t->object->type].mintrail) {
986 t->object->tail = ObjectDefs[t->object->type].mintrail;
988 t->object->tail = tail;
992 /* Balls can change divisions at each throw */
993 /* no, that looks stupid. -jwz */
994 if (t->divisions < 1)
995 t->divisions = 2 * (NRAND(2) + 1);
997 /* search forward for next catch in this hand */
998 for (p = t->next; t->handlink == NULL; p = p->next) {
999 if(p->status < ACTION || p == sp->head) return;
1000 if (p->action == CATCH) {
1001 if (t->handlink == NULL && p->hand == t->hand) {
1007 if (t->height > 0) {
1010 /* search forward for next ball catch */
1011 for (p = t->next; t->balllink == NULL; p = p->next) {
1012 if(p->status < ACTION || p == sp->head) {
1016 if (p->action == CATCH) {
1017 if (t->balllink == NULL && --h < 1) { /* caught */
1018 t->balllink = p; /* complete trajectory */
1020 if (p->type == Full) {
1021 (void) fprintf(stderr, "juggle[%d]: Dropped %d\n",
1022 MI_SCREEN(mi), t->object->color);
1026 DUP_OBJECT(p, t); /* accept catch */
1027 p->angle = t->angle;
1028 p->divisions = t->divisions;
1033 t->type = Empty; /* thrown */
1034 } else if (t->action == CATCH) {
1035 /* search forward for next throw from this hand */
1036 for (p = t->next; t->handlink == NULL; p = p->next) {
1037 if(p->status < ACTION || p == sp->head) return;
1038 if (p->action == THROW && p->hand == t->hand) {
1039 p->type = t->type; /* pass ball */
1040 DUP_OBJECT(p, t); /* pass object */
1041 p->divisions = t->divisions;
1046 t->status = LINKEDACTION;
1051 /* Clap when both hands are empty */
1053 clap(jugglestruct *sp)
1056 for (t = sp->head->next; t != sp->head; t = t->next) {
1057 if (t->status == LINKEDACTION &&
1058 t->action == CATCH &&
1060 t->handlink != NULL &&
1061 t->handlink->height == 0) { /* Completely idle hand */
1063 for (p = t->next; p != sp->head; p = p->next) {
1064 if (p->status == LINKEDACTION &&
1065 p->action == CATCH &&
1066 p->hand != t->hand) { /* Next catch other hand */
1067 if(p->type == Empty &&
1068 p->handlink != NULL &&
1069 p->handlink->height == 0) { /* Also completely idle */
1071 t->handlink->posn = '^'; /* Move first hand's empty throw */
1072 p->posn = '^'; /* to meet second hand's empty
1076 break; /* Only need first catch */
1083 #define CUBIC(s, t) ((((s).a * (t) + (s).b) * (t) + (s).c) * (t) + (s).d)
1085 /* Compute single spline from x0 with velocity dx0 at time t0 to x1
1086 with velocity dx1 at time t1 */
1088 makeSpline(double x0, double dx0, int t0, double x1, double dx1, int t1)
1097 a = ((dx0 + dx1)*t10 - 2*x10) / (t10*t10*t10);
1098 b = (3*x10 - (2*dx0 + dx1)*t10) / (t10*t10);
1103 s.c = (3*a*t0 - 2*b)*t0 + c;
1104 s.d = ((-a*t0 + b)*t0 - c)*t0 +d;
1108 /* Compute a pair of splines. s1 goes from x0 vith velocity dx0 at
1109 time t0 to x1 at time t1. s2 goes from x1 at time t1 to x2 with
1110 velocity dx2 at time t2. The arrival and departure velocities at
1111 x1, t1 must be the same. */
1113 makeSplinePair(Spline *s1, Spline *s2,
1114 double x0, double dx0, int t0,
1116 double x2, double dx2, int t2)
1118 double x10, x21, t21, t10, t20, dx1;
1124 dx1 = (3*x10*t21*t21 + 3*x21*t10*t10 + 3*dx0*t10*t21*t21
1125 - dx2*t10*t10*t21 - 4*dx0*t10*t21*t21) /
1127 *s1 = makeSpline(x0, dx0, t0, x1, dx1, t1);
1128 *s2 = makeSpline(x1, dx1, t1, x2, dx2, t2);
1132 /* Compute a Ballistic path in a pair of degenerate splines. sx goes
1133 from x at time t at constant velocity dx. sy goes from y at time t
1134 with velocity dy and constant acceleration g. */
1136 makeParabola(Trajectory *n,
1137 double x, double dx, double y, double dy, double g)
1139 double t = (double)n->start;
1143 n->xp.d = -dx*t + x;
1146 n->yp.c = -g*t + dy;
1147 n->yp.d = g/2*t*t - dy*t + y;
1153 #define SX 25 /* Shoulder Width */
1155 /* Convert hand position symbols into actual time/space coordinates */
1157 positions(jugglestruct *sp)
1160 unsigned long now = sp->time; /* Make sure we're not lost in the past */
1161 for (t = sp->head->next; t != sp->head; t = t->next) {
1162 if (t->status >= PTHRATCH) {
1164 } else if (t->status == ACTION || t->status == LINKEDACTION) {
1165 /* Allow ACTIONs to be annotated, but we won't mark them ready
1166 for the next stage */
1173 if (t->action == CATCH) { /* Throw-to-catch */
1174 if (t->type == Empty) {
1175 now += (int) THROW_NULL_INTERVAL; /* failed catch is short */
1176 } else { /* successful catch */
1177 now += (int)(THROW_CATCH_INTERVAL);
1179 } else { /* Catch-to-throw */
1180 if(t->object != NULL) {
1181 now += (int) (CATCH_THROW_INTERVAL *
1182 ObjectDefs[t->object->type].weight);
1184 now += (int) (CATCH_THROW_INTERVAL);
1190 else /* Concatenated performances may need clock resync */
1198 /* Add room for the handle */
1199 if(t->action == CATCH && t->object != NULL)
1200 yo -= ObjectDefs[t->object->type].handle;
1203 case '-': xo = sx - pose; break;
1206 case '+': xo = sx + pose; break;
1208 case '=': xo = - sx - pose; yo += pose; break;
1209 case '^': xo = 0; yo += pose*2; break; /* clap */
1211 (void) fprintf(stderr, "juggle: unexpected posn %c\n", t->posn);
1215 #ifdef _2DSpinsDontWorkIn3D
1216 t->angle = (((t->hand == LEFT) ^
1217 (t->posn == '+' || t->posn == '_' || t->posn == 'k' ))?
1223 t->x = t->cx + ((t->hand == LEFT) ? xo : -xo);
1226 /* Only mark complete if it was already linked */
1227 if(t->status == LINKEDACTION) {
1228 t->status = PTHRATCH;
1235 /* Private physics functions */
1237 /* Compute the spin-rate for a trajectory. Different types of throw
1238 (eg, regular thows, bounces, kicks, etc) have different spin
1241 type = type of object
1242 h = trajectory of throwing hand (throws), or next throwing hand (catches)
1243 old = earlier spin to consider
1244 dt = time span of this trajectory
1245 height = height of ball throw or 0 if based on old spin
1246 turns = full club turns required during this operation
1247 togo = partial club turns required to match hands
1250 spinrate(ObjType type, Trajectory *h, double old, double dt,
1251 int height, int turns, double togo)
1253 #ifdef _2DSpinsDontWorkIn3D
1254 const int dir = (h->hand == LEFT) ^ (h->posn == '+')? -1 : 1;
1259 if(ObjectDefs[type].handle != 0) { /* Clubs */
1260 return (dir * turns * 2 * M_PI + togo) / dt;
1261 } else if(height == 0) { /* Balls already spinning */
1263 } else { /* Balls */
1264 return dir * NRAND(height*10)/20/ObjectDefs[type].weight * 2 * M_PI / dt;
1269 /* compute the angle at the end of a spinning trajectory */
1271 end_spin(Trajectory *t)
1273 return t->angle + t->spin * (t->finish - t->start);
1276 /* Sets the initial angle of the catch following hand movement t to
1277 the final angle of the throw n. Also sets the angle of the
1278 subsequent throw to the same angle plus half a turn. */
1280 match_spins_on_catch(Trajectory *t, Trajectory *n)
1282 if(ObjectDefs[t->balllink->object->type].handle == 0) {
1283 t->balllink->angle = end_spin(n);
1284 if(t->balllink->handlink != NULL) {
1285 #ifdef _2DSpinsDontWorkIn3D
1286 t->balllink->handlink->angle = t->balllink->angle + M_PI;
1288 t->balllink->handlink->angle = t->balllink->angle;
1295 find_bounce(jugglestruct *sp,
1296 double yo, double yf, double yc, double tc, double cor)
1298 double tb, i, dy = 0;
1299 const double e = 1; /* permissible error in yc */
1303 yt = height at catch time after one bounce
1304 one or three roots according to timing
1305 find one by interval bisection
1308 for(i = tc / 2; i > 0.0001; i/=2){
1311 (void) fprintf(stderr, "juggle: bounce div by zero!\n");
1314 dy = (yf - yo)/tb + sp->Gr/2*tb;
1316 yt = -cor*dy*dt + sp->Gr/2*dt*dt + yf;
1319 }else if(yt > yc - e){
1325 if(dy*THROW_CATCH_INTERVAL < -200) { /* bounce too hard */
1332 new_predictor(const Trajectory *t, int start, int finish, double angle)
1335 ADD_ELEMENT(Trajectory, n, t->prev);
1340 n->divisions = t->divisions;
1342 n->status = PREDICTOR;
1350 /* Turn abstract timings into physically appropriate object trajectories. */
1352 projectile(jugglestruct *sp)
1355 const int yf = 0; /* Floor height */
1357 for (t = sp->head->next; t != sp->head; t = t->next) {
1358 if (t->status != PTHRATCH || t->action != THROW) {
1360 } else if (t->balllink == NULL) { /* Zero Throw */
1361 t->status = BPREDICTOR;
1362 } else if (t->balllink->handlink == NULL) { /* Incomplete */
1364 } else if(t->balllink == t->handlink) {
1365 /* '2' height - hold on to ball. Don't need to consider
1366 flourishes, 'hands' will do that automatically anyway */
1369 /* Zero spin to avoid wrist injuries */
1371 match_spins_on_catch(t, t);
1373 t->status = BPREDICTOR;
1376 if (t->posn == '_') { /* Bounce once */
1378 const int tb = t->start +
1379 find_bounce(sp, t->y, (double) yf, t->balllink->y,
1380 (double) (t->balllink->start - t->start),
1381 ObjectDefs[t->object->type].cor);
1383 if(tb < t->start) { /* bounce too hard */
1384 t->posn = '+'; /* Use regular throw */
1386 Trajectory *n; /* First (throw) trajectory. */
1387 double dt; /* Time span of a trajectory */
1388 double dy; /* Distance span of a follow-on trajectory.
1389 First trajectory uses t->dy */
1390 /* dx is constant across both trajectories */
1391 t->dx = (t->balllink->x - t->x) / (t->balllink->start - t->start);
1393 { /* ball follows parabola down */
1394 n = new_predictor(t, t->start, tb, t->angle);
1395 if(n == NULL) return False;
1396 dt = n->finish - n->start;
1397 /* Ball rate 4, no flight or matching club turns */
1398 n->spin = spinrate(t->object->type, t, 0.0, dt, 4, 0, 0.0);
1399 t->dy = (yf - t->y)/dt - sp->Gr/2*dt;
1400 makeParabola(n, t->x, t->dx, t->y, t->dy, sp->Gr);
1403 { /* ball follows parabola up */
1404 Trajectory *m = new_predictor(t, n->finish, t->balllink->start,
1406 if(m == NULL) return False;
1407 dt = m->finish - m->start;
1408 /* Use previous ball rate, no flight club turns */
1409 m->spin = spinrate(t->object->type, t, n->spin, dt, 0, 0,
1410 t->balllink->angle - m->angle);
1411 match_spins_on_catch(t, m);
1412 dy = (t->balllink->y - yf)/dt - sp->Gr/2 * dt;
1413 makeParabola(m, t->balllink->x - t->dx * dt,
1414 t->dx, (double) yf, dy, sp->Gr);
1417 t->status = BPREDICTOR;
1420 } else if (t->posn == 'k') { /* Drop & Kick */
1421 Trajectory *n; /* First (drop) trajectory. */
1422 Trajectory *o; /* Second (rest) trajectory */
1423 Trajectory *m; /* Third (kick) trajectory */
1424 const int td = t->start + 2*THROW_CATCH_INTERVAL; /* Drop time */
1425 const int tk = t->balllink->start - 5*THROW_CATCH_INTERVAL; /* Kick */
1428 { /* Fall to ground */
1429 n = new_predictor(t, t->start, td, t->angle);
1430 if(n == NULL) return False;
1431 dt = n->finish - n->start;
1432 /* Ball spin rate 4, no flight club turns */
1433 n->spin = spinrate(t->object->type, t, 0.0, dt, 4, 0,
1434 t->balllink->angle - n->angle);
1435 t->dx = (t->balllink->x - t->x) / dt;
1436 t->dy = (yf - t->y)/dt - sp->Gr/2*dt;
1437 makeParabola(n, t->x, t->dx, t->y, t->dy, sp->Gr);
1440 { /* Rest on ground */
1441 o = new_predictor(t, n->finish, tk, end_spin(n));
1442 if(o == NULL) return False;
1444 makeParabola(o, t->balllink->x, 0.0, (double) yf, 0.0, 0.0);
1449 m = new_predictor(t, o->finish, t->balllink->start, end_spin(o));
1450 if(m == NULL) return False;
1451 dt = m->finish - m->start;
1452 /* Match receiving hand, ball rate 4, one flight club turn */
1453 m->spin = spinrate(t->object->type, t->balllink->handlink, 0.0, dt,
1454 4, 1, t->balllink->angle - m->angle);
1455 match_spins_on_catch(t, m);
1456 dy = (t->balllink->y - yf)/dt - sp->Gr/2 * dt;
1457 makeParabola(m, t->balllink->x, 0.0, (double) yf, dy, sp->Gr);
1460 t->status = BPREDICTOR;
1464 /* Regular flight, no bounce */
1465 { /* ball follows parabola */
1467 Trajectory *n = new_predictor(t, t->start,
1468 t->balllink->start, t->angle);
1469 if(n == NULL) return False;
1470 dt = t->balllink->start - t->start;
1472 n->spin = spinrate(t->object->type, t, 0.0, dt, t->height, t->height/2,
1473 t->balllink->angle - n->angle);
1474 match_spins_on_catch(t, n);
1475 t->dx = (t->balllink->x - t->x) / dt;
1476 t->dy = (t->balllink->y - t->y) / dt - sp->Gr/2 * dt;
1477 makeParabola(n, t->x, t->dx, t->y, t->dy, sp->Gr);
1480 t->status = BPREDICTOR;
1486 /* Turn abstract hand motions into cubic splines. */
1488 hands(jugglestruct *sp)
1490 Trajectory *t, *u, *v;
1492 for (t = sp->head->next; t != sp->head; t = t->next) {
1493 /* no throw => no velocity */
1494 if (t->status != BPREDICTOR) {
1499 if (u == NULL) { /* no next catch */
1503 if (v == NULL) { /* no next throw */
1507 /* double spline takes hand from throw, thru catch, to
1510 t->finish = u->start;
1511 t->status = PREDICTOR;
1513 u->finish = v->start;
1514 u->status = PREDICTOR;
1517 /* FIXME: These adjustments leave a small glitch when alternating
1518 balls and clubs. Just hope no-one notices. :-) */
1520 /* make sure empty hand spin matches the thrown object in case it
1523 t->spin = ((t->hand == LEFT)? -1 : 1 ) *
1524 fabs((u->angle - t->angle)/(u->start - t->start));
1526 u->spin = ((v->hand == LEFT) ^ (v->posn == '+')? -1 : 1 ) *
1527 fabs((v->angle - u->angle)/(v->start - u->start));
1529 (void) makeSplinePair(&t->xp, &u->xp,
1530 t->x, t->dx, t->start,
1532 v->x, v->dx, v->start);
1533 (void) makeSplinePair(&t->yp, &u->yp,
1534 t->y, t->dy, t->start,
1536 v->y, v->dy, v->start);
1538 t->status = PREDICTOR;
1542 /* Given target x, y find_elbow puts hand at target if possible,
1543 * otherwise makes hand point to the target */
1545 find_elbow(int armlength, DXPoint *h, DXPoint *e, DXPoint *p, DXPoint *s,
1549 double x = p->x - s->x;
1550 double y = p->y - s->y;
1551 h2 = x*x + y*y + z*z;
1552 if (h2 > 4 * armlength * armlength) {
1553 t = armlength/sqrt(h2);
1556 h->x = 2 * t * x + s->x;
1557 h->y = 2 * t * y + s->y;
1559 r = sqrt((double)(x*x + z*z));
1560 t = sqrt(4 * armlength * armlength / h2 - 1);
1561 e->x = x*(1 + y*t/r)/2 + s->x;
1562 e->y = (y - r*t)/2 + s->y;
1569 /* NOTE: returned x, y adjusted for arm reach */
1571 reach_arm(ModeInfo * mi, Hand side, DXPoint *p)
1573 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
1575 find_elbow(40, &h, &e, p, &sp->arm[1][side][SHOULDER], 25);
1576 *p = sp->arm[1][side][HAND] = h;
1577 sp->arm[1][side][ELBOW] = e;
1581 /* dumps a human-readable rendition of the current state of the juggle
1582 pipeline to stderr for debugging */
1584 dump(jugglestruct *sp)
1587 for (t = sp->head->next; t != sp->head; t = t->next) {
1588 switch (t->status) {
1590 (void) fprintf(stderr, "%p a %c%d\n", (void*)t, t->posn, t->adam);
1593 (void) fprintf(stderr, "%p T %c%d %s\n", (void*)t, t->posn, t->height,
1594 t->pattern == NULL?"":t->pattern);
1597 if (t->action == CATCH)
1598 (void) fprintf(stderr, "%p A %c%cC\n",
1600 t->hand ? 'R' : 'L');
1602 (void) fprintf(stderr, "%p A %c%c%c%d\n",
1604 t->hand ? 'R' : 'L',
1605 (t->action == THROW)?'T':'N',
1609 (void) fprintf(stderr, "%p L %c%c%c%d %d %p %p\n",
1612 (t->action == THROW)?'T':(t->action == CATCH?'C':'N'),
1613 t->height, t->object == NULL?0:t->object->color,
1614 (void*)t->handlink, (void*)t->balllink);
1617 (void) fprintf(stderr, "%p O %c%c%c%d %d %2d %6lu %6lu\n",
1620 (t->action == THROW)?'T':(t->action == CATCH?'C':'N'),
1621 t->height, t->type, t->object == NULL?0:t->object->color,
1622 t->start, t->finish);
1625 (void) fprintf(stderr, "%p B %c %2d %6lu %6lu %g\n",
1626 (void*)t, t->type == Ball?'b':t->type == Empty?'e':'f',
1627 t->object == NULL?0:t->object->color,
1628 t->start, t->finish, t->yp.c);
1631 (void) fprintf(stderr, "%p P %c %2d %6lu %6lu %g\n",
1632 (void*)t, t->type == Ball?'b':t->type == Empty?'e':'f',
1633 t->object == NULL?0:t->object->color,
1634 t->start, t->finish, t->yp.c);
1637 (void) fprintf(stderr, "%p: status %d not implemented\n",
1638 (void*)t, t->status);
1642 (void) fprintf(stderr, "---\n");
1646 static int get_num_balls(const char *j)
1652 for (p = j; *p; p++) {
1653 if (*p >= '0' && *p <='9') { /* digit */
1654 h = 10*h + (*p - '0');
1666 compare_num_balls(const void *p1, const void *p2)
1669 i = get_num_balls(((patternstruct*)p1)->pattern);
1670 j = get_num_balls(((patternstruct*)p2)->pattern);
1681 /**************************************************************************
1682 * Rendering Functions *
1684 **************************************************************************/
1687 show_arms(ModeInfo * mi)
1690 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
1693 XPoint a[countof(sp->arm[0][0])];
1700 for(side = LEFT; side <= RIGHT; side = (Hand)((int)side + 1)) {
1701 /* Translate into device coords */
1702 for(i = 0; i < countof(a); i++) {
1703 a[i].x = (short)(SCENE_WIDTH/2 + sp->arm[j][side][i].x*sp->scale);
1704 a[i].y = (short)(SCENE_HEIGHT - sp->arm[j][side][i].y*sp->scale);
1706 sp->arm[0][side][i] = sp->arm[1][side][i];
1709 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1712 polys += tube (a[2].x - (side == LEFT ? soffx : -soffx), a[2].y + soffy, 0,
1713 a[1].x, a[1].y, ARMLENGTH/2,
1714 thickness, 0, slices,
1715 True, True, MI_IS_WIREFRAME(mi));
1718 polys += tube (a[1].x, a[1].y, ARMLENGTH/2,
1719 a[0].x, a[0].y, ARMLENGTH,
1720 thickness * 0.8, 0, slices,
1721 True, True, MI_IS_WIREFRAME(mi));
1723 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
1727 glTranslatef (a[2].x - (side == LEFT ? soffx : -soffx),
1731 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1736 glTranslatef (a[1].x, a[1].y, ARMLENGTH/2);
1738 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1743 glTranslatef (a[0].x, a[0].y, ARMLENGTH);
1745 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1753 show_figure(ModeInfo * mi, Bool init)
1756 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
1777 /* #### most of this is unused now */
1778 static const XPoint figure[] = {
1779 { 15, 70}, /* 0 Left Hip */
1780 { 0, 90}, /* 1 Waist */
1781 { SX, 130}, /* 2 Left Shoulder */
1782 {-SX, 130}, /* 3 Right Shoulder */
1783 {-15, 70}, /* 4 Right Hip */
1784 { 0, 130}, /* 5 Neck */
1785 { 0, 140}, /* 6 Chin */
1786 { SX, 0}, /* 7 Left Foot */
1787 {-SX, 0}, /* 8 Right Foot */
1788 {-17, 174}, /* 9 Head1 */
1789 { 17, 140}, /* 10 Head2 */
1791 XPoint a[countof(figure)];
1792 GLfloat gcolor[4] = { 1, 1, 1, 1 };
1794 /* Translate into device coords */
1795 for(i = 0; i < countof(figure); i++) {
1796 a[i].x = (short)(SCENE_WIDTH/2 + (sp->cx + figure[i].x)*sp->scale);
1797 a[i].y = (short)(SCENE_HEIGHT - figure[i].y*sp->scale);
1800 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor);
1803 GLfloat scale = ((GLfloat) a[10].x - a[9].x) / 2;
1808 glTranslatef(a[6].x, a[6].y - scale, 0);
1809 glScalef(scale, scale, scale);
1812 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1815 glScalef(scale, scale, scale);
1816 glTranslatef(0, 0.3, 0);
1818 glTranslatef(0, 0, 0.35);
1819 polys += tube (0, 0, 0,
1822 slices, True, True, MI_IS_WIREFRAME(mi));
1824 glScalef(0.9, 0.9, 1);
1825 polys += unit_sphere(2*slices, 2*slices, MI_IS_WIREFRAME(mi));
1829 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
1830 glTranslatef(0, 1.1, 0);
1833 glScalef(scale, scale, scale);
1834 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1838 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1839 glTranslatef(0, 1.1, 0);
1841 glScalef(0.9, 1.0, 0.9);
1842 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1846 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
1847 glTranslatef(0, 1.0, 0);
1850 glScalef(scale, scale, scale);
1851 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1855 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1856 glTranslatef(0, 0.8, 0);
1859 glScalef(scale, scale, scale);
1860 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1865 glTranslatef(0, 0.7, 0);
1867 for (i = -1; i <= 1; i += 2) {
1870 glRotatef (i*10, 0, 0, 1);
1871 glTranslatef(-i*0.65, 0, 0);
1874 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
1876 glScalef(scale, scale, scale);
1877 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1880 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1882 glTranslatef(0, 0.6, 0);
1883 polys += tube (0, 0, 0,
1886 slices, True, True, MI_IS_WIREFRAME(mi));
1890 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
1892 glTranslatef(0, 4.4, 0);
1894 glScalef(scale, scale, scale);
1895 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1899 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1901 glTranslatef(0, 4.7, 0);
1902 polys += tube (0, 0, 0,
1905 slices, True, True, MI_IS_WIREFRAME(mi));
1909 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
1911 glTranslatef(0, 9.7, 0);
1913 glScalef(scale, scale, scale);
1914 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1918 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1920 glRotatef (-i*10, 0, 0, 1);
1921 glTranslatef(-i*1.75, 9.7, 0.9);
1923 glScalef (0.4, 1, 1);
1924 polys += tube (0, 0, 0,
1927 slices*4, True, True, MI_IS_WIREFRAME(mi));
1936 sp->arm[1][LEFT][SHOULDER].x = sp->cx + figure[2].x;
1937 sp->arm[1][RIGHT][SHOULDER].x = sp->cx + figure[3].x;
1939 /* Initialise arms */
1941 for(i = 0; i < 2; i++){
1942 sp->arm[i][LEFT][SHOULDER].y = figure[2].y;
1943 sp->arm[i][LEFT][ELBOW].x = figure[2].x;
1944 sp->arm[i][LEFT][ELBOW].y = figure[1].y;
1945 sp->arm[i][LEFT][HAND].x = figure[0].x;
1946 sp->arm[i][LEFT][HAND].y = figure[1].y;
1947 sp->arm[i][RIGHT][SHOULDER].y = figure[3].y;
1948 sp->arm[i][RIGHT][ELBOW].x = figure[3].x;
1949 sp->arm[i][RIGHT][ELBOW].y = figure[1].y;
1950 sp->arm[i][RIGHT][HAND].x = figure[4].x;
1951 sp->arm[i][RIGHT][HAND].y = figure[1].y;
1957 typedef struct { GLfloat x, y, z; } XYZ;
1959 /* lifted from sphere.c */
1961 striped_unit_sphere (int stacks, int slices,
1963 GLfloat *color1, GLfloat *color2,
1968 double theta1, theta2, theta3;
1970 XYZ la = { 0, 0, 0 }, lb = { 0, 0, 0 };
1971 XYZ c = {0, 0, 0}; /* center */
1972 double r = 1.0; /* radius */
1973 int stacks2 = stacks * 2;
1980 if (slices < 4 || stacks < 2 || r <= 0)
1982 glBegin (GL_POINTS);
1983 glVertex3f (c.x, c.y, c.z);
1990 for (j = 0; j < stacks; j++)
1992 theta1 = j * (M_PI+M_PI) / stacks2 - M_PI_2;
1993 theta2 = (j + 1) * (M_PI+M_PI) / stacks2 - M_PI_2;
1995 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
1996 ((j == 0 || j == stacks-1 ||
1997 j % (stacks / (stripes+1)))
1998 ? color1 : color2));
2000 glBegin (wire_p ? GL_LINE_LOOP : GL_TRIANGLE_STRIP);
2001 for (i = 0; i <= slices; i++)
2003 theta3 = i * (M_PI+M_PI) / slices;
2005 if (wire_p && i != 0)
2007 glVertex3f (lb.x, lb.y, lb.z);
2008 glVertex3f (la.x, la.y, la.z);
2011 e.x = cos (theta2) * cos(theta3);
2013 e.z = cos (theta2) * sin(theta3);
2014 p.x = c.x + r * e.x;
2015 p.y = c.y + r * e.y;
2016 p.z = c.z + r * e.z;
2018 glNormal3f (e.x, e.y, e.z);
2019 glTexCoord2f (i / (double)slices,
2020 2*(j+1) / (double)stacks2);
2021 glVertex3f (p.x, p.y, p.z);
2024 e.x = cos(theta1) * cos(theta3);
2026 e.z = cos(theta1) * sin(theta3);
2027 p.x = c.x + r * e.x;
2028 p.y = c.y + r * e.y;
2029 p.z = c.z + r * e.z;
2031 glNormal3f (e.x, e.y, e.z);
2032 glTexCoord2f (i / (double)slices,
2033 2*j / (double)stacks2);
2034 glVertex3f (p.x, p.y, p.z);
2046 show_ball(ModeInfo *mi, unsigned long color, Trace *s)
2049 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2050 /*int offset = (int)(s->angle*64*180/M_PI);*/
2051 short x = (short)(SCENE_WIDTH/2 + s->x * sp->scale);
2052 short y = (short)(SCENE_HEIGHT - s->y * sp->scale);
2053 GLfloat gcolor1[4] = { 0, 0, 0, 1 };
2054 GLfloat gcolor2[4] = { 0, 0, 0, 1 };
2057 /* Avoid wrapping */
2058 if(s->y*sp->scale > SCENE_HEIGHT * 2) return 0;
2060 gcolor1[0] = mi->colors[color].red / 65536.0;
2061 gcolor1[1] = mi->colors[color].green / 65536.0;
2062 gcolor1[2] = mi->colors[color].blue / 65536.0;
2064 gcolor2[0] = gcolor1[0] / 3;
2065 gcolor2[1] = gcolor1[1] / 3;
2066 gcolor2[2] = gcolor1[2] / 3;
2069 GLfloat scale = BALLRADIUS;
2071 glTranslatef(x, y, 0);
2072 glScalef(scale, scale, scale);
2074 glRotatef (s->angle / M_PI*180, 1, 1, 0);
2076 polys += striped_unit_sphere (slices, slices, s->divisions,
2077 gcolor1, gcolor2, MI_IS_WIREFRAME(mi));
2084 show_europeanclub(ModeInfo *mi, unsigned long color, Trace *s)
2087 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2088 /*int offset = (int)(s->angle*64*180/M_PI);*/
2089 short x = (short)(SCENE_WIDTH/2 + s->x * sp->scale);
2090 short y = (short)(SCENE_HEIGHT - s->y * sp->scale);
2091 double radius = 12 * sp->scale;
2092 GLfloat gcolor1[4] = { 0, 0, 0, 1 };
2093 GLfloat gcolor2[4] = { 1, 1, 1, 1 };
2095 int divs = s->divisions;
2116 /* Avoid wrapping */
2117 if(s->y*sp->scale > SCENE_HEIGHT * 2) return 0;
2119 gcolor1[0] = mi->colors[color].red / 65536.0;
2120 gcolor1[1] = mi->colors[color].green / 65536.0;
2121 gcolor1[2] = mi->colors[color].blue / 65536.0;
2124 GLfloat scale = radius;
2126 glTranslatef(x, y, 0);
2127 glScalef(scale, scale, scale);
2129 glTranslatef (0, 0, 2); /* put end of handle in hand */
2131 glRotatef (s->angle / M_PI*180, 1, 0, 0);
2134 glScalef (0.5, 1, 0.5);
2135 polys += striped_unit_sphere (slices, slices, divs, gcolor2, gcolor1,
2136 MI_IS_WIREFRAME(mi));
2138 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor2);
2139 polys += tube (0, 0, 0,
2142 slices, True, True, MI_IS_WIREFRAME(mi));
2144 glTranslatef (0, 2, 0);
2145 glScalef (0.25, 0.25, 0.25);
2146 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
2155 show_torch(ModeInfo *mi, unsigned long color, Trace *s)
2159 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2160 XPoint head, tail, last;
2161 DXPoint dhead, dlast;
2162 const double sa = sin(s->angle);
2163 const double ca = cos(s->angle);
2165 const double TailLen = -24;
2166 const double HeadLen = 16;
2167 const short Width = (short)(5 * sqrt(sp->scale));
2179 dhead.x = s->x + HeadLen * PERSPEC * sa;
2180 dhead.y = s->y - HeadLen * ca;
2182 if(color == MI_BLACK_PIXEL(mi)) { /* Use 'last' when erasing */
2184 } else { /* Store 'last' so we can use it later when s->prev has
2186 if(s->prev != s->next) {
2187 dlast.x = s->prev->x + HeadLen * PERSPEC * sin(s->prev->angle);
2188 dlast.y = s->prev->y - HeadLen * cos(s->prev->angle);
2195 /* Avoid wrapping (after last is stored) */
2196 if(s->y*sp->scale > SCENE_HEIGHT * 2) return 0;
2198 head.x = (short)(SCENE_WIDTH/2 + dhead.x*sp->scale);
2199 head.y = (short)(SCENE_HEIGHT - dhead.y*sp->scale);
2201 last.x = (short)(SCENE_WIDTH/2 + dlast.x*sp->scale);
2202 last.y = (short)(SCENE_HEIGHT - dlast.y*sp->scale);
2204 tail.x = (short)(SCENE_WIDTH/2 +
2205 (s->x + TailLen * PERSPEC * sa)*sp->scale );
2206 tail.y = (short)(SCENE_HEIGHT - (s->y - TailLen * ca)*sp->scale );
2208 if(color != MI_BLACK_PIXEL(mi)) {
2209 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
2210 XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi),
2211 Width, LineSolid, CapRound, JoinRound);
2212 draw_line(mi, head.x, head.y, tail.x, tail.y);
2214 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), color);
2215 XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi),
2216 Width * 2, LineSolid, CapRound, JoinRound);
2218 draw_line(mi, head.x, head.y, last.x, last.y);
2226 show_knife(ModeInfo *mi, unsigned long color, Trace *s)
2229 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2230 /*int offset = (int)(s->angle*64*180/M_PI);*/
2231 short x = (short)(SCENE_WIDTH/2 + s->x * sp->scale);
2232 short y = (short)(SCENE_HEIGHT - s->y * sp->scale);
2233 GLfloat gcolor1[4] = { 0, 0, 0, 1 };
2234 GLfloat gcolor2[4] = { 1, 1, 1, 1 };
2237 /* Avoid wrapping */
2238 if(s->y*sp->scale > SCENE_HEIGHT * 2) return 0;
2240 gcolor1[0] = mi->colors[color].red / 65536.0;
2241 gcolor1[1] = mi->colors[color].green / 65536.0;
2242 gcolor1[2] = mi->colors[color].blue / 65536.0;
2245 glTranslatef(x, y, 0);
2248 glTranslatef (0, 0, 2); /* put end of handle in hand */
2249 glRotatef (s->angle / M_PI*180, 1, 0, 0);
2251 glScalef (0.3, 1, 1); /* flatten blade */
2253 glTranslatef(0, 6, 0);
2254 glRotatef (180, 1, 0, 0);
2256 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor1);
2257 polys += tube (0, 0, 0,
2260 slices, True, True, MI_IS_WIREFRAME(mi));
2262 glTranslatef (0, 12, 0);
2263 glScalef (0.7, 10, 0.7);
2264 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor2);
2265 polys += unit_sphere (slices, slices, MI_IS_WIREFRAME(mi));
2273 show_ring(ModeInfo *mi, unsigned long color, Trace *s)
2276 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2277 /*int offset = (int)(s->angle*64*180/M_PI);*/
2278 short x = (short)(SCENE_WIDTH/2 + s->x * sp->scale);
2279 short y = (short)(SCENE_HEIGHT - s->y * sp->scale);
2280 double radius = 12 * sp->scale;
2281 GLfloat gcolor1[4] = { 0, 0, 0, 1 };
2282 GLfloat gcolor2[4] = { 0, 0, 0, 1 };
2285 int wire_p = MI_IS_WIREFRAME(mi);
2286 GLfloat width = M_PI * 2 / slices;
2289 GLfloat thickness = 0.15;
2291 /* Avoid wrapping */
2292 if(s->y*sp->scale > SCENE_HEIGHT * 2) return 0;
2294 gcolor1[0] = mi->colors[color].red / 65536.0;
2295 gcolor1[1] = mi->colors[color].green / 65536.0;
2296 gcolor1[2] = mi->colors[color].blue / 65536.0;
2298 gcolor2[0] = gcolor1[0] / 3;
2299 gcolor2[1] = gcolor1[1] / 3;
2300 gcolor2[2] = gcolor1[2] / 3;
2303 glTranslatef(0, 0, 12); /* back of ring in hand */
2305 glTranslatef(x, y, 0);
2306 glScalef(radius, radius, radius);
2308 glRotatef (90, 0, 1, 0);
2309 glRotatef (s->angle / M_PI*180, 0, 0, 1);
2311 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor1);
2314 for (j = -1; j <= 1; j += 2)
2316 GLfloat z = j * thickness/2;
2317 glFrontFace (j < 0 ? GL_CCW : GL_CW);
2318 glNormal3f (0, 0, j*1);
2319 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
2320 for (i = 0; i < slices + (wire_p ? 0 : 1); i++) {
2321 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
2322 (i % (slices/3) ? gcolor1 : gcolor2));
2323 GLfloat th = i * width;
2324 GLfloat cth = cos(th);
2325 GLfloat sth = sin(th);
2326 glVertex3f (cth * ra, sth * ra, z);
2327 glVertex3f (cth * rb, sth * rb, z);
2334 glFrontFace (GL_CCW);
2335 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
2336 for (i = 0; i < slices + (wire_p ? 0 : 1); i++)
2338 GLfloat th = i * width;
2339 GLfloat cth = cos(th);
2340 GLfloat sth = sin(th);
2341 glNormal3f (cth, sth, 0);
2342 glVertex3f (cth * ra, sth * ra, thickness/2);
2343 glVertex3f (cth * ra, sth * ra, -thickness/2);
2349 glFrontFace (GL_CW);
2350 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
2351 for (i = 0; i < slices + (wire_p ? 0 : 1); i++)
2353 GLfloat th = i * width;
2354 GLfloat cth = cos(th);
2355 GLfloat sth = sin(th);
2356 glNormal3f (-cth, -sth, 0);
2357 glVertex3f (cth * rb, sth * ra, thickness/2);
2358 glVertex3f (cth * rb, sth * ra, -thickness/2);
2363 glFrontFace (GL_CCW);
2370 show_bball(ModeInfo *mi, unsigned long color, Trace *s)
2373 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2374 /*int offset = (int)(s->angle*64*180/M_PI);*/
2375 short x = (short)(SCENE_WIDTH/2 + s->x * sp->scale);
2376 short y = (short)(SCENE_HEIGHT - s->y * sp->scale);
2377 double radius = 12 * sp->scale;
2378 GLfloat gcolor1[4] = { 0, 0, 0, 1 };
2379 GLfloat gcolor2[4] = { 0, 0, 0, 1 };
2383 /* Avoid wrapping */
2384 if(s->y*sp->scale > SCENE_HEIGHT * 2) return 0;
2386 gcolor1[0] = mi->colors[color].red / 65536.0;
2387 gcolor1[1] = mi->colors[color].green / 65536.0;
2388 gcolor1[2] = mi->colors[color].blue / 65536.0;
2391 GLfloat scale = radius;
2394 glTranslatef(0, -6, 5); /* position on top of hand */
2396 glTranslatef(x, y, 0);
2397 glScalef(scale, scale, scale);
2398 glRotatef (s->angle / M_PI*180, 1, 0, 1);
2400 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor1);
2401 polys += unit_sphere (slices, slices, MI_IS_WIREFRAME(mi));
2403 glRotatef (90, 0, 0, 1);
2404 glTranslatef (0, 0, 0.81);
2405 glScalef(0.15, 0.15, 0.15);
2406 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor2);
2407 for (i = 0; i < 3; i++) {
2409 glTranslatef (0, 0, 1);
2410 glRotatef (360 * i / 3, 0, 0, 1);
2411 glTranslatef (2, 0, 0);
2412 glRotatef (18, 0, 1, 0);
2413 glBegin (MI_IS_WIREFRAME(mi) ? GL_LINE_LOOP : GL_TRIANGLE_FAN);
2414 glVertex3f (0, 0, 0);
2415 for (j = slices; j >= 0; j--) {
2416 GLfloat th = j * M_PI*2 / slices;
2417 glVertex3f (cos(th), sin(th), 0);
2430 /**************************************************************************
2431 * Public Functions *
2433 **************************************************************************/
2437 release_juggle (ModeInfo * mi)
2439 if (juggles != NULL) {
2442 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
2443 free_juggle(&juggles[screen]);
2445 juggles = (jugglestruct *) NULL;
2449 /* FIXME: refill_juggle currently just appends new throws to the
2450 * programme. This is fine if the programme is empty, but if there
2451 * are still some trajectories left then it really should take these
2455 refill_juggle(ModeInfo * mi)
2457 jugglestruct *sp = NULL;
2460 if (juggles == NULL)
2462 sp = &juggles[MI_SCREEN(mi)];
2464 /* generate pattern */
2466 if (pattern == NULL) {
2469 #define MAXREPEAT 300
2470 #define CHANGE_BIAS 8 /* larger makes num_ball changes less likely */
2471 #define POSITION_BIAS 20 /* larger makes hand movements less likely */
2474 while (count < MI_CYCLES(mi)) {
2475 char buf[MAXPAT * 3 + 3], *b = buf;
2477 int l = NRAND(MAXPAT) + 1;
2478 int t = NRAND(MIN(MAXREPEAT, (MI_CYCLES(mi) - count))) + 1;
2480 { /* vary number of balls */
2481 int new_balls = sp->num_balls;
2484 if (new_balls == 2) /* Do not juggle 2 that often */
2485 change = NRAND(2 + CHANGE_BIAS / 4);
2487 change = NRAND(2 + CHANGE_BIAS);
2498 if (new_balls < sp->patternindex.minballs) {
2501 if (new_balls > sp->patternindex.maxballs) {
2504 if (new_balls < sp->num_balls) {
2505 if (!program(mi, "[*]", NULL, 1)) /* lose ball */
2508 sp->num_balls = new_balls;
2512 if (NRAND(2) && sp->patternindex.index[sp->num_balls].number) {
2513 /* Pick from PortFolio */
2514 int p = sp->patternindex.index[sp->num_balls].start +
2515 NRAND(sp->patternindex.index[sp->num_balls].number);
2516 if (!program(mi, portfolio[p].pattern, portfolio[p].name, t))
2519 /* Invent a new pattern */
2521 for(i = 0; i < l; i++){
2523 do { /* Triangular Distribution => high values more likely */
2524 m = NRAND(sp->num_balls + 1);
2525 n = NRAND(sp->num_balls + 1);
2527 if (n == sp->num_balls) {
2530 switch(NRAND(5 + POSITION_BIAS)){
2531 case 0: /* Outside throw */
2533 case 1: /* Cross throw */
2535 case 2: /* Cross catch */
2537 case 3: /* Cross throw and catch */
2539 case 4: /* Bounce */
2542 break; /* Inside throw (default) */
2551 if (!program(mi, buf, NULL, t))
2556 } else { /* pattern supplied in height or 'a' notation */
2557 if (!program(mi, pattern, NULL, MI_CYCLES(mi)))
2574 if (!projectile(sp)) {
2581 if(MI_IS_DEBUG(mi)) dump(sp);
2586 change_juggle(ModeInfo * mi)
2588 jugglestruct *sp = NULL;
2591 if (juggles == NULL)
2593 sp = &juggles[MI_SCREEN(mi)];
2595 /* Strip pending trajectories */
2596 for (t = sp->head->next; t != sp->head; t = t->next) {
2597 if(t->start > sp->time || t->finish < sp->time) {
2600 trajectory_destroy(n);
2604 /* Pick the current object theme */
2605 sp->objtypes = choose_object();
2609 mi->polygon_count += show_figure(mi, True);
2614 reshape_juggle (ModeInfo *mi, int width, int height)
2616 GLfloat h = (GLfloat) height / (GLfloat) width;
2618 glViewport (0, 0, (GLint) width, (GLint) height);
2620 glMatrixMode(GL_PROJECTION);
2622 gluPerspective (30.0, 1/h, 1.0, 100.0);
2624 glMatrixMode(GL_MODELVIEW);
2626 gluLookAt( 0.0, 0.0, 30.0,
2630 glClear(GL_COLOR_BUFFER_BIT);
2635 init_juggle (ModeInfo * mi)
2637 jugglestruct *sp = 0;
2638 int wire = MI_IS_WIREFRAME(mi);
2641 juggles = (jugglestruct *)
2642 calloc (MI_NUM_SCREENS(mi), sizeof (jugglestruct));
2644 fprintf(stderr, "%s: out of memory\n", progname);
2649 sp = &juggles[MI_SCREEN(mi)];
2651 sp->glx_context = init_GL(mi);
2653 load_font (mi->dpy, "titleFont", &sp->mode_font, &sp->font_dlist);
2655 reshape_juggle (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
2659 GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
2660 GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
2661 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
2662 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
2664 glEnable(GL_LIGHTING);
2665 glEnable(GL_LIGHT0);
2666 glEnable(GL_DEPTH_TEST);
2667 glEnable(GL_CULL_FACE);
2669 glLightfv(GL_LIGHT0, GL_POSITION, pos);
2670 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
2671 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
2672 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
2675 make_random_colormap (0, 0, 0,
2676 mi->colors, &MI_NPIXELS(mi),
2677 True, False, 0, False);
2680 double spin_speed = 0.05;
2681 double wander_speed = 0.001;
2682 double spin_accel = 0.05;
2683 sp->rot = make_rotator (0, spin_speed, 0,
2684 spin_accel, wander_speed, False);
2685 sp->trackball = gltrackball_init ();
2688 if (only && *only && strcmp(only, " ")) {
2689 balls = clubs = torches = knives = rings = bballs = False;
2690 if (!strcasecmp (only, "balls")) balls = True;
2691 else if (!strcasecmp (only, "clubs")) clubs = True;
2692 else if (!strcasecmp (only, "torches")) torches = True;
2693 else if (!strcasecmp (only, "knives")) knives = True;
2694 else if (!strcasecmp (only, "rings")) rings = True;
2695 else if (!strcasecmp (only, "bballs")) bballs = True;
2697 (void) fprintf (stderr,
2698 "Juggle: -only must be one of: balls, clubs, torches, knives,\n"
2699 "\t rings, or bballs (not \"%s\")\n", only);
2700 #ifdef STANDALONE /* xlock mustn't exit merely because of a bad argument */
2706 /* #### hard to make this look good in OpenGL... */
2710 if (sp->head == 0) { /* first time initializing this juggler */
2712 sp->count = ABS(MI_COUNT(mi));
2716 /* record start time */
2717 sp->begintime = time(NULL);
2718 if(sp->patternindex.maxballs > 0) {
2719 sp->num_balls = sp->patternindex.minballs +
2720 NRAND(sp->patternindex.maxballs - sp->patternindex.minballs);
2723 mi->polygon_count +=
2724 show_figure(mi, True); /* Draw figure. Also discovers
2725 information about the juggler's
2728 /* "7" should be about three times the height of the juggler's
2730 sp->Gr = -GRAVITY(3 * sp->arm[0][RIGHT][SHOULDER].y,
2731 7 * THROW_CATCH_INTERVAL);
2733 if(!balls && !clubs && !torches && !knives && !rings && !bballs)
2734 balls = True; /* Have to juggle something! */
2736 /* create circular trajectory list */
2737 ADD_ELEMENT(Trajectory, sp->head, sp->head);
2738 if(sp->head == NULL){
2743 /* create circular object list */
2744 ADD_ELEMENT(Object, sp->objects, sp->objects);
2745 if(sp->objects == NULL){
2750 sp->pattern = strdup(""); /* Initialise saved pattern with
2754 sp = &juggles[MI_SCREEN(mi)];
2758 !strcasecmp (pattern, ".") ||
2759 !strcasecmp (pattern, "random")))
2762 if (pattern == NULL && sp->patternindex.maxballs == 0) {
2763 /* pattern list needs indexing */
2764 int nelements = countof(portfolio);
2768 /* sort according to number of balls */
2769 qsort((void*)portfolio, nelements,
2770 sizeof(portfolio[1]), compare_num_balls);
2772 /* last pattern has most balls */
2773 sp->patternindex.maxballs = get_num_balls(portfolio[nelements - 1].pattern);
2774 /* run through sorted list, indexing start of each group
2775 and number in group */
2776 sp->patternindex.maxballs = 1;
2777 for (i = 0; i < nelements; i++) {
2778 int b = get_num_balls(portfolio[i].pattern);
2779 if (b > sp->patternindex.maxballs) {
2780 sp->patternindex.index[sp->patternindex.maxballs].number = numpat;
2781 if(numpat == 0) sp->patternindex.minballs = b;
2782 sp->patternindex.maxballs = b;
2784 sp->patternindex.index[sp->patternindex.maxballs].start = i;
2789 sp->patternindex.index[sp->patternindex.maxballs].number = numpat;
2792 /* Set up programme */
2795 /* Only put things here that won't interrupt the programme during
2798 /* Use MIN so that users can resize in interesting ways, eg
2799 narrow windows for tall patterns, etc */
2800 sp->scale = MIN(SCENE_HEIGHT/480.0, SCENE_WIDTH/160.0);
2805 juggle_handle_event (ModeInfo *mi, XEvent *event)
2807 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2809 if (event->xany.type == ButtonPress &&
2810 event->xbutton.button == Button1)
2812 sp->button_down_p = True;
2813 gltrackball_start (sp->trackball,
2814 event->xbutton.x, event->xbutton.y,
2815 MI_WIDTH (mi), MI_HEIGHT (mi));
2818 else if (event->xany.type == ButtonRelease &&
2819 event->xbutton.button == Button1)
2821 sp->button_down_p = False;
2824 else if (event->xany.type == ButtonPress &&
2825 (event->xbutton.button == Button4 ||
2826 event->xbutton.button == Button5 ||
2827 event->xbutton.button == Button6 ||
2828 event->xbutton.button == Button7))
2830 gltrackball_mousewheel (sp->trackball, event->xbutton.button, 10,
2831 !!event->xbutton.state);
2834 else if (event->xany.type == MotionNotify &&
2837 gltrackball_track (sp->trackball,
2838 event->xmotion.x, event->xmotion.y,
2839 MI_WIDTH (mi), MI_HEIGHT (mi));
2842 else if (event->xany.type == KeyPress)
2846 XLookupString (&event->xkey, &c, 1, &keysym, 0);
2847 if (c == ' ' || c == '\n' || c == '\r' || c == '\t')
2860 draw_juggle (ModeInfo *mi)
2862 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2863 Display *dpy = MI_DISPLAY(mi);
2864 Window window = MI_WINDOW(mi);
2866 Trajectory *traj = NULL;
2868 unsigned long future = 0;
2869 char *pattern = NULL;
2871 if (!sp->glx_context)
2874 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sp->glx_context));
2876 glShadeModel(GL_SMOOTH);
2878 glEnable(GL_DEPTH_TEST);
2879 glEnable(GL_NORMALIZE);
2880 glEnable(GL_CULL_FACE);
2882 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
2886 glTranslatef(0,-3,0);
2890 get_position (sp->rot, &x, &y, &z, !sp->button_down_p);
2891 glTranslatef((x - 0.5) * 8,
2895 gltrackball_rotate (sp->trackball);
2897 get_rotation (sp->rot, &x, &y, &z, !sp->button_down_p);
2899 if (y < 0.8) y = 0.8 - (y - 0.8); /* always face forward */
2900 if (y > 1.2) y = 1.2 - (y - 1.2);
2902 glRotatef (x * 360, 1.0, 0.0, 0.0);
2903 glRotatef (y * 360, 0.0, 1.0, 0.0);
2904 glRotatef (z * 360, 0.0, 0.0, 1.0);
2908 GLfloat scale = 20.0 / SCENE_HEIGHT;
2909 glScalef(scale, scale, scale);
2912 glRotatef (180, 0, 0, 1);
2913 glTranslatef(-SCENE_WIDTH/2, -SCENE_HEIGHT/2, 0);
2914 glTranslatef(0, -150, 0);
2916 mi->polygon_count = 0;
2921 (void)gettimeofday(&tv, NULL);
2922 sp->time = (int) ((tv.tv_sec - sp->begintime)*1000 + tv.tv_usec/1000);
2924 sp->time += MI_DELAY(mi) / 1000;
2927 /* First pass: Move arms and strip out expired elements */
2928 for (traj = sp->head->next; traj != sp->head; traj = traj->next) {
2929 if (traj->status != PREDICTOR) {
2930 /* Skip any elements that need further processing */
2931 /* We could remove them, but there shoudn't be many and they
2932 would be needed if we ever got the pattern refiller
2936 if (traj->start > future) { /* Lookahead to the end of the show */
2937 future = traj->start;
2939 if (sp->time < traj->start) { /* early */
2941 } else if (sp->time < traj->finish) { /* working */
2943 /* Look for pattern name */
2944 if(traj->pattern != NULL) {
2945 pattern=traj->pattern;
2948 if (traj->type == Empty || traj->type == Full) {
2949 /* Only interested in hands on this pass */
2950 /* double angle = traj->angle + traj->spin * (sp->time - traj->start);*/
2951 double xd = 0, yd = 0;
2954 /* Find the catching offset */
2955 if(traj->object != NULL) {
2957 /* #### not sure what this is doing, but I'm guessing
2958 that the use of PERSPEC means this isn't needed
2959 in the OpenGL version? -jwz
2961 if(ObjectDefs[traj->object->type].handle > 0) {
2962 /* Handles Need to be oriented */
2963 xd = ObjectDefs[traj->object->type].handle *
2964 PERSPEC * sin(angle);
2965 yd = ObjectDefs[traj->object->type].handle *
2970 /* Balls are always caught at the bottom */
2975 p.x = (CUBIC(traj->xp, sp->time) - xd);
2976 p.y = (CUBIC(traj->yp, sp->time) + yd);
2977 reach_arm(mi, traj->hand, &p);
2979 /* Store updated hand position */
2983 if (traj->type == Ball || traj->type == Full) {
2984 /* Only interested in objects on this pass */
2988 if(traj->type == Full) {
2989 /* Adjusted these in the first pass */
2993 x = CUBIC(traj->xp, sp->time);
2994 y = CUBIC(traj->yp, sp->time);
2997 ADD_ELEMENT(Trace, s, traj->object->trace->prev);
3000 s->angle = traj->angle + traj->spin * (sp->time - traj->start);
3001 s->divisions = traj->divisions;
3002 traj->object->tracelen++;
3003 traj->object->active = True;
3005 } else { /* expired */
3006 Trajectory *n = traj;
3008 trajectory_destroy(n);
3013 mi->polygon_count += show_figure(mi, False);
3014 mi->polygon_count += show_arms(mi);
3017 glTranslatef(0, 0, ARMLENGTH);
3018 for (o = sp->objects->next; o != sp->objects; o = o->next) {
3020 mi->polygon_count += ObjectDefs[o->type].draw(mi, o->color,
3027 /* Save pattern name so we can erase it when it changes */
3028 if(pattern != NULL && strcmp(sp->pattern, pattern) != 0 ) {
3030 sp->pattern = strdup(pattern);
3032 if (MI_IS_VERBOSE(mi)) {
3033 (void) fprintf(stderr, "Juggle[%d]: Running: %s\n",
3034 MI_SCREEN(mi), sp->pattern);
3038 if(sp->mode_font != None) {
3039 print_gl_string (mi->dpy, sp->mode_font, sp->font_dlist,
3040 mi->xgwa.width, mi->xgwa.height,
3041 10, mi->xgwa.height - 10,
3042 sp->pattern, False);
3046 if((int)(sp->time/10) % 1000 == 0)
3047 (void) fprintf(stderr, "sbrk: %d\n", (int)sbrk(0));
3050 if (future < sp->time + 100 * THROW_CATCH_INTERVAL) {
3052 } else if (sp->time > 1<<30) { /* Hard Reset before the clock wraps */
3059 if (mi->fps_p) do_fps (mi);
3062 glXSwapBuffers(dpy, window);
3065 XSCREENSAVER_MODULE_2 ("Juggler3D", juggler3d, juggle)