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
139 # define release_juggle 0
141 #define countof(x) (sizeof((x))/sizeof((*x)))
143 #include "xlockmore.h"
147 #include "gltrackball.h"
151 #ifdef USE_GL /* whole file */
153 #define DEF_PATTERN "random" /* All patterns */
154 #define DEF_TAIL "1" /* No trace */
156 /* Maybe a ROLA BOLA would be at a better angle for viewing */
157 #define DEF_UNI "False" /* No unicycle */ /* Not implemented yet */
159 #define DEF_REAL "True"
160 #define DEF_DESCRIBE "True"
162 #define DEF_BALLS "True" /* Use Balls */
163 #define DEF_CLUBS "True" /* Use Clubs */
164 #define DEF_TORCHES "True" /* Use Torches */
165 #define DEF_KNIVES "True" /* Use Knives */
166 #define DEF_RINGS "True" /* Use Rings */
167 #define DEF_BBALLS "True" /* Use Bowling Balls */
169 static char *pattern;
175 static Bool describe;
184 static XrmOptionDescRec opts[] = {
185 {"-pattern", ".juggle.pattern", XrmoptionSepArg, NULL },
186 {"-tail", ".juggle.tail", XrmoptionSepArg, NULL },
188 {"-uni", ".juggle.uni", XrmoptionNoArg, "on" },
189 {"+uni", ".juggle.uni", XrmoptionNoArg, "off" },
191 {"-real", ".juggle.real", XrmoptionNoArg, "on" },
192 {"+real", ".juggle.real", XrmoptionNoArg, "off" },
193 {"-describe", ".juggle.describe", XrmoptionNoArg, "on" },
194 {"+describe", ".juggle.describe", XrmoptionNoArg, "off" },
195 {"-balls", ".juggle.balls", XrmoptionNoArg, "on" },
196 {"+balls", ".juggle.balls", XrmoptionNoArg, "off" },
197 {"-clubs", ".juggle.clubs", XrmoptionNoArg, "on" },
198 {"+clubs", ".juggle.clubs", XrmoptionNoArg, "off" },
199 {"-torches", ".juggle.torches", XrmoptionNoArg, "on" },
200 {"+torches", ".juggle.torches", XrmoptionNoArg, "off" },
201 {"-knives", ".juggle.knives", XrmoptionNoArg, "on" },
202 {"+knives", ".juggle.knives", XrmoptionNoArg, "off" },
203 {"-rings", ".juggle.rings", XrmoptionNoArg, "on" },
204 {"+rings", ".juggle.rings", XrmoptionNoArg, "off" },
205 {"-bballs", ".juggle.bballs", XrmoptionNoArg, "on" },
206 {"+bballs", ".juggle.bballs", XrmoptionNoArg, "off" },
207 {"-only", ".juggle.only", XrmoptionSepArg, NULL },
210 static argtype vars[] = {
211 { &pattern, "pattern", "Pattern", DEF_PATTERN, t_String },
212 { &tail, "tail", "Tail", DEF_TAIL, t_Int },
214 { &uni, "uni", "Uni", DEF_UNI, t_Bool },
216 { &real, "real", "Real", DEF_REAL, t_Bool },
217 { &describe, "describe", "Describe", DEF_DESCRIBE, t_Bool },
218 { &balls, "balls", "Clubs", DEF_BALLS, t_Bool },
219 { &clubs, "clubs", "Clubs", DEF_CLUBS, t_Bool },
220 { &torches, "torches", "Torches", DEF_TORCHES, t_Bool },
221 { &knives, "knives", "Knives", DEF_KNIVES, t_Bool },
222 { &rings, "rings", "Rings", DEF_RINGS, t_Bool },
223 { &bballs, "bballs", "BBalls", DEF_BBALLS, t_Bool },
224 { &only, "only", "BBalls", " ", t_String },
227 static OptionStruct desc[] =
229 { "-pattern string", "Cambridge Juggling Pattern" },
230 { "-tail num", "Trace Juggling Patterns" },
232 { "-/+uni", "Unicycle" },
234 { "-/+real", "Real-time" },
235 { "-/+describe", "turn on/off pattern descriptions." },
236 { "-/+balls", "turn on/off Balls." },
237 { "-/+clubs", "turn on/off Clubs." },
238 { "-/+torches", "turn on/off Flaming Torches." },
239 { "-/+knives", "turn on/off Knives." },
240 { "-/+rings", "turn on/off Rings." },
241 { "-/+bballs", "turn on/off Bowling Balls." },
242 { "-only", "Turn off all objects but the named one." },
245 ENTRYPOINT ModeSpecOpt juggle_opts =
246 {countof(opts), opts, countof(vars), vars, desc};
249 /* Note: All "lengths" are scaled by sp->scale = MI_HEIGHT/480. All
250 "thicknesses" are scaled by sqrt(sp->scale) so that they are
251 proportionally thicker for smaller windows. Objects spinning out
252 of the plane (such as clubs) fake perspective by compressing their
253 horizontal coordinates by PERSPEC */
257 #define ARMWIDTH ((int) (8.0 * sqrt(sp->scale)))
259 #define BALLRADIUS ARMWIDTH
261 /* build all the models assuming a 480px high scene */
262 #define SCENE_HEIGHT 480
263 #define SCENE_WIDTH ((int)(SCENE_HEIGHT*(MI_WIDTH(mi)/(float)MI_HEIGHT(mi))))
265 /*#define PERSPEC 0.4*/
268 #define GRAVITY(h, t) 4*(double)(h)/((t)*(t))
270 /* Timing based on count. Units are milliseconds. Juggles per second
271 is: 2000 / THROW_CATCH_INTERVAL + CATCH_THROW_INTERVAL */
273 #define THROW_CATCH_INTERVAL (sp->count)
274 #define THROW_NULL_INTERVAL (sp->count * 0.5)
275 #define CATCH_THROW_INTERVAL (sp->count * 0.2)
277 /********************************************************************
278 * Trace Definitions *
280 * These record rendering data so that a drawn object can be erased *
281 * later. Each object has its own Trace list. *
283 ********************************************************************/
285 typedef struct {double x, y; } DXPoint;
286 typedef struct trace *TracePtr;
287 typedef struct trace {
298 /*******************************************************************
299 * Object Definitions *
301 * These describe the various types of Object that can be juggled *
303 *******************************************************************/
304 typedef int (DrawProc)(ModeInfo*, unsigned long, Trace *);
306 static DrawProc show_ball, show_europeanclub, show_torch, show_knife;
307 static DrawProc show_ring, show_bball;
309 typedef enum {BALL, CLUB, TORCH, KNIFE, RING, BBALLS,
310 NUM_OBJECT_TYPES} ObjType;
312 #define OBJMIXPROB 20 /* inverse of the chances of using an odd
313 object in the pattern */
315 static const GLfloat body_color_1[4] = { 0.9, 0.7, 0.5, 1 };
316 static const GLfloat body_color_2[4] = { 0.6, 0.4, 0.2, 1 };
318 static const struct {
319 DrawProc *draw; /* Object Rendering function */
320 int handle; /* Length of object's handle */
321 int mintrail; /* Minimum trail length */
322 double cor; /* Coefficient of Restitution. perfect bounce = 1 */
323 double weight; /* Heavier objects don't get thrown as high */
336 0.55, /* Clubs don't bounce too well */
342 20, /* Torches need flames */
343 0, /* Torches don't bounce -- fire risk! */
350 0, /* Knives don't bounce */
369 /**************************
370 * Trajectory definitions *
371 **************************/
373 typedef enum {HEIGHT, ADAM} Notation;
374 typedef enum {Empty, Full, Ball} Throwable;
375 typedef enum {LEFT, RIGHT} Hand;
376 typedef enum {THROW, CATCH} Action;
377 typedef enum {HAND, ELBOW, SHOULDER} Joint;
378 typedef enum {ATCH, THRATCH, ACTION, LINKEDACTION,
379 PTHRATCH, BPREDICTOR, PREDICTOR} TrajectoryStatus;
380 typedef struct {double a, b, c, d; } Spline;
381 typedef DXPoint Arm[3];
384 /* Object is an arbitrary object being juggled. Each Trajectory
385 * references an Object ("count" tracks this), and each Object is also
386 * linked into a global Objects list. Objects may include a Trace
387 * list for tracking erasures. */
388 typedef struct object *ObjectPtr;
389 typedef struct object {
390 ObjectPtr next, prev;
394 int count; /* reference count */
395 Bool active; /* Object is in use */
405 /* Trajectory is a segment of juggling action. A list of Trajectories
406 * defines the juggling performance. The Trajectory list goes through
407 * multiple processing steps to convert it from basic juggling
408 * notation into rendering data. */
410 typedef struct trajectory *TrajectoryPtr;
411 typedef struct trajectory {
412 TrajectoryPtr prev, next; /* for building list */
413 TrajectoryStatus status;
431 TrajectoryPtr balllink;
432 TrajectoryPtr handlink;
435 double cx; /* Moving juggler */
436 double x, y; /* current position */
437 double dx, dy; /* initial velocity */
441 unsigned long start, finish;
455 const char * pattern;
459 /* List of popular patterns, in any order */
460 /* Patterns should be given in Adam notation so the generator can
461 concatenate them safely. Null descriptions are ok. Height
462 notation will be displayed automatically. */
463 /* Can't const this because it is qsorted. This *should* be reentrant,
465 static /*const*/ patternstruct portfolio[] = {
466 {"[+2 1]", /* +3 1 */ "Typical 2 ball juggler"},
467 {"[2 0]", /* 4 0 */ "2 in 1 hand"},
468 {"[2 0 1]", /* 5 0 1 */},
469 {"[+2 0 +2 0 0]" /* +5 0 +5 0 0 */},
470 {"[+2 0 1 2 2]", /* +4 0 1 2 3 */},
471 {"[2 0 1 1]", /* 6 0 1 1 */},
473 {"[3]", /* 3 */ "3 cascade"},
474 {"[+3]", /* +3 */ "reverse 3 cascade"},
475 {"[=3]", /* =3 */ "cascade 3 under arm"},
476 {"[&3]", /* &3 */ "cascade 3 catching under arm"},
477 {"[_3]", /* _3 */ "bouncing 3 cascade"},
478 {"[+3 x3 =3]", /* +3 x3 =3 */ "Mill's mess"},
479 {"[3 2 1]", /* 5 3 1" */},
480 {"[3 3 1]", /* 4 4 1" */},
481 {"[3 1 2]", /* 6 1 2 */ "See-saw"},
482 {"[=3 3 1 2]", /* =4 5 1 2 */},
483 {"[=3 2 2 3 1 2]", /* =6 2 2 5 1 2 */ "=4 5 1 2 stretched"},
484 {"[+3 3 1 3]", /* +4 4 1 3 */ "anemic shower box"},
485 {"[3 3 1]", /* 4 4 1 */},
486 {"[+3 2 3]", /* +4 2 3 */},
487 {"[+3 1]", /* +5 1 */ "3 shower"},
488 {"[_3 1]", /* _5 1 */ "bouncing 3 shower"},
489 {"[3 0 3 0 3]", /* 5 0 5 0 5 */ "shake 3 out of 5"},
490 {"[3 3 3 0 0]", /* 5 5 5 0 0 */ "flash 3 out of 5"},
491 {"[3 3 0]", /* 4 5 0 */ "complete waste of a 5 ball juggler"},
492 {"[3 3 3 0 0 0 0]", /* 7 7 7 0 0 0 0 */ "3 flash"},
493 {"[+3 0 +3 0 +3 0 0]", /* +7 0 +7 0 +7 0 0 */},
494 {"[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 */},
495 {"[3 0 2 0]", /* 8 0 4 0 */},
496 {"[_3 2 1]", /* _5 3 1 */},
497 {"[_3 0 1]", /* _8 0 1 */},
498 {"[1 _3 1 _3 0 1 _3 0]", /* 1 _7 1 _7 0 1 _7 0 */},
499 {"[_3 2 1 _3 1 2 1]", /* _6 3 1 _6 1 3 1 */},
501 {"[4]", /* 4 */ "4 cascade"},
502 {"[+4 3]", /* +5 3 */ "4 ball half shower"},
503 {"[4 4 2]", /* 5 5 2 */},
504 {"[+4 4 4 +4]", /* +4 4 4 +4 */ "4 columns"},
505 {"[+4 3 +4]", /* +5 3 +4 */},
506 {"[4 3 4 4]", /* 5 3 4 4 */},
507 {"[4 3 3 4]", /* 6 3 3 4 */},
508 {"[4 3 2 4", /* 6 4 2 4 */},
509 {"[+4 1]", /* +7 1 */ "4 shower"},
510 {"[4 4 4 4 0]", /* 5 5 5 5 0 */ "learning 5"},
511 {"[+4 x4 =4]", /* +4 x4 =4 */ "Mill's mess for 4"},
512 {"[+4 2 1 3]", /* +9 3 1 3 */},
513 {"[4 4 1 4 1 4]", /* 6 6 1 5 1 5, by Allen Knutson */},
514 {"[_4 _4 _4 1 _4 1]", /* _5 _6 _6 1 _5 1 */},
515 {"[_4 3 3]", /* _6 3 3 */},
516 {"[_4 3 1]", /* _7 4 1 */},
517 {"[_4 2 1]", /* _8 3 1 */},
518 {"[_4 3 3 3 0]", /* _8 4 4 4 0 */},
519 {"[_4 1 3 1]", /* _9 1 5 1 */},
520 {"[_4 1 3 1 2]", /* _10 1 6 1 2 */},
522 {"[5]", /* 5 */ "5 cascade"},
523 {"[_5 _5 _5 _5 _5 5 5 5 5 5]", /* _5 _5 _5 _5 _5 5 5 5 5 5 */},
524 {"[+5 x5 =5]", /* +5 x5 =5 */ "Mill's mess for 5"},
525 {"[5 4 4]", /* 7 4 4 */},
526 {"[_5 4 4]", /* _7 4 4 */},
527 {"[1 2 3 4 5 5 5 5 5]", /* 1 2 3 4 5 6 7 8 9 */ "5 ramp"},
528 {"[5 4 5 3 1]", /* 8 5 7 4 1, by Allen Knutson */},
529 {"[_5 4 1 +4]", /* _9 5 1 5 */},
530 {"[_5 4 +4 +4]", /* _8 4 +4 +4 */},
531 {"[_5 4 4 4 1]", /* _9 5 5 5 1 */},
533 {"[_5 4 4 +4 4 0]", /*_10 5 5 +5 5 0 */},
535 {"[6]", /* 6 */ "6 cascade"},
536 {"[+6 5]", /* +7 5 */},
537 {"[6 4]", /* 8 4 */},
538 {"[+6 3]", /* +9 3 */},
539 {"[6 5 4 4]", /* 9 7 4 4 */},
540 {"[+6 5 5 5]", /* +9 5 5 5 */},
541 {"[6 0 6]", /* 9 0 9 */},
542 {"[_6 0 _6]", /* _9 0 _9 */},
544 {"[_7]", /* _7 */ "bouncing 7 cascade"},
545 {"[7]", /* 7 */ "7 cascade"},
546 {"[7 6 6 6 6]", /* 11 6 6 6 6 */ "Gatto's High Throw"},
552 typedef struct { int start; int number; } PatternIndex;
554 struct patternindex {
557 PatternIndex index[countof(portfolio)];
561 /* Jugglestruct: per-screen global data. The master Object
562 * and Trajectory lists are anchored here. */
564 GLXContext *glx_context;
566 trackball_state *trackball;
577 time_t begintime; /* should make 'time' usable for at least 48 days
578 on a 32-bit machine */
579 unsigned long time; /* millisecond timer*/
582 struct patternindex patternindex;
583 texture_font_data *font_data;
586 static jugglestruct *juggles = (jugglestruct *) NULL;
592 #define DUP_OBJECT(n, t) { \
593 (n)->object = (t)->object; \
594 if((n)->object != NULL) (n)->object->count++; \
597 /* t must point to an existing element. t must not be an
598 expression ending ->next or ->prev */
599 #define REMOVE(t) { \
600 (t)->next->prev = (t)->prev; \
601 (t)->prev->next = (t)->next; \
605 /* t receives element to be created and added to the list. ot must
606 point to an existing element or be identical to t to start a new
607 list. Applicable to Trajectories, Objects and Traces. */
608 #define ADD_ELEMENT(type, t, ot) \
609 if (((t) = (type*)calloc(1,sizeof(type))) != NULL) { \
610 (t)->next = (ot)->next; \
613 (t)->next->prev = (t); \
617 object_destroy(Object* o)
619 if(o->trace != NULL) {
620 while(o->trace->next != o->trace) {
621 Trace *s = o->trace->next;
622 REMOVE(s); /* Don't eliminate 's' */
630 trajectory_destroy(Trajectory *t) {
631 if(t->name != NULL) free(t->name);
632 if(t->pattern != NULL) free(t->pattern);
633 /* Reduce object link count and call destructor if necessary */
634 if(t->object != NULL && --t->object->count < 1 && t->object->tracelen == 0) {
635 object_destroy(t->object);
637 REMOVE(t); /* Unlink and free */
641 free_juggle(ModeInfo *mi) {
642 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
644 if (sp->head != NULL) {
645 while (sp->head->next != sp->head) {
646 trajectory_destroy(sp->head->next);
649 sp->head = (Trajectory *) NULL;
651 if(sp->objects != NULL) {
652 while (sp->objects->next != sp->objects) {
653 object_destroy(sp->objects->next);
656 sp->objects = (Object*)NULL;
658 if(sp->pattern != NULL) {
665 add_throw(ModeInfo *mi, char type, int h, Notation n, const char* name)
667 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
670 ADD_ELEMENT(Trajectory, t, sp->head->prev);
671 if(t == NULL){ /* Out of Memory */
677 t->name = strdup(name);
690 /* add a Thratch to the performance */
692 program(ModeInfo *mi, const char *patn, const char *name, int cycles)
699 if (MI_IS_VERBOSE(mi)) {
700 (void) fprintf(stderr, "juggle[%d]: Programmed: %s x %d\n",
701 MI_SCREEN(mi), (name == NULL) ? patn : name, cycles);
704 for(w=i=0; i < cycles; i++, w++) { /* repeat until at least "cycles" throws
705 have been programmed */
706 /* title is the pattern name to be supplied to the first throw of
707 a sequence. If no name if given, use an empty title so that
708 the sequences are still delimited. */
709 const char *title = (name != NULL)? name : "";
714 for(p=patn; *p; p++) {
715 if (*p >= '0' && *p <='9') {
717 h = 10*h + (*p - '0');
719 Notation nn = notation;
721 case '[': /* begin Adam notation */
724 case '-': /* Inside throw */
727 case '+': /* Outside throw */
728 case '=': /* Cross throw */
729 case '&': /* Cross catch */
730 case 'x': /* Cross throw and catch */
731 case '_': /* Bounce */
732 case 'k': /* Kickup */
735 case '*': /* Lose ball */
739 case ']': /* end Adam notation */
745 if (!add_throw(mi, type, h, notation, title))
755 if(w == 0) { /* Only warn on first pass */
756 (void) fprintf(stderr,
757 "juggle[%d]: Unexpected pattern instruction: '%c'\n",
764 if (seen) { /* end of sequence */
765 if (!add_throw(mi, type, h, notation, title))
779 [ 3 3 1 3 4 2 3 1 3 3 4 0 2 1 ]
781 4 4 1 3 12 2 4 1 4 4 13 0 3 1
784 #define BOUNCEOVER 10
788 /* Convert Adam notation into heights */
790 adam(jugglestruct *sp)
793 for(t = sp->head->next; t != sp->head; t = t->next) {
794 if (t->status == ATCH) {
797 for(p = t->next; a > 0; p = p->next) {
799 t->height = -9; /* Indicate end of processing for name() */
802 if (p->status != ATCH || p->adam < 0 || p->adam>= a) {
807 if(t->height > BOUNCEOVER && t->posn == ' '){
808 t->posn = '_'; /* high defaults can be bounced */
809 } else if(t->height < 3 && t->posn == '_') {
810 t->posn = ' '; /* Can't bounce short throws. */
812 if(t->height < KICKMIN && t->posn == 'k'){
813 t->posn = ' '; /* Can't kick short throws */
815 if(t->height > THROWMAX){
816 t->posn = 'k'; /* Use kicks for ridiculously high throws */
823 /* Discover converted heights and update the sequence title */
825 name(jugglestruct *sp)
830 for(t = sp->head->next; t != sp->head; t = t->next) {
831 if (t->status == THRATCH && t->name != NULL) {
833 for(p = t; p == t || p->name == NULL; p = p->next) {
834 if(p == sp->head || p->height < 0) { /* end of reliable data */
838 b += sprintf(b, " %d", p->height);
840 b += sprintf(b, " %c%d", p->posn, p->height);
842 if(b - buffer > 500) break; /* otherwise this could eventually
843 overflow. It'll be too big to
847 (void) sprintf(b, ", %s", t->name);
849 free(t->name); /* Don't need name any more, it's been converted
852 if(t->pattern != NULL) free(t->pattern);
853 t->pattern = strdup(buffer);
858 /* Split Thratch notation into explicit throws and catches.
859 Usually Catch follows Throw in same hand, but take care of special
862 /* ..n1.. -> .. LTn RT1 LC RC .. */
863 /* ..nm.. -> .. LTn LC RTm RC .. */
868 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
869 Trajectory *t, *nt, *p;
870 Hand hand = (LRAND() & 1) ? RIGHT : LEFT;
872 for (t = sp->head->next; t != sp->head; t = t->next) {
873 if (t->status > THRATCH) {
875 } else if (t->status == THRATCH) {
878 /* plausibility check */
879 if (t->height <= 2 && t->posn == '_') {
880 t->posn = ' '; /* no short bounces */
882 if (t->height <= 1 && (t->posn == '=' || t->posn == '&')) {
883 t->posn = ' '; /* 1's need close catches */
888 case ' ': posn = '-'; t->posn = '+'; break;
889 case '+': posn = '+'; t->posn = '-'; break;
890 case '=': posn = '='; t->posn = '+'; break;
891 case '&': posn = '+'; t->posn = '='; break;
892 case 'x': posn = '='; t->posn = '='; break;
893 case '_': posn = '_'; t->posn = '-'; break;
894 case 'k': posn = 'k'; t->posn = 'k'; break;
896 (void) fprintf(stderr, "juggle: unexpected posn %c\n", t->posn);
899 hand = (Hand) ((hand + 1) % 2);
904 if (t->height == 1 && p != sp->head) {
905 p = p->prev; /* '1's are thrown earlier than usual */
911 ADD_ELEMENT(Trajectory, nt, p);
919 nt->height = t->height;
929 choose_object(void) {
932 o = (ObjType)NRAND((ObjType)NUM_OBJECT_TYPES);
933 if(balls && o == BALL) break;
934 if(clubs && o == CLUB) break;
935 if(torches && o == TORCH) break;
936 if(knives && o == KNIFE) break;
937 if(rings && o == RING) break;
938 if(bballs && o == BBALLS) break;
943 /* Connnect up throws and catches to figure out which ball goes where.
944 Do the same with the juggler's hands. */
949 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
952 for (t = sp->head->next; t != sp->head; t = t->next) {
953 if (t->status == ACTION) {
954 if (t->action == THROW) {
955 if (t->type == Empty) {
956 /* Create new Object */
957 ADD_ELEMENT(Object, t->object, sp->objects);
958 t->object->count = 1;
959 t->object->tracelen = 0;
960 t->object->active = False;
961 /* Initialise object's circular trace list */
962 ADD_ELEMENT(Trace, t->object->trace, t->object->trace);
964 if (MI_NPIXELS(mi) > 2) {
965 t->object->color = 1 + NRAND(MI_NPIXELS(mi) - 2);
968 t->object->color = 1;
970 t->object->color = 0;
974 /* Small chance of picking a random object instead of the
976 if(NRAND(OBJMIXPROB) == 0) {
977 t->object->type = choose_object();
979 t->object->type = sp->objtypes;
982 /* Check to see if we need trails for this object */
983 if(tail < ObjectDefs[t->object->type].mintrail) {
984 t->object->tail = ObjectDefs[t->object->type].mintrail;
986 t->object->tail = tail;
990 /* Balls can change divisions at each throw */
991 /* no, that looks stupid. -jwz */
992 if (t->divisions < 1)
993 t->divisions = 2 * (NRAND(2) + 1);
995 /* search forward for next catch in this hand */
996 for (p = t->next; t->handlink == NULL; p = p->next) {
997 if(p->status < ACTION || p == sp->head) return;
998 if (p->action == CATCH) {
999 if (t->handlink == NULL && p->hand == t->hand) {
1005 if (t->height > 0) {
1008 /* search forward for next ball catch */
1009 for (p = t->next; t->balllink == NULL; p = p->next) {
1010 if(p->status < ACTION || p == sp->head) {
1014 if (p->action == CATCH) {
1015 if (t->balllink == NULL && --h < 1) { /* caught */
1016 t->balllink = p; /* complete trajectory */
1018 if (p->type == Full) {
1019 (void) fprintf(stderr, "juggle[%d]: Dropped %d\n",
1020 MI_SCREEN(mi), t->object->color);
1024 DUP_OBJECT(p, t); /* accept catch */
1025 p->angle = t->angle;
1026 p->divisions = t->divisions;
1031 t->type = Empty; /* thrown */
1032 } else if (t->action == CATCH) {
1033 /* search forward for next throw from this hand */
1034 for (p = t->next; t->handlink == NULL; p = p->next) {
1035 if(p->status < ACTION || p == sp->head) return;
1036 if (p->action == THROW && p->hand == t->hand) {
1037 p->type = t->type; /* pass ball */
1038 DUP_OBJECT(p, t); /* pass object */
1039 p->divisions = t->divisions;
1044 t->status = LINKEDACTION;
1049 /* Clap when both hands are empty */
1051 clap(jugglestruct *sp)
1054 for (t = sp->head->next; t != sp->head; t = t->next) {
1055 if (t->status == LINKEDACTION &&
1056 t->action == CATCH &&
1058 t->handlink != NULL &&
1059 t->handlink->height == 0) { /* Completely idle hand */
1061 for (p = t->next; p != sp->head; p = p->next) {
1062 if (p->status == LINKEDACTION &&
1063 p->action == CATCH &&
1064 p->hand != t->hand) { /* Next catch other hand */
1065 if(p->type == Empty &&
1066 p->handlink != NULL &&
1067 p->handlink->height == 0) { /* Also completely idle */
1069 t->handlink->posn = '^'; /* Move first hand's empty throw */
1070 p->posn = '^'; /* to meet second hand's empty
1074 break; /* Only need first catch */
1081 #define CUBIC(s, t) ((((s).a * (t) + (s).b) * (t) + (s).c) * (t) + (s).d)
1083 /* Compute single spline from x0 with velocity dx0 at time t0 to x1
1084 with velocity dx1 at time t1 */
1086 makeSpline(double x0, double dx0, int t0, double x1, double dx1, int t1)
1095 a = ((dx0 + dx1)*t10 - 2*x10) / (t10*t10*t10);
1096 b = (3*x10 - (2*dx0 + dx1)*t10) / (t10*t10);
1101 s.c = (3*a*t0 - 2*b)*t0 + c;
1102 s.d = ((-a*t0 + b)*t0 - c)*t0 +d;
1106 /* Compute a pair of splines. s1 goes from x0 vith velocity dx0 at
1107 time t0 to x1 at time t1. s2 goes from x1 at time t1 to x2 with
1108 velocity dx2 at time t2. The arrival and departure velocities at
1109 x1, t1 must be the same. */
1111 makeSplinePair(Spline *s1, Spline *s2,
1112 double x0, double dx0, int t0,
1114 double x2, double dx2, int t2)
1116 double x10, x21, t21, t10, t20, dx1;
1122 dx1 = (3*x10*t21*t21 + 3*x21*t10*t10 + 3*dx0*t10*t21*t21
1123 - dx2*t10*t10*t21 - 4*dx0*t10*t21*t21) /
1125 *s1 = makeSpline(x0, dx0, t0, x1, dx1, t1);
1126 *s2 = makeSpline(x1, dx1, t1, x2, dx2, t2);
1130 /* Compute a Ballistic path in a pair of degenerate splines. sx goes
1131 from x at time t at constant velocity dx. sy goes from y at time t
1132 with velocity dy and constant acceleration g. */
1134 makeParabola(Trajectory *n,
1135 double x, double dx, double y, double dy, double g)
1137 double t = (double)n->start;
1141 n->xp.d = -dx*t + x;
1144 n->yp.c = -g*t + dy;
1145 n->yp.d = g/2*t*t - dy*t + y;
1151 #define SX 25 /* Shoulder Width */
1153 /* Convert hand position symbols into actual time/space coordinates */
1155 positions(jugglestruct *sp)
1158 unsigned long now = sp->time; /* Make sure we're not lost in the past */
1159 for (t = sp->head->next; t != sp->head; t = t->next) {
1160 if (t->status >= PTHRATCH) {
1162 } else if (t->status == ACTION || t->status == LINKEDACTION) {
1163 /* Allow ACTIONs to be annotated, but we won't mark them ready
1164 for the next stage */
1171 if (t->action == CATCH) { /* Throw-to-catch */
1172 if (t->type == Empty) {
1173 now += (int) THROW_NULL_INTERVAL; /* failed catch is short */
1174 } else { /* successful catch */
1175 now += (int)(THROW_CATCH_INTERVAL);
1177 } else { /* Catch-to-throw */
1178 if(t->object != NULL) {
1179 now += (int) (CATCH_THROW_INTERVAL *
1180 ObjectDefs[t->object->type].weight);
1182 now += (int) (CATCH_THROW_INTERVAL);
1188 else /* Concatenated performances may need clock resync */
1196 /* Add room for the handle */
1197 if(t->action == CATCH && t->object != NULL)
1198 yo -= ObjectDefs[t->object->type].handle;
1201 case '-': xo = sx - pose; break;
1204 case '+': xo = sx + pose; break;
1206 case '=': xo = - sx - pose; yo += pose; break;
1207 case '^': xo = 0; yo += pose*2; break; /* clap */
1209 (void) fprintf(stderr, "juggle: unexpected posn %c\n", t->posn);
1213 #ifdef _2DSpinsDontWorkIn3D
1214 t->angle = (((t->hand == LEFT) ^
1215 (t->posn == '+' || t->posn == '_' || t->posn == 'k' ))?
1221 t->x = t->cx + ((t->hand == LEFT) ? xo : -xo);
1224 /* Only mark complete if it was already linked */
1225 if(t->status == LINKEDACTION) {
1226 t->status = PTHRATCH;
1233 /* Private physics functions */
1235 /* Compute the spin-rate for a trajectory. Different types of throw
1236 (eg, regular thows, bounces, kicks, etc) have different spin
1239 type = type of object
1240 h = trajectory of throwing hand (throws), or next throwing hand (catches)
1241 old = earlier spin to consider
1242 dt = time span of this trajectory
1243 height = height of ball throw or 0 if based on old spin
1244 turns = full club turns required during this operation
1245 togo = partial club turns required to match hands
1248 spinrate(ObjType type, Trajectory *h, double old, double dt,
1249 int height, int turns, double togo)
1251 #ifdef _2DSpinsDontWorkIn3D
1252 const int dir = (h->hand == LEFT) ^ (h->posn == '+')? -1 : 1;
1257 if(ObjectDefs[type].handle != 0) { /* Clubs */
1258 return (dir * turns * 2 * M_PI + togo) / dt;
1259 } else if(height == 0) { /* Balls already spinning */
1261 } else { /* Balls */
1262 return dir * NRAND(height*10)/20/ObjectDefs[type].weight * 2 * M_PI / dt;
1267 /* compute the angle at the end of a spinning trajectory */
1269 end_spin(Trajectory *t)
1271 return t->angle + t->spin * (t->finish - t->start);
1274 /* Sets the initial angle of the catch following hand movement t to
1275 the final angle of the throw n. Also sets the angle of the
1276 subsequent throw to the same angle plus half a turn. */
1278 match_spins_on_catch(Trajectory *t, Trajectory *n)
1280 if(ObjectDefs[t->balllink->object->type].handle == 0) {
1281 t->balllink->angle = end_spin(n);
1282 if(t->balllink->handlink != NULL) {
1283 #ifdef _2DSpinsDontWorkIn3D
1284 t->balllink->handlink->angle = t->balllink->angle + M_PI;
1286 t->balllink->handlink->angle = t->balllink->angle;
1293 find_bounce(jugglestruct *sp,
1294 double yo, double yf, double yc, double tc, double cor)
1296 double tb, i, dy = 0;
1297 const double e = 1; /* permissible error in yc */
1301 yt = height at catch time after one bounce
1302 one or three roots according to timing
1303 find one by interval bisection
1306 for(i = tc / 2; i > 0.0001; i/=2){
1309 (void) fprintf(stderr, "juggle: bounce div by zero!\n");
1312 dy = (yf - yo)/tb + sp->Gr/2*tb;
1314 yt = -cor*dy*dt + sp->Gr/2*dt*dt + yf;
1317 }else if(yt > yc - e){
1323 if(dy*THROW_CATCH_INTERVAL < -200) { /* bounce too hard */
1330 new_predictor(const Trajectory *t, int start, int finish, double angle)
1333 ADD_ELEMENT(Trajectory, n, t->prev);
1338 n->divisions = t->divisions;
1340 n->status = PREDICTOR;
1348 /* Turn abstract timings into physically appropriate object trajectories. */
1350 projectile(jugglestruct *sp)
1353 const int yf = 0; /* Floor height */
1355 for (t = sp->head->next; t != sp->head; t = t->next) {
1356 if (t->status != PTHRATCH || t->action != THROW) {
1358 } else if (t->balllink == NULL) { /* Zero Throw */
1359 t->status = BPREDICTOR;
1360 } else if (t->balllink->handlink == NULL) { /* Incomplete */
1362 } else if(t->balllink == t->handlink) {
1363 /* '2' height - hold on to ball. Don't need to consider
1364 flourishes, 'hands' will do that automatically anyway */
1367 /* Zero spin to avoid wrist injuries */
1369 match_spins_on_catch(t, t);
1371 t->status = BPREDICTOR;
1374 if (t->posn == '_') { /* Bounce once */
1376 const int tb = t->start +
1377 find_bounce(sp, t->y, (double) yf, t->balllink->y,
1378 (double) (t->balllink->start - t->start),
1379 ObjectDefs[t->object->type].cor);
1381 if(tb < t->start) { /* bounce too hard */
1382 t->posn = '+'; /* Use regular throw */
1384 Trajectory *n; /* First (throw) trajectory. */
1385 double dt; /* Time span of a trajectory */
1386 double dy; /* Distance span of a follow-on trajectory.
1387 First trajectory uses t->dy */
1388 /* dx is constant across both trajectories */
1389 t->dx = (t->balllink->x - t->x) / (t->balllink->start - t->start);
1391 { /* ball follows parabola down */
1392 n = new_predictor(t, t->start, tb, t->angle);
1393 if(n == NULL) return False;
1394 dt = n->finish - n->start;
1395 /* Ball rate 4, no flight or matching club turns */
1396 n->spin = spinrate(t->object->type, t, 0.0, dt, 4, 0, 0.0);
1397 t->dy = (yf - t->y)/dt - sp->Gr/2*dt;
1398 makeParabola(n, t->x, t->dx, t->y, t->dy, sp->Gr);
1401 { /* ball follows parabola up */
1402 Trajectory *m = new_predictor(t, n->finish, t->balllink->start,
1404 if(m == NULL) return False;
1405 dt = m->finish - m->start;
1406 /* Use previous ball rate, no flight club turns */
1407 m->spin = spinrate(t->object->type, t, n->spin, dt, 0, 0,
1408 t->balllink->angle - m->angle);
1409 match_spins_on_catch(t, m);
1410 dy = (t->balllink->y - yf)/dt - sp->Gr/2 * dt;
1411 makeParabola(m, t->balllink->x - t->dx * dt,
1412 t->dx, (double) yf, dy, sp->Gr);
1415 t->status = BPREDICTOR;
1418 } else if (t->posn == 'k') { /* Drop & Kick */
1419 Trajectory *n; /* First (drop) trajectory. */
1420 Trajectory *o; /* Second (rest) trajectory */
1421 Trajectory *m; /* Third (kick) trajectory */
1422 const int td = t->start + 2*THROW_CATCH_INTERVAL; /* Drop time */
1423 const int tk = t->balllink->start - 5*THROW_CATCH_INTERVAL; /* Kick */
1426 { /* Fall to ground */
1427 n = new_predictor(t, t->start, td, t->angle);
1428 if(n == NULL) return False;
1429 dt = n->finish - n->start;
1430 /* Ball spin rate 4, no flight club turns */
1431 n->spin = spinrate(t->object->type, t, 0.0, dt, 4, 0,
1432 t->balllink->angle - n->angle);
1433 t->dx = (t->balllink->x - t->x) / dt;
1434 t->dy = (yf - t->y)/dt - sp->Gr/2*dt;
1435 makeParabola(n, t->x, t->dx, t->y, t->dy, sp->Gr);
1438 { /* Rest on ground */
1439 o = new_predictor(t, n->finish, tk, end_spin(n));
1440 if(o == NULL) return False;
1442 makeParabola(o, t->balllink->x, 0.0, (double) yf, 0.0, 0.0);
1447 m = new_predictor(t, o->finish, t->balllink->start, end_spin(o));
1448 if(m == NULL) return False;
1449 dt = m->finish - m->start;
1450 /* Match receiving hand, ball rate 4, one flight club turn */
1451 m->spin = spinrate(t->object->type, t->balllink->handlink, 0.0, dt,
1452 4, 1, t->balllink->angle - m->angle);
1453 match_spins_on_catch(t, m);
1454 dy = (t->balllink->y - yf)/dt - sp->Gr/2 * dt;
1455 makeParabola(m, t->balllink->x, 0.0, (double) yf, dy, sp->Gr);
1458 t->status = BPREDICTOR;
1462 /* Regular flight, no bounce */
1463 { /* ball follows parabola */
1465 Trajectory *n = new_predictor(t, t->start,
1466 t->balllink->start, t->angle);
1467 if(n == NULL) return False;
1468 dt = t->balllink->start - t->start;
1470 n->spin = spinrate(t->object->type, t, 0.0, dt, t->height, t->height/2,
1471 t->balllink->angle - n->angle);
1472 match_spins_on_catch(t, n);
1473 t->dx = (t->balllink->x - t->x) / dt;
1474 t->dy = (t->balllink->y - t->y) / dt - sp->Gr/2 * dt;
1475 makeParabola(n, t->x, t->dx, t->y, t->dy, sp->Gr);
1478 t->status = BPREDICTOR;
1484 /* Turn abstract hand motions into cubic splines. */
1486 hands(jugglestruct *sp)
1488 Trajectory *t, *u, *v;
1490 for (t = sp->head->next; t != sp->head; t = t->next) {
1491 /* no throw => no velocity */
1492 if (t->status != BPREDICTOR) {
1497 if (u == NULL) { /* no next catch */
1501 if (v == NULL) { /* no next throw */
1505 /* double spline takes hand from throw, thru catch, to
1508 t->finish = u->start;
1509 t->status = PREDICTOR;
1511 u->finish = v->start;
1512 u->status = PREDICTOR;
1515 /* FIXME: These adjustments leave a small glitch when alternating
1516 balls and clubs. Just hope no-one notices. :-) */
1518 /* make sure empty hand spin matches the thrown object in case it
1521 t->spin = ((t->hand == LEFT)? -1 : 1 ) *
1522 fabs((u->angle - t->angle)/(u->start - t->start));
1524 u->spin = ((v->hand == LEFT) ^ (v->posn == '+')? -1 : 1 ) *
1525 fabs((v->angle - u->angle)/(v->start - u->start));
1527 (void) makeSplinePair(&t->xp, &u->xp,
1528 t->x, t->dx, t->start,
1530 v->x, v->dx, v->start);
1531 (void) makeSplinePair(&t->yp, &u->yp,
1532 t->y, t->dy, t->start,
1534 v->y, v->dy, v->start);
1536 t->status = PREDICTOR;
1540 /* Given target x, y find_elbow puts hand at target if possible,
1541 * otherwise makes hand point to the target */
1543 find_elbow(int armlength, DXPoint *h, DXPoint *e, DXPoint *p, DXPoint *s,
1547 double x = p->x - s->x;
1548 double y = p->y - s->y;
1549 h2 = x*x + y*y + z*z;
1550 if (h2 > 4 * armlength * armlength) {
1551 t = armlength/sqrt(h2);
1554 h->x = 2 * t * x + s->x;
1555 h->y = 2 * t * y + s->y;
1557 r = sqrt((double)(x*x + z*z));
1558 t = sqrt(4 * armlength * armlength / h2 - 1);
1559 e->x = x*(1 + y*t/r)/2 + s->x;
1560 e->y = (y - r*t)/2 + s->y;
1567 /* NOTE: returned x, y adjusted for arm reach */
1569 reach_arm(ModeInfo * mi, Hand side, DXPoint *p)
1571 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
1573 find_elbow(40, &h, &e, p, &sp->arm[1][side][SHOULDER], 25);
1574 *p = sp->arm[1][side][HAND] = h;
1575 sp->arm[1][side][ELBOW] = e;
1579 /* dumps a human-readable rendition of the current state of the juggle
1580 pipeline to stderr for debugging */
1582 dump(jugglestruct *sp)
1585 for (t = sp->head->next; t != sp->head; t = t->next) {
1586 switch (t->status) {
1588 (void) fprintf(stderr, "%p a %c%d\n", (void*)t, t->posn, t->adam);
1591 (void) fprintf(stderr, "%p T %c%d %s\n", (void*)t, t->posn, t->height,
1592 t->pattern == NULL?"":t->pattern);
1595 if (t->action == CATCH)
1596 (void) fprintf(stderr, "%p A %c%cC\n",
1598 t->hand ? 'R' : 'L');
1600 (void) fprintf(stderr, "%p A %c%c%c%d\n",
1602 t->hand ? 'R' : 'L',
1603 (t->action == THROW)?'T':'N',
1607 (void) fprintf(stderr, "%p L %c%c%c%d %d %p %p\n",
1610 (t->action == THROW)?'T':(t->action == CATCH?'C':'N'),
1611 t->height, t->object == NULL?0:t->object->color,
1612 (void*)t->handlink, (void*)t->balllink);
1615 (void) fprintf(stderr, "%p O %c%c%c%d %d %2d %6lu %6lu\n",
1618 (t->action == THROW)?'T':(t->action == CATCH?'C':'N'),
1619 t->height, t->type, t->object == NULL?0:t->object->color,
1620 t->start, t->finish);
1623 (void) fprintf(stderr, "%p B %c %2d %6lu %6lu %g\n",
1624 (void*)t, t->type == Ball?'b':t->type == Empty?'e':'f',
1625 t->object == NULL?0:t->object->color,
1626 t->start, t->finish, t->yp.c);
1629 (void) fprintf(stderr, "%p P %c %2d %6lu %6lu %g\n",
1630 (void*)t, t->type == Ball?'b':t->type == Empty?'e':'f',
1631 t->object == NULL?0:t->object->color,
1632 t->start, t->finish, t->yp.c);
1635 (void) fprintf(stderr, "%p: status %d not implemented\n",
1636 (void*)t, t->status);
1640 (void) fprintf(stderr, "---\n");
1644 static int get_num_balls(const char *j)
1650 for (p = j; *p; p++) {
1651 if (*p >= '0' && *p <='9') { /* digit */
1652 h = 10*h + (*p - '0');
1664 compare_num_balls(const void *p1, const void *p2)
1667 i = get_num_balls(((patternstruct*)p1)->pattern);
1668 j = get_num_balls(((patternstruct*)p2)->pattern);
1679 /**************************************************************************
1680 * Rendering Functions *
1682 **************************************************************************/
1685 show_arms(ModeInfo * mi)
1688 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
1691 XPoint a[countof(sp->arm[0][0])];
1697 glFrontFace(GL_CCW);
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);
1802 glFrontFace(GL_CCW);
1805 GLfloat scale = ((GLfloat) a[10].x - a[9].x) / 2;
1810 glTranslatef(a[6].x, a[6].y - scale, 0);
1811 glScalef(scale, scale, scale);
1814 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1817 glScalef(scale, scale, scale);
1818 glTranslatef(0, 0.3, 0);
1820 glTranslatef(0, 0, 0.35);
1821 polys += tube (0, 0, 0,
1824 slices, True, True, MI_IS_WIREFRAME(mi));
1826 glScalef(0.9, 0.9, 1);
1827 polys += unit_sphere(2*slices, 2*slices, MI_IS_WIREFRAME(mi));
1831 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
1832 glTranslatef(0, 1.1, 0);
1835 glScalef(scale, scale, scale);
1836 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1840 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1841 glTranslatef(0, 1.1, 0);
1843 glScalef(0.9, 1.0, 0.9);
1844 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1848 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
1849 glTranslatef(0, 1.0, 0);
1852 glScalef(scale, scale, scale);
1853 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1857 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1858 glTranslatef(0, 0.8, 0);
1861 glScalef(scale, scale, scale);
1862 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1867 glTranslatef(0, 0.7, 0);
1869 for (i = -1; i <= 1; i += 2) {
1872 glRotatef (i*10, 0, 0, 1);
1873 glTranslatef(-i*0.65, 0, 0);
1876 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
1878 glScalef(scale, scale, scale);
1879 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1882 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1884 glTranslatef(0, 0.6, 0);
1885 polys += tube (0, 0, 0,
1888 slices, True, True, MI_IS_WIREFRAME(mi));
1892 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
1894 glTranslatef(0, 4.4, 0);
1896 glScalef(scale, scale, scale);
1897 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1901 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1903 glTranslatef(0, 4.7, 0);
1904 polys += tube (0, 0, 0,
1907 slices, True, True, MI_IS_WIREFRAME(mi));
1911 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
1913 glTranslatef(0, 9.7, 0);
1915 glScalef(scale, scale, scale);
1916 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1920 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1922 glRotatef (-i*10, 0, 0, 1);
1923 glTranslatef(-i*1.75, 9.7, 0.9);
1925 glScalef (0.4, 1, 1);
1926 polys += tube (0, 0, 0,
1929 slices*4, True, True, MI_IS_WIREFRAME(mi));
1938 sp->arm[1][LEFT][SHOULDER].x = sp->cx + figure[2].x;
1939 sp->arm[1][RIGHT][SHOULDER].x = sp->cx + figure[3].x;
1941 /* Initialise arms */
1943 for(i = 0; i < 2; i++){
1944 sp->arm[i][LEFT][SHOULDER].y = figure[2].y;
1945 sp->arm[i][LEFT][ELBOW].x = figure[2].x;
1946 sp->arm[i][LEFT][ELBOW].y = figure[1].y;
1947 sp->arm[i][LEFT][HAND].x = figure[0].x;
1948 sp->arm[i][LEFT][HAND].y = figure[1].y;
1949 sp->arm[i][RIGHT][SHOULDER].y = figure[3].y;
1950 sp->arm[i][RIGHT][ELBOW].x = figure[3].x;
1951 sp->arm[i][RIGHT][ELBOW].y = figure[1].y;
1952 sp->arm[i][RIGHT][HAND].x = figure[4].x;
1953 sp->arm[i][RIGHT][HAND].y = figure[1].y;
1959 typedef struct { GLfloat x, y, z; } XYZ;
1961 /* lifted from sphere.c */
1963 striped_unit_sphere (int stacks, int slices,
1965 GLfloat *color1, GLfloat *color2,
1970 double theta1, theta2, theta3;
1972 XYZ la = { 0, 0, 0 }, lb = { 0, 0, 0 };
1973 XYZ c = {0, 0, 0}; /* center */
1974 double r = 1.0; /* radius */
1975 int stacks2 = stacks * 2;
1982 if (slices < 4 || stacks < 2 || r <= 0)
1984 glBegin (GL_POINTS);
1985 glVertex3f (c.x, c.y, c.z);
1992 for (j = 0; j < stacks; j++)
1994 theta1 = j * (M_PI+M_PI) / stacks2 - M_PI_2;
1995 theta2 = (j + 1) * (M_PI+M_PI) / stacks2 - M_PI_2;
1997 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
1998 ((j == 0 || j == stacks-1 ||
1999 j % (stacks / (stripes+1)))
2000 ? color1 : color2));
2002 glBegin (wire_p ? GL_LINE_LOOP : GL_TRIANGLE_STRIP);
2003 for (i = 0; i <= slices; i++)
2005 theta3 = i * (M_PI+M_PI) / slices;
2007 if (wire_p && i != 0)
2009 glVertex3f (lb.x, lb.y, lb.z);
2010 glVertex3f (la.x, la.y, la.z);
2013 e.x = cos (theta2) * cos(theta3);
2015 e.z = cos (theta2) * sin(theta3);
2016 p.x = c.x + r * e.x;
2017 p.y = c.y + r * e.y;
2018 p.z = c.z + r * e.z;
2020 glNormal3f (e.x, e.y, e.z);
2021 glTexCoord2f (i / (double)slices,
2022 2*(j+1) / (double)stacks2);
2023 glVertex3f (p.x, p.y, p.z);
2026 e.x = cos(theta1) * cos(theta3);
2028 e.z = cos(theta1) * sin(theta3);
2029 p.x = c.x + r * e.x;
2030 p.y = c.y + r * e.y;
2031 p.z = c.z + r * e.z;
2033 glNormal3f (e.x, e.y, e.z);
2034 glTexCoord2f (i / (double)slices,
2035 2*j / (double)stacks2);
2036 glVertex3f (p.x, p.y, p.z);
2048 show_ball(ModeInfo *mi, unsigned long color, Trace *s)
2051 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2052 /*int offset = (int)(s->angle*64*180/M_PI);*/
2053 short x = (short)(SCENE_WIDTH/2 + s->x * sp->scale);
2054 short y = (short)(SCENE_HEIGHT - s->y * sp->scale);
2055 GLfloat gcolor1[4] = { 0, 0, 0, 1 };
2056 GLfloat gcolor2[4] = { 0, 0, 0, 1 };
2059 /* Avoid wrapping */
2060 if(s->y*sp->scale > SCENE_HEIGHT * 2) return 0;
2062 gcolor1[0] = mi->colors[color].red / 65536.0;
2063 gcolor1[1] = mi->colors[color].green / 65536.0;
2064 gcolor1[2] = mi->colors[color].blue / 65536.0;
2066 gcolor2[0] = gcolor1[0] / 3;
2067 gcolor2[1] = gcolor1[1] / 3;
2068 gcolor2[2] = gcolor1[2] / 3;
2070 glFrontFace(GL_CCW);
2073 GLfloat scale = BALLRADIUS;
2075 glTranslatef(x, y, 0);
2076 glScalef(scale, scale, scale);
2078 glRotatef (s->angle / M_PI*180, 1, 1, 0);
2080 polys += striped_unit_sphere (slices, slices, s->divisions,
2081 gcolor1, gcolor2, MI_IS_WIREFRAME(mi));
2088 show_europeanclub(ModeInfo *mi, unsigned long color, Trace *s)
2091 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2092 /*int offset = (int)(s->angle*64*180/M_PI);*/
2093 short x = (short)(SCENE_WIDTH/2 + s->x * sp->scale);
2094 short y = (short)(SCENE_HEIGHT - s->y * sp->scale);
2095 double radius = 12 * sp->scale;
2096 GLfloat gcolor1[4] = { 0, 0, 0, 1 };
2097 GLfloat gcolor2[4] = { 1, 1, 1, 1 };
2119 /* Avoid wrapping */
2120 if(s->y*sp->scale > SCENE_HEIGHT * 2) return 0;
2122 gcolor1[0] = mi->colors[color].red / 65536.0;
2123 gcolor1[1] = mi->colors[color].green / 65536.0;
2124 gcolor1[2] = mi->colors[color].blue / 65536.0;
2126 glFrontFace(GL_CCW);
2129 GLfloat scale = radius;
2131 glTranslatef(x, y, 0);
2132 glScalef(scale, scale, scale);
2134 glTranslatef (0, 0, 2); /* put end of handle in hand */
2136 glRotatef (s->angle / M_PI*180, 1, 0, 0);
2139 glScalef (0.5, 1, 0.5);
2140 polys += striped_unit_sphere (slices, slices, divs, gcolor2, gcolor1,
2141 MI_IS_WIREFRAME(mi));
2143 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor2);
2144 polys += tube (0, 0, 0,
2147 slices, True, True, MI_IS_WIREFRAME(mi));
2149 glTranslatef (0, 2, 0);
2150 glScalef (0.25, 0.25, 0.25);
2151 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
2160 show_torch(ModeInfo *mi, unsigned long color, Trace *s)
2164 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2165 XPoint head, tail, last;
2166 DXPoint dhead, dlast;
2167 const double sa = sin(s->angle);
2168 const double ca = cos(s->angle);
2170 const double TailLen = -24;
2171 const double HeadLen = 16;
2172 const short Width = (short)(5 * sqrt(sp->scale));
2184 dhead.x = s->x + HeadLen * PERSPEC * sa;
2185 dhead.y = s->y - HeadLen * ca;
2187 if(color == MI_BLACK_PIXEL(mi)) { /* Use 'last' when erasing */
2189 } else { /* Store 'last' so we can use it later when s->prev has
2191 if(s->prev != s->next) {
2192 dlast.x = s->prev->x + HeadLen * PERSPEC * sin(s->prev->angle);
2193 dlast.y = s->prev->y - HeadLen * cos(s->prev->angle);
2200 /* Avoid wrapping (after last is stored) */
2201 if(s->y*sp->scale > SCENE_HEIGHT * 2) return 0;
2203 head.x = (short)(SCENE_WIDTH/2 + dhead.x*sp->scale);
2204 head.y = (short)(SCENE_HEIGHT - dhead.y*sp->scale);
2206 last.x = (short)(SCENE_WIDTH/2 + dlast.x*sp->scale);
2207 last.y = (short)(SCENE_HEIGHT - dlast.y*sp->scale);
2209 tail.x = (short)(SCENE_WIDTH/2 +
2210 (s->x + TailLen * PERSPEC * sa)*sp->scale );
2211 tail.y = (short)(SCENE_HEIGHT - (s->y - TailLen * ca)*sp->scale );
2213 if(color != MI_BLACK_PIXEL(mi)) {
2214 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
2215 XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi),
2216 Width, LineSolid, CapRound, JoinRound);
2217 draw_line(mi, head.x, head.y, tail.x, tail.y);
2219 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), color);
2220 XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi),
2221 Width * 2, LineSolid, CapRound, JoinRound);
2223 draw_line(mi, head.x, head.y, last.x, last.y);
2231 show_knife(ModeInfo *mi, unsigned long color, Trace *s)
2234 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2235 /*int offset = (int)(s->angle*64*180/M_PI);*/
2236 short x = (short)(SCENE_WIDTH/2 + s->x * sp->scale);
2237 short y = (short)(SCENE_HEIGHT - s->y * sp->scale);
2238 GLfloat gcolor1[4] = { 0, 0, 0, 1 };
2239 GLfloat gcolor2[4] = { 1, 1, 1, 1 };
2242 /* Avoid wrapping */
2243 if(s->y*sp->scale > SCENE_HEIGHT * 2) return 0;
2245 gcolor1[0] = mi->colors[color].red / 65536.0;
2246 gcolor1[1] = mi->colors[color].green / 65536.0;
2247 gcolor1[2] = mi->colors[color].blue / 65536.0;
2249 glFrontFace(GL_CCW);
2252 glTranslatef(x, y, 0);
2255 glTranslatef (0, 0, 2); /* put end of handle in hand */
2256 glRotatef (s->angle / M_PI*180, 1, 0, 0);
2258 glScalef (0.3, 1, 1); /* flatten blade */
2260 glTranslatef(0, 6, 0);
2261 glRotatef (180, 1, 0, 0);
2263 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor1);
2264 polys += tube (0, 0, 0,
2267 slices, True, True, MI_IS_WIREFRAME(mi));
2269 glTranslatef (0, 12, 0);
2270 glScalef (0.7, 10, 0.7);
2271 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor2);
2272 polys += unit_sphere (slices, slices, MI_IS_WIREFRAME(mi));
2280 show_ring(ModeInfo *mi, unsigned long color, Trace *s)
2283 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2284 /*int offset = (int)(s->angle*64*180/M_PI);*/
2285 short x = (short)(SCENE_WIDTH/2 + s->x * sp->scale);
2286 short y = (short)(SCENE_HEIGHT - s->y * sp->scale);
2287 double radius = 12 * sp->scale;
2288 GLfloat gcolor1[4] = { 0, 0, 0, 1 };
2289 GLfloat gcolor2[4] = { 0, 0, 0, 1 };
2292 int wire_p = MI_IS_WIREFRAME(mi);
2293 GLfloat width = M_PI * 2 / slices;
2296 GLfloat thickness = 0.15;
2298 /* Avoid wrapping */
2299 if(s->y*sp->scale > SCENE_HEIGHT * 2) return 0;
2301 gcolor1[0] = mi->colors[color].red / 65536.0;
2302 gcolor1[1] = mi->colors[color].green / 65536.0;
2303 gcolor1[2] = mi->colors[color].blue / 65536.0;
2305 gcolor2[0] = gcolor1[0] / 3;
2306 gcolor2[1] = gcolor1[1] / 3;
2307 gcolor2[2] = gcolor1[2] / 3;
2309 glFrontFace(GL_CCW);
2312 glTranslatef(0, 0, 12); /* back of ring in hand */
2314 glTranslatef(x, y, 0);
2315 glScalef(radius, radius, radius);
2317 glRotatef (90, 0, 1, 0);
2318 glRotatef (s->angle / M_PI*180, 0, 0, 1);
2320 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor1);
2323 for (j = -1; j <= 1; j += 2)
2325 GLfloat z = j * thickness/2;
2326 glFrontFace (j < 0 ? GL_CCW : GL_CW);
2327 glNormal3f (0, 0, j*1);
2328 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
2329 for (i = 0; i < slices + (wire_p ? 0 : 1); i++) {
2330 GLfloat th, cth, sth;
2331 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
2332 (i % (slices/3) ? gcolor1 : gcolor2));
2336 glVertex3f (cth * ra, sth * ra, z);
2337 glVertex3f (cth * rb, sth * rb, z);
2344 glFrontFace (GL_CCW);
2345 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
2346 for (i = 0; i < slices + (wire_p ? 0 : 1); i++)
2348 GLfloat th = i * width;
2349 GLfloat cth = cos(th);
2350 GLfloat sth = sin(th);
2351 glNormal3f (cth, sth, 0);
2352 glVertex3f (cth * ra, sth * ra, thickness/2);
2353 glVertex3f (cth * ra, sth * ra, -thickness/2);
2359 glFrontFace (GL_CW);
2360 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
2361 for (i = 0; i < slices + (wire_p ? 0 : 1); i++)
2363 GLfloat th = i * width;
2364 GLfloat cth = cos(th);
2365 GLfloat sth = sin(th);
2366 glNormal3f (-cth, -sth, 0);
2367 glVertex3f (cth * rb, sth * ra, thickness/2);
2368 glVertex3f (cth * rb, sth * ra, -thickness/2);
2373 glFrontFace (GL_CCW);
2380 show_bball(ModeInfo *mi, unsigned long color, Trace *s)
2383 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2384 /*int offset = (int)(s->angle*64*180/M_PI);*/
2385 short x = (short)(SCENE_WIDTH/2 + s->x * sp->scale);
2386 short y = (short)(SCENE_HEIGHT - s->y * sp->scale);
2387 double radius = 12 * sp->scale;
2388 GLfloat gcolor1[4] = { 0, 0, 0, 1 };
2389 GLfloat gcolor2[4] = { 0, 0, 0, 1 };
2393 /* Avoid wrapping */
2394 if(s->y*sp->scale > SCENE_HEIGHT * 2) return 0;
2396 gcolor1[0] = mi->colors[color].red / 65536.0;
2397 gcolor1[1] = mi->colors[color].green / 65536.0;
2398 gcolor1[2] = mi->colors[color].blue / 65536.0;
2400 glFrontFace(GL_CCW);
2403 GLfloat scale = radius;
2406 glTranslatef(0, -6, 5); /* position on top of hand */
2408 glTranslatef(x, y, 0);
2409 glScalef(scale, scale, scale);
2410 glRotatef (s->angle / M_PI*180, 1, 0, 1);
2412 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor1);
2413 polys += unit_sphere (slices, slices, MI_IS_WIREFRAME(mi));
2415 glRotatef (90, 0, 0, 1);
2416 glTranslatef (0, 0, 0.81);
2417 glScalef(0.15, 0.15, 0.15);
2418 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor2);
2419 for (i = 0; i < 3; i++) {
2421 glTranslatef (0, 0, 1);
2422 glRotatef (360 * i / 3, 0, 0, 1);
2423 glTranslatef (2, 0, 0);
2424 glRotatef (18, 0, 1, 0);
2425 glBegin (MI_IS_WIREFRAME(mi) ? GL_LINE_LOOP : GL_TRIANGLE_FAN);
2426 glVertex3f (0, 0, 0);
2427 for (j = slices; j >= 0; j--) {
2428 GLfloat th = j * M_PI*2 / slices;
2429 glVertex3f (cos(th), sin(th), 0);
2442 /**************************************************************************
2443 * Public Functions *
2445 **************************************************************************/
2448 /* FIXME: refill_juggle currently just appends new throws to the
2449 * programme. This is fine if the programme is empty, but if there
2450 * are still some trajectories left then it really should take these
2454 refill_juggle(ModeInfo * mi)
2456 jugglestruct *sp = NULL;
2459 if (juggles == NULL)
2461 sp = &juggles[MI_SCREEN(mi)];
2463 /* generate pattern */
2465 if (pattern == NULL) {
2468 #define MAXREPEAT 300
2469 #define CHANGE_BIAS 8 /* larger makes num_ball changes less likely */
2470 #define POSITION_BIAS 20 /* larger makes hand movements less likely */
2473 while (count < MI_CYCLES(mi)) {
2474 char buf[MAXPAT * 3 + 3], *b = buf;
2476 int l = NRAND(MAXPAT) + 1;
2477 int t = NRAND(MIN(MAXREPEAT, (MI_CYCLES(mi) - count))) + 1;
2479 { /* vary number of balls */
2480 int new_balls = sp->num_balls;
2483 if (new_balls == 2) /* Do not juggle 2 that often */
2484 change = NRAND(2 + CHANGE_BIAS / 4);
2486 change = NRAND(2 + CHANGE_BIAS);
2497 if (new_balls < sp->patternindex.minballs) {
2500 if (new_balls > sp->patternindex.maxballs) {
2503 if (new_balls < sp->num_balls) {
2504 if (!program(mi, "[*]", NULL, 1)) /* lose ball */
2507 sp->num_balls = new_balls;
2511 if (NRAND(2) && sp->patternindex.index[sp->num_balls].number) {
2512 /* Pick from PortFolio */
2513 int p = sp->patternindex.index[sp->num_balls].start +
2514 NRAND(sp->patternindex.index[sp->num_balls].number);
2515 if (!program(mi, portfolio[p].pattern, portfolio[p].name, t))
2518 /* Invent a new pattern */
2520 for(i = 0; i < l; i++){
2522 do { /* Triangular Distribution => high values more likely */
2523 m = NRAND(sp->num_balls + 1);
2524 n = NRAND(sp->num_balls + 1);
2526 if (n == sp->num_balls) {
2529 switch(NRAND(5 + POSITION_BIAS)){
2530 case 0: /* Outside throw */
2532 case 1: /* Cross throw */
2534 case 2: /* Cross catch */
2536 case 3: /* Cross throw and catch */
2538 case 4: /* Bounce */
2541 break; /* Inside throw (default) */
2550 if (!program(mi, buf, NULL, t))
2555 } else { /* pattern supplied in height or 'a' notation */
2556 if (!program(mi, pattern, NULL, MI_CYCLES(mi)))
2573 if (!projectile(sp)) {
2580 if(MI_IS_DEBUG(mi)) dump(sp);
2585 change_juggle(ModeInfo * mi)
2587 jugglestruct *sp = NULL;
2590 if (juggles == NULL)
2592 sp = &juggles[MI_SCREEN(mi)];
2594 /* Strip pending trajectories */
2595 for (t = sp->head->next; t != sp->head; t = t->next) {
2596 if(t->start > sp->time || t->finish < sp->time) {
2599 trajectory_destroy(n);
2603 /* Pick the current object theme */
2604 sp->objtypes = choose_object();
2608 mi->polygon_count += show_figure(mi, True);
2613 reshape_juggle (ModeInfo *mi, int width, int height)
2615 GLfloat h = (GLfloat) height / (GLfloat) width;
2617 glViewport (0, 0, (GLint) width, (GLint) height);
2619 glMatrixMode(GL_PROJECTION);
2621 gluPerspective (30.0, 1/h, 1.0, 100.0);
2623 glMatrixMode(GL_MODELVIEW);
2625 gluLookAt( 0.0, 0.0, 30.0,
2629 glClear(GL_COLOR_BUFFER_BIT);
2634 init_juggle (ModeInfo * mi)
2636 jugglestruct *sp = 0;
2637 int wire = MI_IS_WIREFRAME(mi);
2639 MI_INIT (mi, juggles, free_juggle);
2641 sp = &juggles[MI_SCREEN(mi)];
2643 if (!sp->glx_context) /* re-initting breaks print_texture_label */
2644 sp->glx_context = init_GL(mi);
2646 sp->font_data = load_texture_font (mi->dpy, "titleFont");
2648 reshape_juggle (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
2649 clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
2653 GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
2654 GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
2655 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
2656 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
2658 glEnable(GL_LIGHTING);
2659 glEnable(GL_LIGHT0);
2660 glEnable(GL_DEPTH_TEST);
2661 glEnable(GL_CULL_FACE);
2663 glLightfv(GL_LIGHT0, GL_POSITION, pos);
2664 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
2665 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
2666 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
2669 make_random_colormap (0, 0, 0,
2670 mi->colors, &MI_NPIXELS(mi),
2671 True, False, 0, False);
2674 double spin_speed = 0.05;
2675 double wander_speed = 0.001;
2676 double spin_accel = 0.05;
2677 sp->rot = make_rotator (0, spin_speed, 0,
2678 spin_accel, wander_speed, False);
2679 sp->trackball = gltrackball_init (False);
2682 if (only && *only && strcmp(only, " ")) {
2683 balls = clubs = torches = knives = rings = bballs = False;
2684 if (!strcasecmp (only, "balls")) balls = True;
2685 else if (!strcasecmp (only, "clubs")) clubs = True;
2686 else if (!strcasecmp (only, "torches")) torches = True;
2687 else if (!strcasecmp (only, "knives")) knives = True;
2688 else if (!strcasecmp (only, "rings")) rings = True;
2689 else if (!strcasecmp (only, "bballs")) bballs = True;
2691 (void) fprintf (stderr,
2692 "Juggle: -only must be one of: balls, clubs, torches, knives,\n"
2693 "\t rings, or bballs (not \"%s\")\n", only);
2694 #ifdef STANDALONE /* xlock mustn't exit merely because of a bad argument */
2700 /* #### hard to make this look good in OpenGL... */
2704 if (sp->head == 0) { /* first time initializing this juggler */
2706 sp->count = ABS(MI_COUNT(mi));
2710 /* record start time */
2711 sp->begintime = time(NULL);
2712 if(sp->patternindex.maxballs > 0) {
2713 sp->num_balls = sp->patternindex.minballs +
2714 NRAND(sp->patternindex.maxballs - sp->patternindex.minballs);
2717 mi->polygon_count +=
2718 show_figure(mi, True); /* Draw figure. Also discovers
2719 information about the juggler's
2722 /* "7" should be about three times the height of the juggler's
2724 sp->Gr = -GRAVITY(3 * sp->arm[0][RIGHT][SHOULDER].y,
2725 7 * THROW_CATCH_INTERVAL);
2727 if(!balls && !clubs && !torches && !knives && !rings && !bballs)
2728 balls = True; /* Have to juggle something! */
2730 /* create circular trajectory list */
2731 ADD_ELEMENT(Trajectory, sp->head, sp->head);
2732 if(sp->head == NULL){
2737 /* create circular object list */
2738 ADD_ELEMENT(Object, sp->objects, sp->objects);
2739 if(sp->objects == NULL){
2744 sp->pattern = strdup(""); /* Initialise saved pattern with
2748 sp = &juggles[MI_SCREEN(mi)];
2752 !strcasecmp (pattern, ".") ||
2753 !strcasecmp (pattern, "random")))
2756 if (pattern == NULL && sp->patternindex.maxballs == 0) {
2757 /* pattern list needs indexing */
2758 int nelements = countof(portfolio);
2762 /* sort according to number of balls */
2763 qsort((void*)portfolio, nelements,
2764 sizeof(portfolio[1]), compare_num_balls);
2766 /* last pattern has most balls */
2767 sp->patternindex.maxballs = get_num_balls(portfolio[nelements - 1].pattern);
2768 /* run through sorted list, indexing start of each group
2769 and number in group */
2770 sp->patternindex.maxballs = 1;
2771 for (i = 0; i < nelements; i++) {
2772 int b = get_num_balls(portfolio[i].pattern);
2773 if (b > sp->patternindex.maxballs) {
2774 sp->patternindex.index[sp->patternindex.maxballs].number = numpat;
2775 if(numpat == 0) sp->patternindex.minballs = b;
2776 sp->patternindex.maxballs = b;
2778 sp->patternindex.index[sp->patternindex.maxballs].start = i;
2783 sp->patternindex.index[sp->patternindex.maxballs].number = numpat;
2786 /* Set up programme */
2789 /* Only put things here that won't interrupt the programme during
2792 /* Use MIN so that users can resize in interesting ways, eg
2793 narrow windows for tall patterns, etc */
2794 sp->scale = MIN(SCENE_HEIGHT/480.0, SCENE_WIDTH/160.0);
2799 juggle_handle_event (ModeInfo *mi, XEvent *event)
2801 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2803 if (gltrackball_event_handler (event, sp->trackball,
2804 MI_WIDTH (mi), MI_HEIGHT (mi),
2805 &sp->button_down_p))
2807 else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
2818 draw_juggle (ModeInfo *mi)
2820 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2821 Display *dpy = MI_DISPLAY(mi);
2822 Window window = MI_WINDOW(mi);
2824 Trajectory *traj = NULL;
2826 unsigned long future = 0;
2827 char *pattern = NULL;
2829 if (!sp->glx_context)
2832 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sp->glx_context));
2834 glShadeModel(GL_SMOOTH);
2836 glEnable(GL_DEPTH_TEST);
2837 glEnable(GL_NORMALIZE);
2838 glEnable(GL_CULL_FACE);
2840 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
2843 glRotatef(current_device_rotation(), 0, 0, 1);
2845 glTranslatef(0,-3,0);
2849 get_position (sp->rot, &x, &y, &z, !sp->button_down_p);
2850 glTranslatef((x - 0.5) * 8,
2854 gltrackball_rotate (sp->trackball);
2856 get_rotation (sp->rot, &x, &y, &z, !sp->button_down_p);
2858 if (y < 0.8) y = 0.8 - (y - 0.8); /* always face forward */
2859 if (y > 1.2) y = 1.2 - (y - 1.2);
2861 glRotatef (x * 360, 1.0, 0.0, 0.0);
2862 glRotatef (y * 360, 0.0, 1.0, 0.0);
2863 glRotatef (z * 360, 0.0, 0.0, 1.0);
2867 GLfloat scale = 20.0 / SCENE_HEIGHT;
2868 glScalef(scale, scale, scale);
2871 glRotatef (180, 0, 0, 1);
2872 glTranslatef(-SCENE_WIDTH/2, -SCENE_HEIGHT/2, 0);
2873 glTranslatef(0, -150, 0);
2875 mi->polygon_count = 0;
2880 (void)gettimeofday(&tv, NULL);
2881 sp->time = (int) ((tv.tv_sec - sp->begintime)*1000 + tv.tv_usec/1000);
2883 sp->time += MI_DELAY(mi) / 1000;
2886 /* First pass: Move arms and strip out expired elements */
2887 for (traj = sp->head->next; traj != sp->head; traj = traj->next) {
2888 if (traj->status != PREDICTOR) {
2889 /* Skip any elements that need further processing */
2890 /* We could remove them, but there shoudn't be many and they
2891 would be needed if we ever got the pattern refiller
2895 if (traj->start > future) { /* Lookahead to the end of the show */
2896 future = traj->start;
2898 if (sp->time < traj->start) { /* early */
2900 } else if (sp->time < traj->finish) { /* working */
2902 /* Look for pattern name */
2903 if(traj->pattern != NULL) {
2904 pattern=traj->pattern;
2907 if (traj->type == Empty || traj->type == Full) {
2908 /* Only interested in hands on this pass */
2909 /* double angle = traj->angle + traj->spin * (sp->time - traj->start);*/
2910 double xd = 0, yd = 0;
2913 /* Find the catching offset */
2914 if(traj->object != NULL) {
2916 /* #### not sure what this is doing, but I'm guessing
2917 that the use of PERSPEC means this isn't needed
2918 in the OpenGL version? -jwz
2920 if(ObjectDefs[traj->object->type].handle > 0) {
2921 /* Handles Need to be oriented */
2922 xd = ObjectDefs[traj->object->type].handle *
2923 PERSPEC * sin(angle);
2924 yd = ObjectDefs[traj->object->type].handle *
2929 /* Balls are always caught at the bottom */
2934 p.x = (CUBIC(traj->xp, sp->time) - xd);
2935 p.y = (CUBIC(traj->yp, sp->time) + yd);
2936 reach_arm(mi, traj->hand, &p);
2938 /* Store updated hand position */
2942 if (traj->type == Ball || traj->type == Full) {
2943 /* Only interested in objects on this pass */
2947 if(traj->type == Full) {
2948 /* Adjusted these in the first pass */
2952 x = CUBIC(traj->xp, sp->time);
2953 y = CUBIC(traj->yp, sp->time);
2956 ADD_ELEMENT(Trace, s, traj->object->trace->prev);
2959 s->angle = traj->angle + traj->spin * (sp->time - traj->start);
2960 s->divisions = traj->divisions;
2961 traj->object->tracelen++;
2962 traj->object->active = True;
2964 } else { /* expired */
2965 Trajectory *n = traj;
2967 trajectory_destroy(n);
2972 mi->polygon_count += show_figure(mi, False);
2973 mi->polygon_count += show_arms(mi);
2976 glTranslatef(0, 0, ARMLENGTH);
2977 for (o = sp->objects->next; o != sp->objects; o = o->next) {
2979 mi->polygon_count += ObjectDefs[o->type].draw(mi, o->color,
2986 /* Save pattern name so we can erase it when it changes */
2987 if(pattern != NULL && strcmp(sp->pattern, pattern) != 0 ) {
2989 sp->pattern = strdup(pattern);
2991 if (MI_IS_VERBOSE(mi)) {
2992 (void) fprintf(stderr, "Juggle[%d]: Running: %s\n",
2993 MI_SCREEN(mi), sp->pattern);
2997 glColor3f (1, 1, 0);
2998 print_texture_label (mi->dpy, sp->font_data,
2999 mi->xgwa.width, mi->xgwa.height,
3003 if((int)(sp->time/10) % 1000 == 0)
3004 (void) fprintf(stderr, "sbrk: %d\n", (int)sbrk(0));
3007 if (future < sp->time + 100 * THROW_CATCH_INTERVAL) {
3009 } else if (sp->time > 1<<30) { /* Hard Reset before the clock wraps */
3015 if (mi->fps_p) do_fps (mi);
3018 glXSwapBuffers(dpy, window);
3021 XSCREENSAVER_MODULE_2 ("Juggler3D", juggler3d, juggle)