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"
150 #ifdef USE_GL /* whole file */
152 #define DEF_PATTERN "random" /* All patterns */
153 #define DEF_TAIL "1" /* No trace */
155 /* Maybe a ROLA BOLA would be at a better angle for viewing */
156 #define DEF_UNI "False" /* No unicycle */ /* Not implemented yet */
158 #define DEF_REAL "True"
159 #define DEF_DESCRIBE "True"
161 #define DEF_BALLS "True" /* Use Balls */
162 #define DEF_CLUBS "True" /* Use Clubs */
163 #define DEF_TORCHES "True" /* Use Torches */
164 #define DEF_KNIVES "True" /* Use Knives */
165 #define DEF_RINGS "True" /* Use Rings */
166 #define DEF_BBALLS "True" /* Use Bowling Balls */
168 static char *pattern;
174 static Bool describe;
183 static XrmOptionDescRec opts[] = {
184 {"-pattern", ".juggle.pattern", XrmoptionSepArg, NULL },
185 {"-tail", ".juggle.tail", XrmoptionSepArg, NULL },
187 {"-uni", ".juggle.uni", XrmoptionNoArg, "on" },
188 {"+uni", ".juggle.uni", XrmoptionNoArg, "off" },
190 {"-real", ".juggle.real", XrmoptionNoArg, "on" },
191 {"+real", ".juggle.real", XrmoptionNoArg, "off" },
192 {"-describe", ".juggle.describe", XrmoptionNoArg, "on" },
193 {"+describe", ".juggle.describe", XrmoptionNoArg, "off" },
194 {"-balls", ".juggle.balls", XrmoptionNoArg, "on" },
195 {"+balls", ".juggle.balls", XrmoptionNoArg, "off" },
196 {"-clubs", ".juggle.clubs", XrmoptionNoArg, "on" },
197 {"+clubs", ".juggle.clubs", XrmoptionNoArg, "off" },
198 {"-torches", ".juggle.torches", XrmoptionNoArg, "on" },
199 {"+torches", ".juggle.torches", XrmoptionNoArg, "off" },
200 {"-knives", ".juggle.knives", XrmoptionNoArg, "on" },
201 {"+knives", ".juggle.knives", XrmoptionNoArg, "off" },
202 {"-rings", ".juggle.rings", XrmoptionNoArg, "on" },
203 {"+rings", ".juggle.rings", XrmoptionNoArg, "off" },
204 {"-bballs", ".juggle.bballs", XrmoptionNoArg, "on" },
205 {"+bballs", ".juggle.bballs", XrmoptionNoArg, "off" },
206 {"-only", ".juggle.only", XrmoptionSepArg, NULL },
209 static argtype vars[] = {
210 { &pattern, "pattern", "Pattern", DEF_PATTERN, t_String },
211 { &tail, "tail", "Tail", DEF_TAIL, t_Int },
213 { &uni, "uni", "Uni", DEF_UNI, t_Bool },
215 { &real, "real", "Real", DEF_REAL, t_Bool },
216 { &describe, "describe", "Describe", DEF_DESCRIBE, t_Bool },
217 { &balls, "balls", "Clubs", DEF_BALLS, t_Bool },
218 { &clubs, "clubs", "Clubs", DEF_CLUBS, t_Bool },
219 { &torches, "torches", "Torches", DEF_TORCHES, t_Bool },
220 { &knives, "knives", "Knives", DEF_KNIVES, t_Bool },
221 { &rings, "rings", "Rings", DEF_RINGS, t_Bool },
222 { &bballs, "bballs", "BBalls", DEF_BBALLS, t_Bool },
223 { &only, "only", "BBalls", " ", t_String },
226 static OptionStruct desc[] =
228 { "-pattern string", "Cambridge Juggling Pattern" },
229 { "-tail num", "Trace Juggling Patterns" },
231 { "-/+uni", "Unicycle" },
233 { "-/+real", "Real-time" },
234 { "-/+describe", "turn on/off pattern descriptions." },
235 { "-/+balls", "turn on/off Balls." },
236 { "-/+clubs", "turn on/off Clubs." },
237 { "-/+torches", "turn on/off Flaming Torches." },
238 { "-/+knives", "turn on/off Knives." },
239 { "-/+rings", "turn on/off Rings." },
240 { "-/+bballs", "turn on/off Bowling Balls." },
241 { "-only", "Turn off all objects but the named one." },
244 ENTRYPOINT ModeSpecOpt juggle_opts =
245 {countof(opts), opts, countof(vars), vars, desc};
248 /* Note: All "lengths" are scaled by sp->scale = MI_HEIGHT/480. All
249 "thicknesses" are scaled by sqrt(sp->scale) so that they are
250 proportionally thicker for smaller windows. Objects spinning out
251 of the plane (such as clubs) fake perspective by compressing their
252 horizontal coordinates by PERSPEC */
256 #define ARMWIDTH ((int) (8.0 * sqrt(sp->scale)))
258 #define BALLRADIUS ARMWIDTH
260 /* build all the models assuming a 480px high scene */
261 #define SCENE_HEIGHT 480
262 #define SCENE_WIDTH ((int)(SCENE_HEIGHT*(MI_WIDTH(mi)/(float)MI_HEIGHT(mi))))
264 /*#define PERSPEC 0.4*/
267 #define GRAVITY(h, t) 4*(double)(h)/((t)*(t))
269 /* Timing based on count. Units are milliseconds. Juggles per second
270 is: 2000 / THROW_CATCH_INTERVAL + CATCH_THROW_INTERVAL */
272 #define THROW_CATCH_INTERVAL (sp->count)
273 #define THROW_NULL_INTERVAL (sp->count * 0.5)
274 #define CATCH_THROW_INTERVAL (sp->count * 0.2)
276 /********************************************************************
277 * Trace Definitions *
279 * These record rendering data so that a drawn object can be erased *
280 * later. Each object has its own Trace list. *
282 ********************************************************************/
284 typedef struct {double x, y; } DXPoint;
285 typedef struct trace *TracePtr;
286 typedef struct trace {
297 /*******************************************************************
298 * Object Definitions *
300 * These describe the various types of Object that can be juggled *
302 *******************************************************************/
303 typedef int (DrawProc)(ModeInfo*, unsigned long, Trace *);
305 static DrawProc show_ball, show_europeanclub, show_torch, show_knife;
306 static DrawProc show_ring, show_bball;
308 typedef enum {BALL, CLUB, TORCH, KNIFE, RING, BBALLS,
309 NUM_OBJECT_TYPES} ObjType;
311 #define OBJMIXPROB 20 /* inverse of the chances of using an odd
312 object in the pattern */
314 static const GLfloat body_color_1[4] = { 0.9, 0.7, 0.5, 1 };
315 static const GLfloat body_color_2[4] = { 0.6, 0.4, 0.2, 1 };
317 static const struct {
318 DrawProc *draw; /* Object Rendering function */
319 int handle; /* Length of object's handle */
320 int mintrail; /* Minimum trail length */
321 double cor; /* Coefficient of Restitution. perfect bounce = 1 */
322 double weight; /* Heavier objects don't get thrown as high */
335 0.55, /* Clubs don't bounce too well */
341 20, /* Torches need flames */
342 0, /* Torches don't bounce -- fire risk! */
349 0, /* Knives don't bounce */
368 /**************************
369 * Trajectory definitions *
370 **************************/
372 typedef enum {HEIGHT, ADAM} Notation;
373 typedef enum {Empty, Full, Ball} Throwable;
374 typedef enum {LEFT, RIGHT} Hand;
375 typedef enum {THROW, CATCH} Action;
376 typedef enum {HAND, ELBOW, SHOULDER} Joint;
377 typedef enum {ATCH, THRATCH, ACTION, LINKEDACTION,
378 PTHRATCH, BPREDICTOR, PREDICTOR} TrajectoryStatus;
379 typedef struct {double a, b, c, d; } Spline;
380 typedef DXPoint Arm[3];
383 /* Object is an arbitrary object being juggled. Each Trajectory
384 * references an Object ("count" tracks this), and each Object is also
385 * linked into a global Objects list. Objects may include a Trace
386 * list for tracking erasures. */
387 typedef struct object *ObjectPtr;
388 typedef struct object {
389 ObjectPtr next, prev;
393 int count; /* reference count */
394 Bool active; /* Object is in use */
404 /* Trajectory is a segment of juggling action. A list of Trajectories
405 * defines the juggling performance. The Trajectory list goes through
406 * multiple processing steps to convert it from basic juggling
407 * notation into rendering data. */
409 typedef struct trajectory *TrajectoryPtr;
410 typedef struct trajectory {
411 TrajectoryPtr prev, next; /* for building list */
412 TrajectoryStatus status;
430 TrajectoryPtr balllink;
431 TrajectoryPtr handlink;
434 double cx; /* Moving juggler */
435 double x, y; /* current position */
436 double dx, dy; /* initial velocity */
440 unsigned long start, finish;
454 const char * pattern;
458 /* List of popular patterns, in any order */
459 /* Patterns should be given in Adam notation so the generator can
460 concatenate them safely. Null descriptions are ok. Height
461 notation will be displayed automatically. */
462 /* Can't const this because it is qsorted. This *should* be reentrant,
464 static /*const*/ patternstruct portfolio[] = {
465 {"[+2 1]", /* +3 1 */ "Typical 2 ball juggler"},
466 {"[2 0]", /* 4 0 */ "2 in 1 hand"},
467 {"[2 0 1]", /* 5 0 1 */},
468 {"[+2 0 +2 0 0]" /* +5 0 +5 0 0 */},
469 {"[+2 0 1 2 2]", /* +4 0 1 2 3 */},
470 {"[2 0 1 1]", /* 6 0 1 1 */},
472 {"[3]", /* 3 */ "3 cascade"},
473 {"[+3]", /* +3 */ "reverse 3 cascade"},
474 {"[=3]", /* =3 */ "cascade 3 under arm"},
475 {"[&3]", /* &3 */ "cascade 3 catching under arm"},
476 {"[_3]", /* _3 */ "bouncing 3 cascade"},
477 {"[+3 x3 =3]", /* +3 x3 =3 */ "Mill's mess"},
478 {"[3 2 1]", /* 5 3 1" */},
479 {"[3 3 1]", /* 4 4 1" */},
480 {"[3 1 2]", /* 6 1 2 */ "See-saw"},
481 {"[=3 3 1 2]", /* =4 5 1 2 */},
482 {"[=3 2 2 3 1 2]", /* =6 2 2 5 1 2 */ "=4 5 1 2 stretched"},
483 {"[+3 3 1 3]", /* +4 4 1 3 */ "anemic shower box"},
484 {"[3 3 1]", /* 4 4 1 */},
485 {"[+3 2 3]", /* +4 2 3 */},
486 {"[+3 1]", /* +5 1 */ "3 shower"},
487 {"[_3 1]", /* _5 1 */ "bouncing 3 shower"},
488 {"[3 0 3 0 3]", /* 5 0 5 0 5 */ "shake 3 out of 5"},
489 {"[3 3 3 0 0]", /* 5 5 5 0 0 */ "flash 3 out of 5"},
490 {"[3 3 0]", /* 4 5 0 */ "complete waste of a 5 ball juggler"},
491 {"[3 3 3 0 0 0 0]", /* 7 7 7 0 0 0 0 */ "3 flash"},
492 {"[+3 0 +3 0 +3 0 0]", /* +7 0 +7 0 +7 0 0 */},
493 {"[3 2 2 0 3 2 0 2 3 0 2 2 0]", /* 7 3 3 0 7 3 0 3 7 0 3 3 0 */},
494 {"[3 0 2 0]", /* 8 0 4 0 */},
495 {"[_3 2 1]", /* _5 3 1 */},
496 {"[_3 0 1]", /* _8 0 1 */},
497 {"[1 _3 1 _3 0 1 _3 0]", /* 1 _7 1 _7 0 1 _7 0 */},
498 {"[_3 2 1 _3 1 2 1]", /* _6 3 1 _6 1 3 1 */},
500 {"[4]", /* 4 */ "4 cascade"},
501 {"[+4 3]", /* +5 3 */ "4 ball half shower"},
502 {"[4 4 2]", /* 5 5 2 */},
503 {"[+4 4 4 +4]", /* +4 4 4 +4 */ "4 columns"},
504 {"[+4 3 +4]", /* +5 3 +4 */},
505 {"[4 3 4 4]", /* 5 3 4 4 */},
506 {"[4 3 3 4]", /* 6 3 3 4 */},
507 {"[4 3 2 4", /* 6 4 2 4 */},
508 {"[+4 1]", /* +7 1 */ "4 shower"},
509 {"[4 4 4 4 0]", /* 5 5 5 5 0 */ "learning 5"},
510 {"[+4 x4 =4]", /* +4 x4 =4 */ "Mill's mess for 4"},
511 {"[+4 2 1 3]", /* +9 3 1 3 */},
512 {"[4 4 1 4 1 4]", /* 6 6 1 5 1 5, by Allen Knutson */},
513 {"[_4 _4 _4 1 _4 1]", /* _5 _6 _6 1 _5 1 */},
514 {"[_4 3 3]", /* _6 3 3 */},
515 {"[_4 3 1]", /* _7 4 1 */},
516 {"[_4 2 1]", /* _8 3 1 */},
517 {"[_4 3 3 3 0]", /* _8 4 4 4 0 */},
518 {"[_4 1 3 1]", /* _9 1 5 1 */},
519 {"[_4 1 3 1 2]", /* _10 1 6 1 2 */},
521 {"[5]", /* 5 */ "5 cascade"},
522 {"[_5 _5 _5 _5 _5 5 5 5 5 5]", /* _5 _5 _5 _5 _5 5 5 5 5 5 */},
523 {"[+5 x5 =5]", /* +5 x5 =5 */ "Mill's mess for 5"},
524 {"[5 4 4]", /* 7 4 4 */},
525 {"[_5 4 4]", /* _7 4 4 */},
526 {"[1 2 3 4 5 5 5 5 5]", /* 1 2 3 4 5 6 7 8 9 */ "5 ramp"},
527 {"[5 4 5 3 1]", /* 8 5 7 4 1, by Allen Knutson */},
528 {"[_5 4 1 +4]", /* _9 5 1 5 */},
529 {"[_5 4 +4 +4]", /* _8 4 +4 +4 */},
530 {"[_5 4 4 4 1]", /* _9 5 5 5 1 */},
532 {"[_5 4 4 +4 4 0]", /*_10 5 5 +5 5 0 */},
534 {"[6]", /* 6 */ "6 cascade"},
535 {"[+6 5]", /* +7 5 */},
536 {"[6 4]", /* 8 4 */},
537 {"[+6 3]", /* +9 3 */},
538 {"[6 5 4 4]", /* 9 7 4 4 */},
539 {"[+6 5 5 5]", /* +9 5 5 5 */},
540 {"[6 0 6]", /* 9 0 9 */},
541 {"[_6 0 _6]", /* _9 0 _9 */},
543 {"[_7]", /* _7 */ "bouncing 7 cascade"},
544 {"[7]", /* 7 */ "7 cascade"},
545 {"[7 6 6 6 6]", /* 11 6 6 6 6 */ "Gatto's High Throw"},
551 typedef struct { int start; int number; } PatternIndex;
553 struct patternindex {
556 PatternIndex index[countof(portfolio)];
560 /* Jugglestruct: per-screen global data. The master Object
561 * and Trajectory lists are anchored here. */
563 GLXContext *glx_context;
565 trackball_state *trackball;
576 time_t begintime; /* should make 'time' usable for at least 48 days
577 on a 32-bit machine */
578 unsigned long time; /* millisecond timer*/
581 struct patternindex patternindex;
582 texture_font_data *font_data;
585 static jugglestruct *juggles = (jugglestruct *) NULL;
591 #define DUP_OBJECT(n, t) { \
592 (n)->object = (t)->object; \
593 if((n)->object != NULL) (n)->object->count++; \
596 /* t must point to an existing element. t must not be an
597 expression ending ->next or ->prev */
598 #define REMOVE(t) { \
599 (t)->next->prev = (t)->prev; \
600 (t)->prev->next = (t)->next; \
604 /* t receives element to be created and added to the list. ot must
605 point to an existing element or be identical to t to start a new
606 list. Applicable to Trajectories, Objects and Traces. */
607 #define ADD_ELEMENT(type, t, ot) \
608 if (((t) = (type*)calloc(1,sizeof(type))) != NULL) { \
609 (t)->next = (ot)->next; \
612 (t)->next->prev = (t); \
616 object_destroy(Object* o)
618 if(o->trace != NULL) {
619 while(o->trace->next != o->trace) {
620 Trace *s = o->trace->next;
621 REMOVE(s); /* Don't eliminate 's' */
629 trajectory_destroy(Trajectory *t) {
630 if(t->name != NULL) free(t->name);
631 if(t->pattern != NULL) free(t->pattern);
632 /* Reduce object link count and call destructor if necessary */
633 if(t->object != NULL && --t->object->count < 1 && t->object->tracelen == 0) {
634 object_destroy(t->object);
636 REMOVE(t); /* Unlink and free */
640 free_juggle(jugglestruct *sp) {
641 if (sp->head != NULL) {
642 while (sp->head->next != sp->head) {
643 trajectory_destroy(sp->head->next);
646 sp->head = (Trajectory *) NULL;
648 if(sp->objects != NULL) {
649 while (sp->objects->next != sp->objects) {
650 object_destroy(sp->objects->next);
653 sp->objects = (Object*)NULL;
655 if(sp->pattern != NULL) {
662 add_throw(jugglestruct *sp, char type, int h, Notation n, const char* name)
666 ADD_ELEMENT(Trajectory, t, sp->head->prev);
667 if(t == NULL){ /* Out of Memory */
673 t->name = strdup(name);
686 /* add a Thratch to the performance */
688 program(ModeInfo *mi, const char *patn, const char *name, int cycles)
690 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
696 if (MI_IS_VERBOSE(mi)) {
697 (void) fprintf(stderr, "juggle[%d]: Programmed: %s x %d\n",
698 MI_SCREEN(mi), (name == NULL) ? patn : name, cycles);
701 for(w=i=0; i < cycles; i++, w++) { /* repeat until at least "cycles" throws
702 have been programmed */
703 /* title is the pattern name to be supplied to the first throw of
704 a sequence. If no name if given, use an empty title so that
705 the sequences are still delimited. */
706 const char *title = (name != NULL)? name : "";
711 for(p=patn; *p; p++) {
712 if (*p >= '0' && *p <='9') {
714 h = 10*h + (*p - '0');
716 Notation nn = notation;
718 case '[': /* begin Adam notation */
721 case '-': /* Inside throw */
724 case '+': /* Outside throw */
725 case '=': /* Cross throw */
726 case '&': /* Cross catch */
727 case 'x': /* Cross throw and catch */
728 case '_': /* Bounce */
729 case 'k': /* Kickup */
732 case '*': /* Lose ball */
736 case ']': /* end Adam notation */
742 if (!add_throw(sp, type, h, notation, title))
752 if(w == 0) { /* Only warn on first pass */
753 (void) fprintf(stderr,
754 "juggle[%d]: Unexpected pattern instruction: '%c'\n",
761 if (seen) { /* end of sequence */
762 if (!add_throw(sp, type, h, notation, title))
776 [ 3 3 1 3 4 2 3 1 3 3 4 0 2 1 ]
778 4 4 1 3 12 2 4 1 4 4 13 0 3 1
781 #define BOUNCEOVER 10
785 /* Convert Adam notation into heights */
787 adam(jugglestruct *sp)
790 for(t = sp->head->next; t != sp->head; t = t->next) {
791 if (t->status == ATCH) {
794 for(p = t->next; a > 0; p = p->next) {
796 t->height = -9; /* Indicate end of processing for name() */
799 if (p->status != ATCH || p->adam < 0 || p->adam>= a) {
804 if(t->height > BOUNCEOVER && t->posn == ' '){
805 t->posn = '_'; /* high defaults can be bounced */
806 } else if(t->height < 3 && t->posn == '_') {
807 t->posn = ' '; /* Can't bounce short throws. */
809 if(t->height < KICKMIN && t->posn == 'k'){
810 t->posn = ' '; /* Can't kick short throws */
812 if(t->height > THROWMAX){
813 t->posn = 'k'; /* Use kicks for ridiculously high throws */
820 /* Discover converted heights and update the sequence title */
822 name(jugglestruct *sp)
827 for(t = sp->head->next; t != sp->head; t = t->next) {
828 if (t->status == THRATCH && t->name != NULL) {
830 for(p = t; p == t || p->name == NULL; p = p->next) {
831 if(p == sp->head || p->height < 0) { /* end of reliable data */
835 b += sprintf(b, " %d", p->height);
837 b += sprintf(b, " %c%d", p->posn, p->height);
839 if(b - buffer > 500) break; /* otherwise this could eventually
840 overflow. It'll be too big to
844 (void) sprintf(b, ", %s", t->name);
846 free(t->name); /* Don't need name any more, it's been converted
849 if(t->pattern != NULL) free(t->pattern);
850 t->pattern = strdup(buffer);
855 /* Split Thratch notation into explicit throws and catches.
856 Usually Catch follows Throw in same hand, but take care of special
859 /* ..n1.. -> .. LTn RT1 LC RC .. */
860 /* ..nm.. -> .. LTn LC RTm RC .. */
863 part(jugglestruct *sp)
865 Trajectory *t, *nt, *p;
866 Hand hand = (LRAND() & 1) ? RIGHT : LEFT;
868 for (t = sp->head->next; t != sp->head; t = t->next) {
869 if (t->status > THRATCH) {
871 } else if (t->status == THRATCH) {
874 /* plausibility check */
875 if (t->height <= 2 && t->posn == '_') {
876 t->posn = ' '; /* no short bounces */
878 if (t->height <= 1 && (t->posn == '=' || t->posn == '&')) {
879 t->posn = ' '; /* 1's need close catches */
884 case ' ': posn = '-'; t->posn = '+'; break;
885 case '+': posn = '+'; t->posn = '-'; break;
886 case '=': posn = '='; t->posn = '+'; break;
887 case '&': posn = '+'; t->posn = '='; break;
888 case 'x': posn = '='; t->posn = '='; break;
889 case '_': posn = '_'; t->posn = '-'; break;
890 case 'k': posn = 'k'; t->posn = 'k'; break;
892 (void) fprintf(stderr, "juggle: unexpected posn %c\n", t->posn);
895 hand = (Hand) ((hand + 1) % 2);
900 if (t->height == 1 && p != sp->head) {
901 p = p->prev; /* '1's are thrown earlier than usual */
907 ADD_ELEMENT(Trajectory, nt, p);
915 nt->height = t->height;
925 choose_object(void) {
928 o = (ObjType)NRAND((ObjType)NUM_OBJECT_TYPES);
929 if(balls && o == BALL) break;
930 if(clubs && o == CLUB) break;
931 if(torches && o == TORCH) break;
932 if(knives && o == KNIFE) break;
933 if(rings && o == RING) break;
934 if(bballs && o == BBALLS) break;
939 /* Connnect up throws and catches to figure out which ball goes where.
940 Do the same with the juggler's hands. */
945 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
948 for (t = sp->head->next; t != sp->head; t = t->next) {
949 if (t->status == ACTION) {
950 if (t->action == THROW) {
951 if (t->type == Empty) {
952 /* Create new Object */
953 ADD_ELEMENT(Object, t->object, sp->objects);
954 t->object->count = 1;
955 t->object->tracelen = 0;
956 t->object->active = False;
957 /* Initialise object's circular trace list */
958 ADD_ELEMENT(Trace, t->object->trace, t->object->trace);
960 if (MI_NPIXELS(mi) > 2) {
961 t->object->color = 1 + NRAND(MI_NPIXELS(mi) - 2);
964 t->object->color = 1;
966 t->object->color = 0;
970 /* Small chance of picking a random object instead of the
972 if(NRAND(OBJMIXPROB) == 0) {
973 t->object->type = choose_object();
975 t->object->type = sp->objtypes;
978 /* Check to see if we need trails for this object */
979 if(tail < ObjectDefs[t->object->type].mintrail) {
980 t->object->tail = ObjectDefs[t->object->type].mintrail;
982 t->object->tail = tail;
986 /* Balls can change divisions at each throw */
987 /* no, that looks stupid. -jwz */
988 if (t->divisions < 1)
989 t->divisions = 2 * (NRAND(2) + 1);
991 /* search forward for next catch in this hand */
992 for (p = t->next; t->handlink == NULL; p = p->next) {
993 if(p->status < ACTION || p == sp->head) return;
994 if (p->action == CATCH) {
995 if (t->handlink == NULL && p->hand == t->hand) {
1001 if (t->height > 0) {
1004 /* search forward for next ball catch */
1005 for (p = t->next; t->balllink == NULL; p = p->next) {
1006 if(p->status < ACTION || p == sp->head) {
1010 if (p->action == CATCH) {
1011 if (t->balllink == NULL && --h < 1) { /* caught */
1012 t->balllink = p; /* complete trajectory */
1014 if (p->type == Full) {
1015 (void) fprintf(stderr, "juggle[%d]: Dropped %d\n",
1016 MI_SCREEN(mi), t->object->color);
1020 DUP_OBJECT(p, t); /* accept catch */
1021 p->angle = t->angle;
1022 p->divisions = t->divisions;
1027 t->type = Empty; /* thrown */
1028 } else if (t->action == CATCH) {
1029 /* search forward for next throw from this hand */
1030 for (p = t->next; t->handlink == NULL; p = p->next) {
1031 if(p->status < ACTION || p == sp->head) return;
1032 if (p->action == THROW && p->hand == t->hand) {
1033 p->type = t->type; /* pass ball */
1034 DUP_OBJECT(p, t); /* pass object */
1035 p->divisions = t->divisions;
1040 t->status = LINKEDACTION;
1045 /* Clap when both hands are empty */
1047 clap(jugglestruct *sp)
1050 for (t = sp->head->next; t != sp->head; t = t->next) {
1051 if (t->status == LINKEDACTION &&
1052 t->action == CATCH &&
1054 t->handlink != NULL &&
1055 t->handlink->height == 0) { /* Completely idle hand */
1057 for (p = t->next; p != sp->head; p = p->next) {
1058 if (p->status == LINKEDACTION &&
1059 p->action == CATCH &&
1060 p->hand != t->hand) { /* Next catch other hand */
1061 if(p->type == Empty &&
1062 p->handlink != NULL &&
1063 p->handlink->height == 0) { /* Also completely idle */
1065 t->handlink->posn = '^'; /* Move first hand's empty throw */
1066 p->posn = '^'; /* to meet second hand's empty
1070 break; /* Only need first catch */
1077 #define CUBIC(s, t) ((((s).a * (t) + (s).b) * (t) + (s).c) * (t) + (s).d)
1079 /* Compute single spline from x0 with velocity dx0 at time t0 to x1
1080 with velocity dx1 at time t1 */
1082 makeSpline(double x0, double dx0, int t0, double x1, double dx1, int t1)
1091 a = ((dx0 + dx1)*t10 - 2*x10) / (t10*t10*t10);
1092 b = (3*x10 - (2*dx0 + dx1)*t10) / (t10*t10);
1097 s.c = (3*a*t0 - 2*b)*t0 + c;
1098 s.d = ((-a*t0 + b)*t0 - c)*t0 +d;
1102 /* Compute a pair of splines. s1 goes from x0 vith velocity dx0 at
1103 time t0 to x1 at time t1. s2 goes from x1 at time t1 to x2 with
1104 velocity dx2 at time t2. The arrival and departure velocities at
1105 x1, t1 must be the same. */
1107 makeSplinePair(Spline *s1, Spline *s2,
1108 double x0, double dx0, int t0,
1110 double x2, double dx2, int t2)
1112 double x10, x21, t21, t10, t20, dx1;
1118 dx1 = (3*x10*t21*t21 + 3*x21*t10*t10 + 3*dx0*t10*t21*t21
1119 - dx2*t10*t10*t21 - 4*dx0*t10*t21*t21) /
1121 *s1 = makeSpline(x0, dx0, t0, x1, dx1, t1);
1122 *s2 = makeSpline(x1, dx1, t1, x2, dx2, t2);
1126 /* Compute a Ballistic path in a pair of degenerate splines. sx goes
1127 from x at time t at constant velocity dx. sy goes from y at time t
1128 with velocity dy and constant acceleration g. */
1130 makeParabola(Trajectory *n,
1131 double x, double dx, double y, double dy, double g)
1133 double t = (double)n->start;
1137 n->xp.d = -dx*t + x;
1140 n->yp.c = -g*t + dy;
1141 n->yp.d = g/2*t*t - dy*t + y;
1147 #define SX 25 /* Shoulder Width */
1149 /* Convert hand position symbols into actual time/space coordinates */
1151 positions(jugglestruct *sp)
1154 unsigned long now = sp->time; /* Make sure we're not lost in the past */
1155 for (t = sp->head->next; t != sp->head; t = t->next) {
1156 if (t->status >= PTHRATCH) {
1158 } else if (t->status == ACTION || t->status == LINKEDACTION) {
1159 /* Allow ACTIONs to be annotated, but we won't mark them ready
1160 for the next stage */
1167 if (t->action == CATCH) { /* Throw-to-catch */
1168 if (t->type == Empty) {
1169 now += (int) THROW_NULL_INTERVAL; /* failed catch is short */
1170 } else { /* successful catch */
1171 now += (int)(THROW_CATCH_INTERVAL);
1173 } else { /* Catch-to-throw */
1174 if(t->object != NULL) {
1175 now += (int) (CATCH_THROW_INTERVAL *
1176 ObjectDefs[t->object->type].weight);
1178 now += (int) (CATCH_THROW_INTERVAL);
1184 else /* Concatenated performances may need clock resync */
1192 /* Add room for the handle */
1193 if(t->action == CATCH && t->object != NULL)
1194 yo -= ObjectDefs[t->object->type].handle;
1197 case '-': xo = sx - pose; break;
1200 case '+': xo = sx + pose; break;
1202 case '=': xo = - sx - pose; yo += pose; break;
1203 case '^': xo = 0; yo += pose*2; break; /* clap */
1205 (void) fprintf(stderr, "juggle: unexpected posn %c\n", t->posn);
1209 #ifdef _2DSpinsDontWorkIn3D
1210 t->angle = (((t->hand == LEFT) ^
1211 (t->posn == '+' || t->posn == '_' || t->posn == 'k' ))?
1217 t->x = t->cx + ((t->hand == LEFT) ? xo : -xo);
1220 /* Only mark complete if it was already linked */
1221 if(t->status == LINKEDACTION) {
1222 t->status = PTHRATCH;
1229 /* Private physics functions */
1231 /* Compute the spin-rate for a trajectory. Different types of throw
1232 (eg, regular thows, bounces, kicks, etc) have different spin
1235 type = type of object
1236 h = trajectory of throwing hand (throws), or next throwing hand (catches)
1237 old = earlier spin to consider
1238 dt = time span of this trajectory
1239 height = height of ball throw or 0 if based on old spin
1240 turns = full club turns required during this operation
1241 togo = partial club turns required to match hands
1244 spinrate(ObjType type, Trajectory *h, double old, double dt,
1245 int height, int turns, double togo)
1247 #ifdef _2DSpinsDontWorkIn3D
1248 const int dir = (h->hand == LEFT) ^ (h->posn == '+')? -1 : 1;
1253 if(ObjectDefs[type].handle != 0) { /* Clubs */
1254 return (dir * turns * 2 * M_PI + togo) / dt;
1255 } else if(height == 0) { /* Balls already spinning */
1257 } else { /* Balls */
1258 return dir * NRAND(height*10)/20/ObjectDefs[type].weight * 2 * M_PI / dt;
1263 /* compute the angle at the end of a spinning trajectory */
1265 end_spin(Trajectory *t)
1267 return t->angle + t->spin * (t->finish - t->start);
1270 /* Sets the initial angle of the catch following hand movement t to
1271 the final angle of the throw n. Also sets the angle of the
1272 subsequent throw to the same angle plus half a turn. */
1274 match_spins_on_catch(Trajectory *t, Trajectory *n)
1276 if(ObjectDefs[t->balllink->object->type].handle == 0) {
1277 t->balllink->angle = end_spin(n);
1278 if(t->balllink->handlink != NULL) {
1279 #ifdef _2DSpinsDontWorkIn3D
1280 t->balllink->handlink->angle = t->balllink->angle + M_PI;
1282 t->balllink->handlink->angle = t->balllink->angle;
1289 find_bounce(jugglestruct *sp,
1290 double yo, double yf, double yc, double tc, double cor)
1292 double tb, i, dy = 0;
1293 const double e = 1; /* permissible error in yc */
1297 yt = height at catch time after one bounce
1298 one or three roots according to timing
1299 find one by interval bisection
1302 for(i = tc / 2; i > 0.0001; i/=2){
1305 (void) fprintf(stderr, "juggle: bounce div by zero!\n");
1308 dy = (yf - yo)/tb + sp->Gr/2*tb;
1310 yt = -cor*dy*dt + sp->Gr/2*dt*dt + yf;
1313 }else if(yt > yc - e){
1319 if(dy*THROW_CATCH_INTERVAL < -200) { /* bounce too hard */
1326 new_predictor(const Trajectory *t, int start, int finish, double angle)
1329 ADD_ELEMENT(Trajectory, n, t->prev);
1334 n->divisions = t->divisions;
1336 n->status = PREDICTOR;
1344 /* Turn abstract timings into physically appropriate object trajectories. */
1346 projectile(jugglestruct *sp)
1349 const int yf = 0; /* Floor height */
1351 for (t = sp->head->next; t != sp->head; t = t->next) {
1352 if (t->status != PTHRATCH || t->action != THROW) {
1354 } else if (t->balllink == NULL) { /* Zero Throw */
1355 t->status = BPREDICTOR;
1356 } else if (t->balllink->handlink == NULL) { /* Incomplete */
1358 } else if(t->balllink == t->handlink) {
1359 /* '2' height - hold on to ball. Don't need to consider
1360 flourishes, 'hands' will do that automatically anyway */
1363 /* Zero spin to avoid wrist injuries */
1365 match_spins_on_catch(t, t);
1367 t->status = BPREDICTOR;
1370 if (t->posn == '_') { /* Bounce once */
1372 const int tb = t->start +
1373 find_bounce(sp, t->y, (double) yf, t->balllink->y,
1374 (double) (t->balllink->start - t->start),
1375 ObjectDefs[t->object->type].cor);
1377 if(tb < t->start) { /* bounce too hard */
1378 t->posn = '+'; /* Use regular throw */
1380 Trajectory *n; /* First (throw) trajectory. */
1381 double dt; /* Time span of a trajectory */
1382 double dy; /* Distance span of a follow-on trajectory.
1383 First trajectory uses t->dy */
1384 /* dx is constant across both trajectories */
1385 t->dx = (t->balllink->x - t->x) / (t->balllink->start - t->start);
1387 { /* ball follows parabola down */
1388 n = new_predictor(t, t->start, tb, t->angle);
1389 if(n == NULL) return False;
1390 dt = n->finish - n->start;
1391 /* Ball rate 4, no flight or matching club turns */
1392 n->spin = spinrate(t->object->type, t, 0.0, dt, 4, 0, 0.0);
1393 t->dy = (yf - t->y)/dt - sp->Gr/2*dt;
1394 makeParabola(n, t->x, t->dx, t->y, t->dy, sp->Gr);
1397 { /* ball follows parabola up */
1398 Trajectory *m = new_predictor(t, n->finish, t->balllink->start,
1400 if(m == NULL) return False;
1401 dt = m->finish - m->start;
1402 /* Use previous ball rate, no flight club turns */
1403 m->spin = spinrate(t->object->type, t, n->spin, dt, 0, 0,
1404 t->balllink->angle - m->angle);
1405 match_spins_on_catch(t, m);
1406 dy = (t->balllink->y - yf)/dt - sp->Gr/2 * dt;
1407 makeParabola(m, t->balllink->x - t->dx * dt,
1408 t->dx, (double) yf, dy, sp->Gr);
1411 t->status = BPREDICTOR;
1414 } else if (t->posn == 'k') { /* Drop & Kick */
1415 Trajectory *n; /* First (drop) trajectory. */
1416 Trajectory *o; /* Second (rest) trajectory */
1417 Trajectory *m; /* Third (kick) trajectory */
1418 const int td = t->start + 2*THROW_CATCH_INTERVAL; /* Drop time */
1419 const int tk = t->balllink->start - 5*THROW_CATCH_INTERVAL; /* Kick */
1422 { /* Fall to ground */
1423 n = new_predictor(t, t->start, td, t->angle);
1424 if(n == NULL) return False;
1425 dt = n->finish - n->start;
1426 /* Ball spin rate 4, no flight club turns */
1427 n->spin = spinrate(t->object->type, t, 0.0, dt, 4, 0,
1428 t->balllink->angle - n->angle);
1429 t->dx = (t->balllink->x - t->x) / dt;
1430 t->dy = (yf - t->y)/dt - sp->Gr/2*dt;
1431 makeParabola(n, t->x, t->dx, t->y, t->dy, sp->Gr);
1434 { /* Rest on ground */
1435 o = new_predictor(t, n->finish, tk, end_spin(n));
1436 if(o == NULL) return False;
1438 makeParabola(o, t->balllink->x, 0.0, (double) yf, 0.0, 0.0);
1443 m = new_predictor(t, o->finish, t->balllink->start, end_spin(o));
1444 if(m == NULL) return False;
1445 dt = m->finish - m->start;
1446 /* Match receiving hand, ball rate 4, one flight club turn */
1447 m->spin = spinrate(t->object->type, t->balllink->handlink, 0.0, dt,
1448 4, 1, t->balllink->angle - m->angle);
1449 match_spins_on_catch(t, m);
1450 dy = (t->balllink->y - yf)/dt - sp->Gr/2 * dt;
1451 makeParabola(m, t->balllink->x, 0.0, (double) yf, dy, sp->Gr);
1454 t->status = BPREDICTOR;
1458 /* Regular flight, no bounce */
1459 { /* ball follows parabola */
1461 Trajectory *n = new_predictor(t, t->start,
1462 t->balllink->start, t->angle);
1463 if(n == NULL) return False;
1464 dt = t->balllink->start - t->start;
1466 n->spin = spinrate(t->object->type, t, 0.0, dt, t->height, t->height/2,
1467 t->balllink->angle - n->angle);
1468 match_spins_on_catch(t, n);
1469 t->dx = (t->balllink->x - t->x) / dt;
1470 t->dy = (t->balllink->y - t->y) / dt - sp->Gr/2 * dt;
1471 makeParabola(n, t->x, t->dx, t->y, t->dy, sp->Gr);
1474 t->status = BPREDICTOR;
1480 /* Turn abstract hand motions into cubic splines. */
1482 hands(jugglestruct *sp)
1484 Trajectory *t, *u, *v;
1486 for (t = sp->head->next; t != sp->head; t = t->next) {
1487 /* no throw => no velocity */
1488 if (t->status != BPREDICTOR) {
1493 if (u == NULL) { /* no next catch */
1497 if (v == NULL) { /* no next throw */
1501 /* double spline takes hand from throw, thru catch, to
1504 t->finish = u->start;
1505 t->status = PREDICTOR;
1507 u->finish = v->start;
1508 u->status = PREDICTOR;
1511 /* FIXME: These adjustments leave a small glitch when alternating
1512 balls and clubs. Just hope no-one notices. :-) */
1514 /* make sure empty hand spin matches the thrown object in case it
1517 t->spin = ((t->hand == LEFT)? -1 : 1 ) *
1518 fabs((u->angle - t->angle)/(u->start - t->start));
1520 u->spin = ((v->hand == LEFT) ^ (v->posn == '+')? -1 : 1 ) *
1521 fabs((v->angle - u->angle)/(v->start - u->start));
1523 (void) makeSplinePair(&t->xp, &u->xp,
1524 t->x, t->dx, t->start,
1526 v->x, v->dx, v->start);
1527 (void) makeSplinePair(&t->yp, &u->yp,
1528 t->y, t->dy, t->start,
1530 v->y, v->dy, v->start);
1532 t->status = PREDICTOR;
1536 /* Given target x, y find_elbow puts hand at target if possible,
1537 * otherwise makes hand point to the target */
1539 find_elbow(int armlength, DXPoint *h, DXPoint *e, DXPoint *p, DXPoint *s,
1543 double x = p->x - s->x;
1544 double y = p->y - s->y;
1545 h2 = x*x + y*y + z*z;
1546 if (h2 > 4 * armlength * armlength) {
1547 t = armlength/sqrt(h2);
1550 h->x = 2 * t * x + s->x;
1551 h->y = 2 * t * y + s->y;
1553 r = sqrt((double)(x*x + z*z));
1554 t = sqrt(4 * armlength * armlength / h2 - 1);
1555 e->x = x*(1 + y*t/r)/2 + s->x;
1556 e->y = (y - r*t)/2 + s->y;
1563 /* NOTE: returned x, y adjusted for arm reach */
1565 reach_arm(ModeInfo * mi, Hand side, DXPoint *p)
1567 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
1569 find_elbow(40, &h, &e, p, &sp->arm[1][side][SHOULDER], 25);
1570 *p = sp->arm[1][side][HAND] = h;
1571 sp->arm[1][side][ELBOW] = e;
1575 /* dumps a human-readable rendition of the current state of the juggle
1576 pipeline to stderr for debugging */
1578 dump(jugglestruct *sp)
1581 for (t = sp->head->next; t != sp->head; t = t->next) {
1582 switch (t->status) {
1584 (void) fprintf(stderr, "%p a %c%d\n", (void*)t, t->posn, t->adam);
1587 (void) fprintf(stderr, "%p T %c%d %s\n", (void*)t, t->posn, t->height,
1588 t->pattern == NULL?"":t->pattern);
1591 if (t->action == CATCH)
1592 (void) fprintf(stderr, "%p A %c%cC\n",
1594 t->hand ? 'R' : 'L');
1596 (void) fprintf(stderr, "%p A %c%c%c%d\n",
1598 t->hand ? 'R' : 'L',
1599 (t->action == THROW)?'T':'N',
1603 (void) fprintf(stderr, "%p L %c%c%c%d %d %p %p\n",
1606 (t->action == THROW)?'T':(t->action == CATCH?'C':'N'),
1607 t->height, t->object == NULL?0:t->object->color,
1608 (void*)t->handlink, (void*)t->balllink);
1611 (void) fprintf(stderr, "%p O %c%c%c%d %d %2d %6lu %6lu\n",
1614 (t->action == THROW)?'T':(t->action == CATCH?'C':'N'),
1615 t->height, t->type, t->object == NULL?0:t->object->color,
1616 t->start, t->finish);
1619 (void) fprintf(stderr, "%p B %c %2d %6lu %6lu %g\n",
1620 (void*)t, t->type == Ball?'b':t->type == Empty?'e':'f',
1621 t->object == NULL?0:t->object->color,
1622 t->start, t->finish, t->yp.c);
1625 (void) fprintf(stderr, "%p P %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: status %d not implemented\n",
1632 (void*)t, t->status);
1636 (void) fprintf(stderr, "---\n");
1640 static int get_num_balls(const char *j)
1646 for (p = j; *p; p++) {
1647 if (*p >= '0' && *p <='9') { /* digit */
1648 h = 10*h + (*p - '0');
1660 compare_num_balls(const void *p1, const void *p2)
1663 i = get_num_balls(((patternstruct*)p1)->pattern);
1664 j = get_num_balls(((patternstruct*)p2)->pattern);
1675 /**************************************************************************
1676 * Rendering Functions *
1678 **************************************************************************/
1681 show_arms(ModeInfo * mi)
1684 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
1687 XPoint a[countof(sp->arm[0][0])];
1693 glFrontFace(GL_CCW);
1696 for(side = LEFT; side <= RIGHT; side = (Hand)((int)side + 1)) {
1697 /* Translate into device coords */
1698 for(i = 0; i < countof(a); i++) {
1699 a[i].x = (short)(SCENE_WIDTH/2 + sp->arm[j][side][i].x*sp->scale);
1700 a[i].y = (short)(SCENE_HEIGHT - sp->arm[j][side][i].y*sp->scale);
1702 sp->arm[0][side][i] = sp->arm[1][side][i];
1705 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1708 polys += tube (a[2].x - (side == LEFT ? soffx : -soffx), a[2].y + soffy, 0,
1709 a[1].x, a[1].y, ARMLENGTH/2,
1710 thickness, 0, slices,
1711 True, True, MI_IS_WIREFRAME(mi));
1714 polys += tube (a[1].x, a[1].y, ARMLENGTH/2,
1715 a[0].x, a[0].y, ARMLENGTH,
1716 thickness * 0.8, 0, slices,
1717 True, True, MI_IS_WIREFRAME(mi));
1719 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
1723 glTranslatef (a[2].x - (side == LEFT ? soffx : -soffx),
1727 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1732 glTranslatef (a[1].x, a[1].y, ARMLENGTH/2);
1734 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1739 glTranslatef (a[0].x, a[0].y, ARMLENGTH);
1741 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1749 show_figure(ModeInfo * mi, Bool init)
1752 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
1773 /* #### most of this is unused now */
1774 static const XPoint figure[] = {
1775 { 15, 70}, /* 0 Left Hip */
1776 { 0, 90}, /* 1 Waist */
1777 { SX, 130}, /* 2 Left Shoulder */
1778 {-SX, 130}, /* 3 Right Shoulder */
1779 {-15, 70}, /* 4 Right Hip */
1780 { 0, 130}, /* 5 Neck */
1781 { 0, 140}, /* 6 Chin */
1782 { SX, 0}, /* 7 Left Foot */
1783 {-SX, 0}, /* 8 Right Foot */
1784 {-17, 174}, /* 9 Head1 */
1785 { 17, 140}, /* 10 Head2 */
1787 XPoint a[countof(figure)];
1788 GLfloat gcolor[4] = { 1, 1, 1, 1 };
1790 /* Translate into device coords */
1791 for(i = 0; i < countof(figure); i++) {
1792 a[i].x = (short)(SCENE_WIDTH/2 + (sp->cx + figure[i].x)*sp->scale);
1793 a[i].y = (short)(SCENE_HEIGHT - figure[i].y*sp->scale);
1796 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor);
1798 glFrontFace(GL_CCW);
1801 GLfloat scale = ((GLfloat) a[10].x - a[9].x) / 2;
1806 glTranslatef(a[6].x, a[6].y - scale, 0);
1807 glScalef(scale, scale, scale);
1810 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1813 glScalef(scale, scale, scale);
1814 glTranslatef(0, 0.3, 0);
1816 glTranslatef(0, 0, 0.35);
1817 polys += tube (0, 0, 0,
1820 slices, True, True, MI_IS_WIREFRAME(mi));
1822 glScalef(0.9, 0.9, 1);
1823 polys += unit_sphere(2*slices, 2*slices, MI_IS_WIREFRAME(mi));
1827 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
1828 glTranslatef(0, 1.1, 0);
1831 glScalef(scale, scale, scale);
1832 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1836 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1837 glTranslatef(0, 1.1, 0);
1839 glScalef(0.9, 1.0, 0.9);
1840 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1844 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
1845 glTranslatef(0, 1.0, 0);
1848 glScalef(scale, scale, scale);
1849 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1853 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1854 glTranslatef(0, 0.8, 0);
1857 glScalef(scale, scale, scale);
1858 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1863 glTranslatef(0, 0.7, 0);
1865 for (i = -1; i <= 1; i += 2) {
1868 glRotatef (i*10, 0, 0, 1);
1869 glTranslatef(-i*0.65, 0, 0);
1872 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
1874 glScalef(scale, scale, scale);
1875 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1878 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1880 glTranslatef(0, 0.6, 0);
1881 polys += tube (0, 0, 0,
1884 slices, True, True, MI_IS_WIREFRAME(mi));
1888 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
1890 glTranslatef(0, 4.4, 0);
1892 glScalef(scale, scale, scale);
1893 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1897 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1899 glTranslatef(0, 4.7, 0);
1900 polys += tube (0, 0, 0,
1903 slices, True, True, MI_IS_WIREFRAME(mi));
1907 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
1909 glTranslatef(0, 9.7, 0);
1911 glScalef(scale, scale, scale);
1912 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
1916 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
1918 glRotatef (-i*10, 0, 0, 1);
1919 glTranslatef(-i*1.75, 9.7, 0.9);
1921 glScalef (0.4, 1, 1);
1922 polys += tube (0, 0, 0,
1925 slices*4, True, True, MI_IS_WIREFRAME(mi));
1934 sp->arm[1][LEFT][SHOULDER].x = sp->cx + figure[2].x;
1935 sp->arm[1][RIGHT][SHOULDER].x = sp->cx + figure[3].x;
1937 /* Initialise arms */
1939 for(i = 0; i < 2; i++){
1940 sp->arm[i][LEFT][SHOULDER].y = figure[2].y;
1941 sp->arm[i][LEFT][ELBOW].x = figure[2].x;
1942 sp->arm[i][LEFT][ELBOW].y = figure[1].y;
1943 sp->arm[i][LEFT][HAND].x = figure[0].x;
1944 sp->arm[i][LEFT][HAND].y = figure[1].y;
1945 sp->arm[i][RIGHT][SHOULDER].y = figure[3].y;
1946 sp->arm[i][RIGHT][ELBOW].x = figure[3].x;
1947 sp->arm[i][RIGHT][ELBOW].y = figure[1].y;
1948 sp->arm[i][RIGHT][HAND].x = figure[4].x;
1949 sp->arm[i][RIGHT][HAND].y = figure[1].y;
1955 typedef struct { GLfloat x, y, z; } XYZ;
1957 /* lifted from sphere.c */
1959 striped_unit_sphere (int stacks, int slices,
1961 GLfloat *color1, GLfloat *color2,
1966 double theta1, theta2, theta3;
1968 XYZ la = { 0, 0, 0 }, lb = { 0, 0, 0 };
1969 XYZ c = {0, 0, 0}; /* center */
1970 double r = 1.0; /* radius */
1971 int stacks2 = stacks * 2;
1978 if (slices < 4 || stacks < 2 || r <= 0)
1980 glBegin (GL_POINTS);
1981 glVertex3f (c.x, c.y, c.z);
1988 for (j = 0; j < stacks; j++)
1990 theta1 = j * (M_PI+M_PI) / stacks2 - M_PI_2;
1991 theta2 = (j + 1) * (M_PI+M_PI) / stacks2 - M_PI_2;
1993 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
1994 ((j == 0 || j == stacks-1 ||
1995 j % (stacks / (stripes+1)))
1996 ? color1 : color2));
1998 glBegin (wire_p ? GL_LINE_LOOP : GL_TRIANGLE_STRIP);
1999 for (i = 0; i <= slices; i++)
2001 theta3 = i * (M_PI+M_PI) / slices;
2003 if (wire_p && i != 0)
2005 glVertex3f (lb.x, lb.y, lb.z);
2006 glVertex3f (la.x, la.y, la.z);
2009 e.x = cos (theta2) * cos(theta3);
2011 e.z = cos (theta2) * sin(theta3);
2012 p.x = c.x + r * e.x;
2013 p.y = c.y + r * e.y;
2014 p.z = c.z + r * e.z;
2016 glNormal3f (e.x, e.y, e.z);
2017 glTexCoord2f (i / (double)slices,
2018 2*(j+1) / (double)stacks2);
2019 glVertex3f (p.x, p.y, p.z);
2022 e.x = cos(theta1) * cos(theta3);
2024 e.z = cos(theta1) * sin(theta3);
2025 p.x = c.x + r * e.x;
2026 p.y = c.y + r * e.y;
2027 p.z = c.z + r * e.z;
2029 glNormal3f (e.x, e.y, e.z);
2030 glTexCoord2f (i / (double)slices,
2031 2*j / (double)stacks2);
2032 glVertex3f (p.x, p.y, p.z);
2044 show_ball(ModeInfo *mi, unsigned long color, Trace *s)
2047 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2048 /*int offset = (int)(s->angle*64*180/M_PI);*/
2049 short x = (short)(SCENE_WIDTH/2 + s->x * sp->scale);
2050 short y = (short)(SCENE_HEIGHT - s->y * sp->scale);
2051 GLfloat gcolor1[4] = { 0, 0, 0, 1 };
2052 GLfloat gcolor2[4] = { 0, 0, 0, 1 };
2055 /* Avoid wrapping */
2056 if(s->y*sp->scale > SCENE_HEIGHT * 2) return 0;
2058 gcolor1[0] = mi->colors[color].red / 65536.0;
2059 gcolor1[1] = mi->colors[color].green / 65536.0;
2060 gcolor1[2] = mi->colors[color].blue / 65536.0;
2062 gcolor2[0] = gcolor1[0] / 3;
2063 gcolor2[1] = gcolor1[1] / 3;
2064 gcolor2[2] = gcolor1[2] / 3;
2066 glFrontFace(GL_CCW);
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;
2123 glFrontFace(GL_CCW);
2126 GLfloat scale = radius;
2128 glTranslatef(x, y, 0);
2129 glScalef(scale, scale, scale);
2131 glTranslatef (0, 0, 2); /* put end of handle in hand */
2133 glRotatef (s->angle / M_PI*180, 1, 0, 0);
2136 glScalef (0.5, 1, 0.5);
2137 polys += striped_unit_sphere (slices, slices, divs, gcolor2, gcolor1,
2138 MI_IS_WIREFRAME(mi));
2140 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor2);
2141 polys += tube (0, 0, 0,
2144 slices, True, True, MI_IS_WIREFRAME(mi));
2146 glTranslatef (0, 2, 0);
2147 glScalef (0.25, 0.25, 0.25);
2148 polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
2157 show_torch(ModeInfo *mi, unsigned long color, Trace *s)
2161 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2162 XPoint head, tail, last;
2163 DXPoint dhead, dlast;
2164 const double sa = sin(s->angle);
2165 const double ca = cos(s->angle);
2167 const double TailLen = -24;
2168 const double HeadLen = 16;
2169 const short Width = (short)(5 * sqrt(sp->scale));
2181 dhead.x = s->x + HeadLen * PERSPEC * sa;
2182 dhead.y = s->y - HeadLen * ca;
2184 if(color == MI_BLACK_PIXEL(mi)) { /* Use 'last' when erasing */
2186 } else { /* Store 'last' so we can use it later when s->prev has
2188 if(s->prev != s->next) {
2189 dlast.x = s->prev->x + HeadLen * PERSPEC * sin(s->prev->angle);
2190 dlast.y = s->prev->y - HeadLen * cos(s->prev->angle);
2197 /* Avoid wrapping (after last is stored) */
2198 if(s->y*sp->scale > SCENE_HEIGHT * 2) return 0;
2200 head.x = (short)(SCENE_WIDTH/2 + dhead.x*sp->scale);
2201 head.y = (short)(SCENE_HEIGHT - dhead.y*sp->scale);
2203 last.x = (short)(SCENE_WIDTH/2 + dlast.x*sp->scale);
2204 last.y = (short)(SCENE_HEIGHT - dlast.y*sp->scale);
2206 tail.x = (short)(SCENE_WIDTH/2 +
2207 (s->x + TailLen * PERSPEC * sa)*sp->scale );
2208 tail.y = (short)(SCENE_HEIGHT - (s->y - TailLen * ca)*sp->scale );
2210 if(color != MI_BLACK_PIXEL(mi)) {
2211 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
2212 XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi),
2213 Width, LineSolid, CapRound, JoinRound);
2214 draw_line(mi, head.x, head.y, tail.x, tail.y);
2216 XSetForeground(MI_DISPLAY(mi), MI_GC(mi), color);
2217 XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi),
2218 Width * 2, LineSolid, CapRound, JoinRound);
2220 draw_line(mi, head.x, head.y, last.x, last.y);
2228 show_knife(ModeInfo *mi, unsigned long color, Trace *s)
2231 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2232 /*int offset = (int)(s->angle*64*180/M_PI);*/
2233 short x = (short)(SCENE_WIDTH/2 + s->x * sp->scale);
2234 short y = (short)(SCENE_HEIGHT - s->y * sp->scale);
2235 GLfloat gcolor1[4] = { 0, 0, 0, 1 };
2236 GLfloat gcolor2[4] = { 1, 1, 1, 1 };
2239 /* Avoid wrapping */
2240 if(s->y*sp->scale > SCENE_HEIGHT * 2) return 0;
2242 gcolor1[0] = mi->colors[color].red / 65536.0;
2243 gcolor1[1] = mi->colors[color].green / 65536.0;
2244 gcolor1[2] = mi->colors[color].blue / 65536.0;
2246 glFrontFace(GL_CCW);
2249 glTranslatef(x, y, 0);
2252 glTranslatef (0, 0, 2); /* put end of handle in hand */
2253 glRotatef (s->angle / M_PI*180, 1, 0, 0);
2255 glScalef (0.3, 1, 1); /* flatten blade */
2257 glTranslatef(0, 6, 0);
2258 glRotatef (180, 1, 0, 0);
2260 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor1);
2261 polys += tube (0, 0, 0,
2264 slices, True, True, MI_IS_WIREFRAME(mi));
2266 glTranslatef (0, 12, 0);
2267 glScalef (0.7, 10, 0.7);
2268 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor2);
2269 polys += unit_sphere (slices, slices, MI_IS_WIREFRAME(mi));
2277 show_ring(ModeInfo *mi, unsigned long color, Trace *s)
2280 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2281 /*int offset = (int)(s->angle*64*180/M_PI);*/
2282 short x = (short)(SCENE_WIDTH/2 + s->x * sp->scale);
2283 short y = (short)(SCENE_HEIGHT - s->y * sp->scale);
2284 double radius = 12 * sp->scale;
2285 GLfloat gcolor1[4] = { 0, 0, 0, 1 };
2286 GLfloat gcolor2[4] = { 0, 0, 0, 1 };
2289 int wire_p = MI_IS_WIREFRAME(mi);
2290 GLfloat width = M_PI * 2 / slices;
2293 GLfloat thickness = 0.15;
2295 /* Avoid wrapping */
2296 if(s->y*sp->scale > SCENE_HEIGHT * 2) return 0;
2298 gcolor1[0] = mi->colors[color].red / 65536.0;
2299 gcolor1[1] = mi->colors[color].green / 65536.0;
2300 gcolor1[2] = mi->colors[color].blue / 65536.0;
2302 gcolor2[0] = gcolor1[0] / 3;
2303 gcolor2[1] = gcolor1[1] / 3;
2304 gcolor2[2] = gcolor1[2] / 3;
2306 glFrontFace(GL_CCW);
2309 glTranslatef(0, 0, 12); /* back of ring in hand */
2311 glTranslatef(x, y, 0);
2312 glScalef(radius, radius, radius);
2314 glRotatef (90, 0, 1, 0);
2315 glRotatef (s->angle / M_PI*180, 0, 0, 1);
2317 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor1);
2320 for (j = -1; j <= 1; j += 2)
2322 GLfloat z = j * thickness/2;
2323 glFrontFace (j < 0 ? GL_CCW : GL_CW);
2324 glNormal3f (0, 0, j*1);
2325 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
2326 for (i = 0; i < slices + (wire_p ? 0 : 1); i++) {
2327 GLfloat th, cth, sth;
2328 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
2329 (i % (slices/3) ? gcolor1 : gcolor2));
2333 glVertex3f (cth * ra, sth * ra, z);
2334 glVertex3f (cth * rb, sth * rb, z);
2341 glFrontFace (GL_CCW);
2342 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
2343 for (i = 0; i < slices + (wire_p ? 0 : 1); i++)
2345 GLfloat th = i * width;
2346 GLfloat cth = cos(th);
2347 GLfloat sth = sin(th);
2348 glNormal3f (cth, sth, 0);
2349 glVertex3f (cth * ra, sth * ra, thickness/2);
2350 glVertex3f (cth * ra, sth * ra, -thickness/2);
2356 glFrontFace (GL_CW);
2357 glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
2358 for (i = 0; i < slices + (wire_p ? 0 : 1); i++)
2360 GLfloat th = i * width;
2361 GLfloat cth = cos(th);
2362 GLfloat sth = sin(th);
2363 glNormal3f (-cth, -sth, 0);
2364 glVertex3f (cth * rb, sth * ra, thickness/2);
2365 glVertex3f (cth * rb, sth * ra, -thickness/2);
2370 glFrontFace (GL_CCW);
2377 show_bball(ModeInfo *mi, unsigned long color, Trace *s)
2380 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2381 /*int offset = (int)(s->angle*64*180/M_PI);*/
2382 short x = (short)(SCENE_WIDTH/2 + s->x * sp->scale);
2383 short y = (short)(SCENE_HEIGHT - s->y * sp->scale);
2384 double radius = 12 * sp->scale;
2385 GLfloat gcolor1[4] = { 0, 0, 0, 1 };
2386 GLfloat gcolor2[4] = { 0, 0, 0, 1 };
2390 /* Avoid wrapping */
2391 if(s->y*sp->scale > SCENE_HEIGHT * 2) return 0;
2393 gcolor1[0] = mi->colors[color].red / 65536.0;
2394 gcolor1[1] = mi->colors[color].green / 65536.0;
2395 gcolor1[2] = mi->colors[color].blue / 65536.0;
2397 glFrontFace(GL_CCW);
2400 GLfloat scale = radius;
2403 glTranslatef(0, -6, 5); /* position on top of hand */
2405 glTranslatef(x, y, 0);
2406 glScalef(scale, scale, scale);
2407 glRotatef (s->angle / M_PI*180, 1, 0, 1);
2409 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor1);
2410 polys += unit_sphere (slices, slices, MI_IS_WIREFRAME(mi));
2412 glRotatef (90, 0, 0, 1);
2413 glTranslatef (0, 0, 0.81);
2414 glScalef(0.15, 0.15, 0.15);
2415 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor2);
2416 for (i = 0; i < 3; i++) {
2418 glTranslatef (0, 0, 1);
2419 glRotatef (360 * i / 3, 0, 0, 1);
2420 glTranslatef (2, 0, 0);
2421 glRotatef (18, 0, 1, 0);
2422 glBegin (MI_IS_WIREFRAME(mi) ? GL_LINE_LOOP : GL_TRIANGLE_FAN);
2423 glVertex3f (0, 0, 0);
2424 for (j = slices; j >= 0; j--) {
2425 GLfloat th = j * M_PI*2 / slices;
2426 glVertex3f (cos(th), sin(th), 0);
2439 /**************************************************************************
2440 * Public Functions *
2442 **************************************************************************/
2446 release_juggle (ModeInfo * mi)
2448 if (juggles != NULL) {
2451 for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
2452 free_juggle(&juggles[screen]);
2454 juggles = (jugglestruct *) NULL;
2458 /* FIXME: refill_juggle currently just appends new throws to the
2459 * programme. This is fine if the programme is empty, but if there
2460 * are still some trajectories left then it really should take these
2464 refill_juggle(ModeInfo * mi)
2466 jugglestruct *sp = NULL;
2469 if (juggles == NULL)
2471 sp = &juggles[MI_SCREEN(mi)];
2473 /* generate pattern */
2475 if (pattern == NULL) {
2478 #define MAXREPEAT 300
2479 #define CHANGE_BIAS 8 /* larger makes num_ball changes less likely */
2480 #define POSITION_BIAS 20 /* larger makes hand movements less likely */
2483 while (count < MI_CYCLES(mi)) {
2484 char buf[MAXPAT * 3 + 3], *b = buf;
2486 int l = NRAND(MAXPAT) + 1;
2487 int t = NRAND(MIN(MAXREPEAT, (MI_CYCLES(mi) - count))) + 1;
2489 { /* vary number of balls */
2490 int new_balls = sp->num_balls;
2493 if (new_balls == 2) /* Do not juggle 2 that often */
2494 change = NRAND(2 + CHANGE_BIAS / 4);
2496 change = NRAND(2 + CHANGE_BIAS);
2507 if (new_balls < sp->patternindex.minballs) {
2510 if (new_balls > sp->patternindex.maxballs) {
2513 if (new_balls < sp->num_balls) {
2514 if (!program(mi, "[*]", NULL, 1)) /* lose ball */
2517 sp->num_balls = new_balls;
2521 if (NRAND(2) && sp->patternindex.index[sp->num_balls].number) {
2522 /* Pick from PortFolio */
2523 int p = sp->patternindex.index[sp->num_balls].start +
2524 NRAND(sp->patternindex.index[sp->num_balls].number);
2525 if (!program(mi, portfolio[p].pattern, portfolio[p].name, t))
2528 /* Invent a new pattern */
2530 for(i = 0; i < l; i++){
2532 do { /* Triangular Distribution => high values more likely */
2533 m = NRAND(sp->num_balls + 1);
2534 n = NRAND(sp->num_balls + 1);
2536 if (n == sp->num_balls) {
2539 switch(NRAND(5 + POSITION_BIAS)){
2540 case 0: /* Outside throw */
2542 case 1: /* Cross throw */
2544 case 2: /* Cross catch */
2546 case 3: /* Cross throw and catch */
2548 case 4: /* Bounce */
2551 break; /* Inside throw (default) */
2560 if (!program(mi, buf, NULL, t))
2565 } else { /* pattern supplied in height or 'a' notation */
2566 if (!program(mi, pattern, NULL, MI_CYCLES(mi)))
2583 if (!projectile(sp)) {
2590 if(MI_IS_DEBUG(mi)) dump(sp);
2595 change_juggle(ModeInfo * mi)
2597 jugglestruct *sp = NULL;
2600 if (juggles == NULL)
2602 sp = &juggles[MI_SCREEN(mi)];
2604 /* Strip pending trajectories */
2605 for (t = sp->head->next; t != sp->head; t = t->next) {
2606 if(t->start > sp->time || t->finish < sp->time) {
2609 trajectory_destroy(n);
2613 /* Pick the current object theme */
2614 sp->objtypes = choose_object();
2618 mi->polygon_count += show_figure(mi, True);
2623 reshape_juggle (ModeInfo *mi, int width, int height)
2625 GLfloat h = (GLfloat) height / (GLfloat) width;
2627 glViewport (0, 0, (GLint) width, (GLint) height);
2629 glMatrixMode(GL_PROJECTION);
2631 gluPerspective (30.0, 1/h, 1.0, 100.0);
2633 glMatrixMode(GL_MODELVIEW);
2635 gluLookAt( 0.0, 0.0, 30.0,
2639 glClear(GL_COLOR_BUFFER_BIT);
2644 init_juggle (ModeInfo * mi)
2646 jugglestruct *sp = 0;
2647 int wire = MI_IS_WIREFRAME(mi);
2650 juggles = (jugglestruct *)
2651 calloc (MI_NUM_SCREENS(mi), sizeof (jugglestruct));
2653 fprintf(stderr, "%s: out of memory\n", progname);
2658 sp = &juggles[MI_SCREEN(mi)];
2660 if (!sp->glx_context) /* re-initting breaks print_texture_label */
2661 sp->glx_context = init_GL(mi);
2663 sp->font_data = load_texture_font (mi->dpy, "titleFont");
2665 reshape_juggle (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
2666 clear_gl_error(); /* WTF? sometimes "invalid op" from glViewport! */
2670 GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
2671 GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
2672 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
2673 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
2675 glEnable(GL_LIGHTING);
2676 glEnable(GL_LIGHT0);
2677 glEnable(GL_DEPTH_TEST);
2678 glEnable(GL_CULL_FACE);
2680 glLightfv(GL_LIGHT0, GL_POSITION, pos);
2681 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
2682 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
2683 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
2686 make_random_colormap (0, 0, 0,
2687 mi->colors, &MI_NPIXELS(mi),
2688 True, False, 0, False);
2691 double spin_speed = 0.05;
2692 double wander_speed = 0.001;
2693 double spin_accel = 0.05;
2694 sp->rot = make_rotator (0, spin_speed, 0,
2695 spin_accel, wander_speed, False);
2696 sp->trackball = gltrackball_init (False);
2699 if (only && *only && strcmp(only, " ")) {
2700 balls = clubs = torches = knives = rings = bballs = False;
2701 if (!strcasecmp (only, "balls")) balls = True;
2702 else if (!strcasecmp (only, "clubs")) clubs = True;
2703 else if (!strcasecmp (only, "torches")) torches = True;
2704 else if (!strcasecmp (only, "knives")) knives = True;
2705 else if (!strcasecmp (only, "rings")) rings = True;
2706 else if (!strcasecmp (only, "bballs")) bballs = True;
2708 (void) fprintf (stderr,
2709 "Juggle: -only must be one of: balls, clubs, torches, knives,\n"
2710 "\t rings, or bballs (not \"%s\")\n", only);
2711 #ifdef STANDALONE /* xlock mustn't exit merely because of a bad argument */
2717 /* #### hard to make this look good in OpenGL... */
2721 if (sp->head == 0) { /* first time initializing this juggler */
2723 sp->count = ABS(MI_COUNT(mi));
2727 /* record start time */
2728 sp->begintime = time(NULL);
2729 if(sp->patternindex.maxballs > 0) {
2730 sp->num_balls = sp->patternindex.minballs +
2731 NRAND(sp->patternindex.maxballs - sp->patternindex.minballs);
2734 mi->polygon_count +=
2735 show_figure(mi, True); /* Draw figure. Also discovers
2736 information about the juggler's
2739 /* "7" should be about three times the height of the juggler's
2741 sp->Gr = -GRAVITY(3 * sp->arm[0][RIGHT][SHOULDER].y,
2742 7 * THROW_CATCH_INTERVAL);
2744 if(!balls && !clubs && !torches && !knives && !rings && !bballs)
2745 balls = True; /* Have to juggle something! */
2747 /* create circular trajectory list */
2748 ADD_ELEMENT(Trajectory, sp->head, sp->head);
2749 if(sp->head == NULL){
2754 /* create circular object list */
2755 ADD_ELEMENT(Object, sp->objects, sp->objects);
2756 if(sp->objects == NULL){
2761 sp->pattern = strdup(""); /* Initialise saved pattern with
2765 sp = &juggles[MI_SCREEN(mi)];
2769 !strcasecmp (pattern, ".") ||
2770 !strcasecmp (pattern, "random")))
2773 if (pattern == NULL && sp->patternindex.maxballs == 0) {
2774 /* pattern list needs indexing */
2775 int nelements = countof(portfolio);
2779 /* sort according to number of balls */
2780 qsort((void*)portfolio, nelements,
2781 sizeof(portfolio[1]), compare_num_balls);
2783 /* last pattern has most balls */
2784 sp->patternindex.maxballs = get_num_balls(portfolio[nelements - 1].pattern);
2785 /* run through sorted list, indexing start of each group
2786 and number in group */
2787 sp->patternindex.maxballs = 1;
2788 for (i = 0; i < nelements; i++) {
2789 int b = get_num_balls(portfolio[i].pattern);
2790 if (b > sp->patternindex.maxballs) {
2791 sp->patternindex.index[sp->patternindex.maxballs].number = numpat;
2792 if(numpat == 0) sp->patternindex.minballs = b;
2793 sp->patternindex.maxballs = b;
2795 sp->patternindex.index[sp->patternindex.maxballs].start = i;
2800 sp->patternindex.index[sp->patternindex.maxballs].number = numpat;
2803 /* Set up programme */
2806 /* Only put things here that won't interrupt the programme during
2809 /* Use MIN so that users can resize in interesting ways, eg
2810 narrow windows for tall patterns, etc */
2811 sp->scale = MIN(SCENE_HEIGHT/480.0, SCENE_WIDTH/160.0);
2816 juggle_handle_event (ModeInfo *mi, XEvent *event)
2818 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2820 if (gltrackball_event_handler (event, sp->trackball,
2821 MI_WIDTH (mi), MI_HEIGHT (mi),
2822 &sp->button_down_p))
2824 else if (screenhack_event_helper (MI_DISPLAY(mi), MI_WINDOW(mi), event))
2835 draw_juggle (ModeInfo *mi)
2837 jugglestruct *sp = &juggles[MI_SCREEN(mi)];
2838 Display *dpy = MI_DISPLAY(mi);
2839 Window window = MI_WINDOW(mi);
2841 Trajectory *traj = NULL;
2843 unsigned long future = 0;
2844 char *pattern = NULL;
2846 if (!sp->glx_context)
2849 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sp->glx_context));
2851 glShadeModel(GL_SMOOTH);
2853 glEnable(GL_DEPTH_TEST);
2854 glEnable(GL_NORMALIZE);
2855 glEnable(GL_CULL_FACE);
2857 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
2860 glRotatef(current_device_rotation(), 0, 0, 1);
2862 glTranslatef(0,-3,0);
2866 get_position (sp->rot, &x, &y, &z, !sp->button_down_p);
2867 glTranslatef((x - 0.5) * 8,
2871 gltrackball_rotate (sp->trackball);
2873 get_rotation (sp->rot, &x, &y, &z, !sp->button_down_p);
2875 if (y < 0.8) y = 0.8 - (y - 0.8); /* always face forward */
2876 if (y > 1.2) y = 1.2 - (y - 1.2);
2878 glRotatef (x * 360, 1.0, 0.0, 0.0);
2879 glRotatef (y * 360, 0.0, 1.0, 0.0);
2880 glRotatef (z * 360, 0.0, 0.0, 1.0);
2884 GLfloat scale = 20.0 / SCENE_HEIGHT;
2885 glScalef(scale, scale, scale);
2888 glRotatef (180, 0, 0, 1);
2889 glTranslatef(-SCENE_WIDTH/2, -SCENE_HEIGHT/2, 0);
2890 glTranslatef(0, -150, 0);
2892 mi->polygon_count = 0;
2897 (void)gettimeofday(&tv, NULL);
2898 sp->time = (int) ((tv.tv_sec - sp->begintime)*1000 + tv.tv_usec/1000);
2900 sp->time += MI_DELAY(mi) / 1000;
2903 /* First pass: Move arms and strip out expired elements */
2904 for (traj = sp->head->next; traj != sp->head; traj = traj->next) {
2905 if (traj->status != PREDICTOR) {
2906 /* Skip any elements that need further processing */
2907 /* We could remove them, but there shoudn't be many and they
2908 would be needed if we ever got the pattern refiller
2912 if (traj->start > future) { /* Lookahead to the end of the show */
2913 future = traj->start;
2915 if (sp->time < traj->start) { /* early */
2917 } else if (sp->time < traj->finish) { /* working */
2919 /* Look for pattern name */
2920 if(traj->pattern != NULL) {
2921 pattern=traj->pattern;
2924 if (traj->type == Empty || traj->type == Full) {
2925 /* Only interested in hands on this pass */
2926 /* double angle = traj->angle + traj->spin * (sp->time - traj->start);*/
2927 double xd = 0, yd = 0;
2930 /* Find the catching offset */
2931 if(traj->object != NULL) {
2933 /* #### not sure what this is doing, but I'm guessing
2934 that the use of PERSPEC means this isn't needed
2935 in the OpenGL version? -jwz
2937 if(ObjectDefs[traj->object->type].handle > 0) {
2938 /* Handles Need to be oriented */
2939 xd = ObjectDefs[traj->object->type].handle *
2940 PERSPEC * sin(angle);
2941 yd = ObjectDefs[traj->object->type].handle *
2946 /* Balls are always caught at the bottom */
2951 p.x = (CUBIC(traj->xp, sp->time) - xd);
2952 p.y = (CUBIC(traj->yp, sp->time) + yd);
2953 reach_arm(mi, traj->hand, &p);
2955 /* Store updated hand position */
2959 if (traj->type == Ball || traj->type == Full) {
2960 /* Only interested in objects on this pass */
2964 if(traj->type == Full) {
2965 /* Adjusted these in the first pass */
2969 x = CUBIC(traj->xp, sp->time);
2970 y = CUBIC(traj->yp, sp->time);
2973 ADD_ELEMENT(Trace, s, traj->object->trace->prev);
2976 s->angle = traj->angle + traj->spin * (sp->time - traj->start);
2977 s->divisions = traj->divisions;
2978 traj->object->tracelen++;
2979 traj->object->active = True;
2981 } else { /* expired */
2982 Trajectory *n = traj;
2984 trajectory_destroy(n);
2989 mi->polygon_count += show_figure(mi, False);
2990 mi->polygon_count += show_arms(mi);
2993 glTranslatef(0, 0, ARMLENGTH);
2994 for (o = sp->objects->next; o != sp->objects; o = o->next) {
2996 mi->polygon_count += ObjectDefs[o->type].draw(mi, o->color,
3003 /* Save pattern name so we can erase it when it changes */
3004 if(pattern != NULL && strcmp(sp->pattern, pattern) != 0 ) {
3006 sp->pattern = strdup(pattern);
3008 if (MI_IS_VERBOSE(mi)) {
3009 (void) fprintf(stderr, "Juggle[%d]: Running: %s\n",
3010 MI_SCREEN(mi), sp->pattern);
3014 glColor3f (1, 1, 0);
3015 print_texture_label (mi->dpy, sp->font_data,
3016 mi->xgwa.width, mi->xgwa.height,
3020 if((int)(sp->time/10) % 1000 == 0)
3021 (void) fprintf(stderr, "sbrk: %d\n", (int)sbrk(0));
3024 if (future < sp->time + 100 * THROW_CATCH_INTERVAL) {
3026 } else if (sp->time > 1<<30) { /* Hard Reset before the clock wraps */
3033 if (mi->fps_p) do_fps (mi);
3036 glXSwapBuffers(dpy, window);
3039 XSCREENSAVER_MODULE_2 ("Juggler3D", juggler3d, juggle)