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