source: trunk/third/xscreensaver/hacks/juggle.c @ 20148

Revision 20148, 39.2 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r20147, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C; tab-width: 4 -*- */
2/* juggle */
3
4#if 0
5static const char sccsid[] = "@(#)juggle.c      5.00 2000/11/01 xlockmore";
6#endif
7
8/*-
9 * Copyright (c) 1996 by Tim Auckland <Tim.Auckland@Procket.com>
10 *
11 * Permission to use, copy, modify, and distribute this software and its
12 * documentation for any purpose and without fee is hereby granted,
13 * provided that the above copyright notice appear in all copies and that
14 * both that copyright notice and this permission notice appear in
15 * supporting documentation.
16 *
17 * This file is provided AS IS with no warranties of any kind.  The author
18 * shall have no liability with respect to the infringement of copyrights,
19 * trade secrets or any patents by this file or any part thereof.  In no
20 * event will the author be liable for any lost revenue or profits or
21 * other special, indirect and consequential damages.
22 *
23 * Revision History
24 * 01-Nov-2000: Allocation checks
25 * 1996: Written
26 */
27
28/*-
29 * TODO
30 * Fix timing to run at approx same speed on all machines.
31 * Store shorter pattern and refill when required.
32 * Use -cycles and -count in a rational manner.
33 * Merge pattern selector with pattern generator.
34 * Add clubs
35 * Clap when all the balls are in the air
36 */
37
38
39/*-
40Notes on Adam Chalcraft Juggling Notation (used by permission)
41a-> Adam's notation  s-> Site swap (Cambridge) notation
42
43To define a map from a-notation to s-notation
44("site-swap"), both of which look like doubly infinite sequences of natural
45numbers. In s-notation, there is a restriction on what is allowed, namely
46for the sequence s_n, the associated function f(n)=n+s_n must be a
47bijection. In a-notation, there is no restriction.
48
49To go from a-notation to s-notation, you start by mapping each a_n to a
50permutation of N, the natural numbers.
51
520 -> the identity
531 -> (10) [i.e. f(1)=0, f(0)=1]
542 -> (210) [i.e. f(2)=1, f(1)=0, f(0)=2]
553 -> (3210) [i.e. f(3)=2, f(2)=1, f(1)=0, f(0)=3]
56etc.
57
58Then for each n, you look at how long 0 takes to get back to 0 again and
59you call this t_n. If a_n=0, for example, then since the identity leaves 0
60alone, it gets back to 0 in 1 step, so t_n=1. If a_n=1, then f(0)=1. Now any
61further a_n=0 leave 1 alone, but the next a_n>0 sends 1 back to 0. Hence t_n
62is 2 + the number of 0's following the 1. Finally, set s_n = t_n - 1.
63
64To give some examples, it helps to have a notation for cyclic sequences. By
65(123), for example, I mean ...123123123123... . Now under the a-notation ->
66s-notation mapping we have some familiar examples:
67
68(0)->(0), (1)->(1), (2)->(2) etc.
69(21)->(31), (31)->(51), (41)->(71) etc.
70(10)->(20), (20)->(40), (30)->(60) etc.
71(331)->(441), (312)->(612), (303)->(504), (321)->(531)
72(43)->(53), (434)->(534), (433)->(633)
73(552)->(672)
74
75In general, the number of balls is the *average* of the s-notation, and the
76*maximum* of the a-notation. Another theorem is that the minimum values in
77the a-notation and the s-notation and equal, and preserved in the same
78positions.
79
80The usefulness of a-notation is the fact that there are no restrictions on
81what is allowed. This makes random juggle generation much easier. It also
82makes enumeration very easy. Another handy feature is computing changes.
83Suppose you can do (5) and want a neat change up to (771) in s-notation
84[Mike Day actually needed this example!]. Write them both in a-notation,
85which gives (5) and (551). Now concatenate them (in general, there may be
86more than one way to do this, but not in this example), to get
87...55555555551551551551551...
88Now convert back to s-notation, to get
89...55555566771771771771771...
90So the answer is to do two 6 throws and then go straight into (771).
91Coming back down of course,
92...5515515515515515555555555...
93converts to
94...7717717717716615555555555...
95so the answer is to do a single 661 and then drop straight down to (5).
96
97[The number of balls in the generated pattern occasionally changes.  In
98 order to decrease the number of balls I had to introduce a new symbol
99 into the Adam notation, [*] which means 'lose the current ball'.]
100*/
101
102#ifdef STANDALONE
103#define MODE_juggle
104#define PROGCLASS "Juggle"
105#define HACK_INIT init_juggle
106#define HACK_DRAW draw_juggle
107#define juggle_opts xlockmore_opts
108#define DEFAULTS "*delay: 10000 \n" \
109"*count: 150 \n" \
110"*cycles: 30 \n" \
111"*ncolors: 32 \n"
112#define SMOOTH_COLORS
113#include "xlockmore.h"          /* in xscreensaver distribution */
114#else /* STANDALONE */
115#include "xlock.h"              /* in xlockmore distribution */
116#endif /* STANDALONE */
117
118#ifdef MODE_juggle
119
120#define DEF_PATTERN "." /* All patterns */
121#define DEF_TRAIL "0" /* No trace */
122#ifdef UNI
123#define DEF_UNI "FALSE" /* No unicycle */ /* Not implemented yet */
124#endif
125#define DEF_SOLID "FALSE" /* Not solid */
126
127static char *pattern;
128static int trail;
129#ifdef UNI
130static Bool uni;
131#endif
132static Bool solid;
133
134static XrmOptionDescRec opts[] =
135{
136  {(char* ) "-pattern", (char *) ".juggle.pattern",
137   XrmoptionSepArg, (caddr_t) NULL},
138  {(char* ) "-trail", (char *) ".juggle.trail",
139   XrmoptionSepArg, (caddr_t) NULL},
140#ifdef UNI
141  {(char *) "-uni", (char *) ".juggle.uni", XrmoptionNoArg, (caddr_t) "on"},
142  {(char *) "+uni", (char *) ".juggle.uni", XrmoptionNoArg, (caddr_t) "off"},
143#endif
144  {(char *) "-solid", (char *) ".juggle.solid", XrmoptionNoArg, (caddr_t) "on"},
145  {(char *) "+solid", (char *) ".juggle.solid", XrmoptionNoArg, (caddr_t) "off"}
146};
147static argtype vars[] =
148{
149  {(caddr_t *) &pattern, (char *) "pattern",
150   (char *) "Pattern", (char *) DEF_PATTERN, t_String},
151  {(caddr_t *) &trail, (char *) "trail",
152   (char *) "Trail", (char *) DEF_TRAIL, t_Int},
153#ifdef UNI
154  {(caddr_t *) &uni, (char *) "uni",
155   (char *) "Uni", (char *) DEF_UNI, t_Bool},
156#endif
157  {(caddr_t *) &solid, (char *) "solid",
158   (char *) "Solid", (char *) DEF_SOLID, t_Bool}
159};
160static OptionStruct desc[] =
161{
162  {(char *) "-pattern string", (char *) "Cambridge Juggling Pattern"},
163  {(char *) "-trail num", (char *) "Trace Juggling Patterns"},
164#ifdef UNI
165  {(char *) "-/+uni", (char *) "Unicycle"},
166#endif
167  {(char *) "-/+solid", (char *) "solid color (else its a 4 panel look (half white))"}
168};
169
170ModeSpecOpt juggle_opts =
171{sizeof opts / sizeof opts[0], opts,
172 sizeof vars / sizeof vars[0], vars, desc};
173
174#ifdef USE_MODULES
175ModStruct   juggle_description = {
176        "juggle", "init_juggle", "draw_juggle", "release_juggle",
177        "draw_juggle", "init_juggle", (char *) NULL, &juggle_opts,
178        10000, 150, 30, 1, 64, 1.0, "",
179        "Shows a Juggler, juggling", 0, NULL
180};
181
182#endif
183
184#ifdef USE_XVMSUTILS
185#include <X11/unix_time.h>
186#endif
187#include <time.h>
188#if HAVE_SYS_TIME_H
189#include <sys/time.h>
190#else
191#if HAVE_SYS_SELECT_H
192#include <sys/select.h>
193#endif
194#endif
195
196/* Figure */
197#define ARMLENGTH ((int) (40.0 * sp->scale))
198#define ARMWIDTH ((int) (8.0 * sqrt(sp->scale)))
199#define POSE ((int) (10.0 * sp->scale))
200#define SX ((int) (25.0 * sp->scale))
201#define SZ ((int) (25.0 * sp->scale))
202#define SY ((int) (25.0 * sp->scale))
203#define HIPY ((int) (85.0 * sp->scale))
204#define RHIPX ((int) (-15.0 * sp->scale))
205#define LHIPX ((int) (15.0 * sp->scale))
206#define RFX ((int) (-25.0 * sp->scale))
207#define LFX ((int) (25.0 * sp->scale))
208#define FY ((int) (155.0 * sp->scale))
209#define WSTY ((int) (65.0 * sp->scale))
210#define NEY ((int) (15.0 * sp->scale))
211#define HED ((int) (35.0 * sp->scale))
212#define BALLRADIUS ARMWIDTH
213#define FIGURE1 7
214#define FIGURE2 3
215#define TRACE_LENGTH 50
216#define SPIN_DEGREES 750  /* Average spinning between a throw and the next catch */
217
218/* macros */
219
220#ifndef XtNumber
221#define XtNumber(arr)   ((unsigned int) (sizeof(arr) / sizeof(arr[0])))
222#endif
223
224#define GRAVITY(h, t) 4*(double)(h)/((t)*(t))
225
226#define THROW_CATCH_INTERVAL (sp->count)
227#define THROW_NULL_INTERVAL  (sp->count * 0.5)
228#define CATCH_THROW_INTERVAL (sp->count * 0.2)
229#define COR 0.8  /* coeff of restitution of balls (1 = perfect bounce) */
230
231
232/* typedefs */
233
234typedef enum {HEIGHT, ADAM} Notation;
235typedef enum {Empty, Full, Ball} Throwable;
236typedef enum {LEFT, RIGHT} Hand;
237typedef enum {THROW, CATCH} Action; /* DROP is not an option */
238typedef enum {ATCH, THRATCH, ACTION, LINKEDACTION, PTHRATCH, BPREDICTOR,
239        PREDICTOR} TrajectoryStatus;
240
241typedef struct trajectory *TrajectoryPtr;
242
243typedef struct {double a, b, c, d; } Spline;
244
245typedef struct trajectory {
246  TrajectoryPtr prev, next;  /* for building list */
247  TrajectoryStatus status;
248
249  /* Throw */
250  char posn;
251  int height;
252  int adam;
253
254  /* Action */
255  Hand hand;
256  Action action;
257
258  /* LinkedAction */
259  int color;
260  int spin, divisions;
261  double degree_offset;
262  TrajectoryPtr balllink;
263  TrajectoryPtr handlink;
264
265  /* PThratch */
266
267  double dx; /* initial velocity */
268  double dy;
269
270  /* Predictor */
271  Throwable type;
272  int start, finish;
273  Spline xp, yp;
274  int x, y; /* current position */
275} Trajectory;
276
277/* structs */
278
279typedef struct {
280  int         width;
281  int         height;
282  double      scale;
283  int         complexity;
284  int         cx;
285  int         cy;
286  double      Gr;
287  int         pattern;
288  Trajectory  *head;
289  XPoint   figure_path[FIGURE1];
290  XSegment figure_segs[FIGURE2];
291  XPoint      arm[2][3];
292  XPoint      *trace;
293  int         traceindex;
294  int         count;
295  time_t      begintime; /* seconds */
296  int         time; /* millisecond timer */
297  Bool        solid, uni;       
298} jugglestruct;
299
300static jugglestruct *juggles = (jugglestruct *) NULL;
301
302typedef struct {
303  char * pattern;
304  char * name;
305} patternstruct;
306
307#define MINBALLS 2
308#define MAXBALLS 7
309
310typedef struct {
311  int start;
312  int number;
313} PatternIndex;
314
315static PatternIndex* patternindex = (PatternIndex *) NULL;
316
317/* List of popular patterns, in any order */
318static patternstruct portfolio[] = {
319  {(char *) "[+2 1]", (char *) "+3 1, Typical 2 ball juggler"},
320  {(char *) "[2 0]", (char *) "4 0, 2 balls 1 hand"},
321  {(char *) "[2 0 1]", (char *) "5 0 1"},
322  {(char *) "[+2 0 +2 0 0]", (char *) "+5 0 +5 0 0"},
323  {(char *) "[3]", (char *) "3, cascade"},
324  {(char *) "[+3]", (char *) "+3, reverse cascade"},
325  {(char *) "[=3]", (char *) "=3, cascade under arm"},
326  {(char *) "[&3]", (char *) "&3, cascade catching under arm"},
327  {(char *) "[_3]", (char *) "_3, bouncing cascade"},
328  {(char *) "[+3 x3 =3]", (char *) "+3 x3 =3, Mill's mess"},
329  {(char *) "[3 2 1]", (char *) "5 3 1"},
330  {(char *) "[3 3 1]", (char *) "4 4 1"},
331  {(char *) "[3 1 2]", (char *) "6 1 2, See-saw"},
332  {(char *) "[=3 3 1 2]", (char *) "=4 5 1 2"},
333  {(char *) "[=3 2 2 3 1 2]", (char *) "=6 2 2 5 1 2, =4 5 1 2 stretched"},
334  {(char *) "[+3 3 1 3]", (char *) "+4 4 1 3, anemic shower box"},
335  {(char *) "[3 3 1]", (char *) "4 4 1"},
336  {(char *) "[+3 2 3]", (char *) "+4 2 3"},
337  {(char *) "[+3 1]", (char *) "+5 1, 3 shower"},
338  {(char *) "[_3 1]", (char *) "_5 1, bouncing 3 shower"},
339  {(char *) "[3 0 3 0 3]", (char *) "5 0 5 0 5, shake 3 out of 5"},
340  {(char *) "[3 3 3 0 0]", (char *) "5 5 5 0 0, flash 3 out of 5"},
341  {(char *) "[3 3 0]", (char *) "4 5 0, complete waste of a 5 ball juggler"},
342  {(char *) "[3 3 3 0 0 0 0]", (char *) "7 7 7 0 0 0 0, 3 flash"},
343  {(char *) "[+3 0 +3 0 +3 0 0]", (char *) "+7 0 +7 0 +7 0 0"},
344  {(char *) "[4]", (char *) "4, 4 cascade"},
345  {(char *) "[+4 3]", (char *) "+5 3, 4 ball half shower"},
346  {(char *) "[4 4 2]", (char *) "5 5 2"},
347  {(char *) "[+4 4 4 +4]", (char *) "+4 4 4 +4, 4 columns"},
348  {(char *) "[4 3 +4]", (char *) "5 3 +4"},
349  {(char *) "[+4 1]", (char *) "+7 1, 4 shower"},
350  {(char *) "[4 4 4 4 0]", (char *) "5 5 5 5 0, learning 5"},
351  {(char *) "[5]", (char *) "5, 5 cascade"},
352  {(char *) "[_5 _5 _5 _5 _5 5 5 5 5 5]", (char *) "_5 _5 _5 _5 _5 5 5 5 5 5, jump rope"},
353  {(char *) "[+5 x5 =5]", (char *) "+5 x5 =5, Mill's mess for 5"},
354  {(char *) "[6]", (char *) "6, 6 cascade"},
355  {(char *) "[7]", (char *) "7, 7 cascade"},
356  {(char *) "[_7]", (char *) "_7, bouncing 7 cascade"},
357};
358
359/* Private Functions */
360
361/* list management */
362
363/* t receives trajectory to be created.  ot must point to an existing
364   trajectory or be identical to t to start a new list. */
365#define INSERT_AFTER_TOP(t, ot)                                 \
366  if ((t = (Trajectory *)calloc(1, sizeof(Trajectory))) == NULL) \
367    {free_juggle(sp); return;}                                  \
368  (t)->next = (ot)->next;                                       \
369  (t)->prev = (ot);                                             \
370  (ot)->next = (t);                                             \
371  (t)->next->prev = (t)
372#define INSERT_AFTER(t, ot)                                     \
373  if ((t = (Trajectory *)calloc(1, sizeof(Trajectory))) == NULL) \
374    {free_juggle(sp); return False;}                            \
375  (t)->next = (ot)->next;                                       \
376  (t)->prev = (ot);                                             \
377  (ot)->next = (t);                                             \
378  (t)->next->prev = (t)
379
380
381/* t must point to an existing trajectory.  t must not be an
382   expression ending ->next or ->prev */
383#define REMOVE(t)                                               \
384  (t)->next->prev = (t)->prev;                                  \
385  (t)->prev->next = (t)->next;                                  \
386  (void) free((void *) t)
387
388static void
389free_pattern(jugglestruct *sp) {
390        if (sp->head != NULL) {
391                while (sp->head->next != sp->head) {
392                        Trajectory *t = sp->head->next;
393
394                        REMOVE(t); /* don't eliminate t */
395                }
396                (void) free((void *) sp->head);
397                sp->head = (Trajectory *) NULL;
398        }
399}
400
401static void
402free_juggle(jugglestruct *sp)
403{
404        if (sp->trace != NULL) {
405                (void) free((void *) sp->trace);
406                sp->trace = (XPoint *) NULL;
407        }
408        free_pattern(sp);
409}
410
411static Bool
412add_throw(jugglestruct *sp, char type, int h, Notation n)
413{
414  Trajectory *t;
415
416  INSERT_AFTER(t, sp->head->prev);
417  t->posn = type;
418  if (n == ADAM) {
419        t->adam = h;
420        t->status = ATCH;
421  } else {
422        t->height = h;
423        t->status = THRATCH;
424  }
425  return True;
426}
427
428/* add a Thratch to the performance */
429static Bool
430program(ModeInfo *mi, const char *patn, int repeat)
431{
432  jugglestruct *sp = &juggles[MI_SCREEN(mi)];
433  const char *p;
434  int h, i, seen;
435  Notation notation;
436  char type;
437
438  if (MI_IS_VERBOSE(mi)) {
439        (void) fprintf(stderr, "%s x %d\n", patn, repeat);
440  }
441
442  for(i=0; i < repeat; i++) {
443        type=' ';
444        h = 0;
445        seen = 0;
446        notation = HEIGHT;
447        for(p=patn; *p; p++) {
448                if (*p >= '0' && *p <='9') {
449                seen = 1;
450                h = 10*h + (*p - '0');
451          } else {
452                Notation nn = notation;
453                switch (*p) {
454                case '[':            /* begin Adam notation */
455                  notation = ADAM;
456                  break;
457                case '-':            /* Inside throw */
458                case '+':            /* Outside throw */
459                case '=':            /* Cross throw */
460                case '&':            /* Cross catch */
461                case 'x':            /* Cross throw and catch */
462                case '_':            /* Bounce */
463                  type = *p;
464                  break;
465                case '*':            /* Lose ball */
466                  seen = 1;
467                  h = -1;
468                  /* fall through */
469                case ']':             /* end Adam notation */
470                  nn = HEIGHT;
471                  /* fall through */
472                case ' ':
473                  if (seen) {
474                        if (!add_throw(sp, type, h, notation))
475                                return False;
476                        type=' ';
477                        h = 0;
478                        seen = 0;
479                  }
480                  notation = nn;
481                  break;
482                default:
483                  (void) fprintf(stderr, "Unexpected pattern instruction: '%s'\n", p);
484                  break;
485                }
486          }
487        }
488        if (seen) {
489          if (!add_throw(sp, type, h, notation))
490                return False;
491        }
492  }
493        return True;
494}
495
496/*
497 ~~~~\~~~~~\~~~
498 \\~\\~\~\\\~~~
499 \\~\\\\~\\\~\~
500 \\\\\\\\\\\~\\
501
502[33134231334021]
503
5044 4 1 3 12 2 4 1 4 4 13 0 3 1
505
506*/
507#define BOUNCEOVER 10
508
509static void
510adam(jugglestruct *sp)
511{
512  Trajectory *t, *p;
513  for(t = sp->head->next; t != sp->head; t = t->next) {
514        if (t->status == ATCH) {
515          int a = t->adam;
516          t->height = 0;
517          for(p = t->next; a > 0 && p != sp->head; p = p->next) {
518                if (p->status != ATCH || p->adam < 0 || p->adam>= a) {
519                  a--;
520                }
521                t->height++;
522          }
523          if(t->height > BOUNCEOVER && t->posn == ' '){
524                t->posn = '_'; /* high defaults can be bounced */
525          }
526          t->status = THRATCH;
527#if 0
528          (void) fprintf(stderr, "%d\t%d\n", t->adam, t->height);
529#endif
530        }
531  }
532}
533
534/* Split Thratch notation into explicit throws and catches.
535   Usually Catch follows Throw in same hand, but take care of special
536   cases. */
537
538/* ..n1.. -> .. LTn RT1 LC RC .. */
539/* ..nm.. -> .. LTn LC RTm RC .. */
540
541static Bool
542part(jugglestruct *sp)
543{
544  Trajectory *t, *nt, *p;
545  Hand hand = (LRAND() & 1) ? RIGHT : LEFT;
546
547  for (t = sp->head->next; t != sp->head; t = t->next) {
548        if (t->status > THRATCH) {
549          hand = t->hand;
550        } else if (t->status == THRATCH) {
551          char posn = '=';
552
553          /* plausibility check */
554          if (t->height <= 2 && t->posn == '_') {
555                t->posn = ' '; /* no short bounces */
556          }
557          if (t->height <= 1 && (t->posn == '=' || t->posn == '&')) {
558                t->posn = ' '; /* 1's need close catches */
559          }
560
561          switch (t->posn) {
562                  /*         throw          catch    */
563          case ' ': /* fall through */
564          case '-': posn = '-'; t->posn = '+'; break;
565          case '+': posn = '+'; t->posn = '-'; break;
566          case '=': posn = '='; t->posn = '+'; break;
567          case '&': posn = '+'; t->posn = '='; break;
568          case 'x': posn = '='; t->posn = '='; break;
569          case '_': posn = '_'; t->posn = '-'; break;
570          default: (void) fprintf(stderr, "unexpected posn %c\n", t->posn); break;
571          }
572          hand = (Hand) ((hand + 1) % 2);
573          t->status = ACTION;
574          t->hand = hand;
575          p = t->prev;
576
577          if (t->height == 1) {
578                p = p->prev; /* early throw */
579          }
580          t->action = CATCH;
581          INSERT_AFTER(nt, p);
582          nt->status = ACTION;
583          nt->action = THROW;
584          nt->height = t->height;
585          nt->hand = hand;
586          nt->posn = posn;
587        }
588  }
589  return True;
590}
591
592/* Connnect up throws and catches to figure out which ball goes where.
593   Do the same with the juggler's hands. */
594
595static void
596lob(ModeInfo *mi)
597{
598  jugglestruct *sp = &juggles[MI_SCREEN(mi)];
599  Trajectory *t, *p;
600  int h;
601  for (t = sp->head->next; t != sp->head; t = t->next) {
602        if (t->status == ACTION) {
603#if 0
604          (void) fprintf(stderr, (t->action == CATCH) ? "A %c%c%c\n" : "A %c%c%c%d\n",
605                          t->posn,
606                          t->hand ? 'R' : 'L',
607                          (t->action == THROW) ? 'T' : (t->action == CATCH ? 'C' : 'N'),
608                          t->height);
609#endif
610          if (t->action == THROW) {
611                if (t->type == Empty) {
612                  if (MI_NPIXELS(mi) > 2) {
613                        t->color = 1 + NRAND(MI_NPIXELS(mi) - 2);
614                  }
615                  t->spin = NRAND(5) - 2;
616                  t->degree_offset = NRAND(360);
617                  t->divisions = 2 * ((LRAND() & 1) + 1);
618                }
619
620                /* search forward for next hand catch */
621                for (p = t->next; t->handlink == NULL && p != sp->head; p = p->next) {
622                  if (p->action == CATCH) {
623                        if (t->handlink == NULL && p->hand == t->hand) {
624                          t->handlink = p; /* next catch in this hand */
625                        }
626                  }
627                }
628
629                if (t->height > 0) {
630                  h = t->height - 1;
631
632                  /* search forward for next ball catch */
633                  for (p = t->next; t->balllink == NULL&& p != sp->head; p = p->next) {
634                        if (p->action == CATCH) {
635                          if (t->balllink == NULL && --h < 1) { /* caught */
636#if 0
637                                if (p->type == Full) {
638                                  /* dropped */
639                                }
640#endif
641                                t->balllink = p; /* complete trajectory */
642                                p->type = Full;
643                                p->color = t->color; /* accept catch */
644                                p->spin = t->spin;
645                                p->degree_offset = t->degree_offset;
646                                p->divisions = t->divisions;
647                          }
648                        }
649                  }
650                }
651                t->type = Empty; /* thrown */
652          } else if (t->action == CATCH) {
653                /* search forward for next throw from this hand */
654                for (p = t->next; t->handlink == NULL && p != sp->head; p = p->next) {
655                  if (p->action == THROW && p->hand == t->hand) {
656                        p->type = t->type; /* pass ball */
657                        p->color = t->color; /* pass color */
658                        p->spin = NRAND(5) - 2;
659                        p->degree_offset = NRAND(360);
660                        p->divisions = 2 * ((LRAND() & 1) + 1);
661                        t->handlink = p;
662                  }
663                }
664          }
665          t->status = LINKEDACTION;
666        }
667  }
668}
669
670/* Convert hand position symbols into actual time/space coordinates */
671static void
672positions(jugglestruct *sp)
673{
674  Trajectory *t;
675  int now = 0;
676  for (t = sp->head->next; t != sp->head; t = t->next) {
677        if (t->status == PTHRATCH) {
678          now = t->start;
679        } else if (t->status == LINKEDACTION) {
680          int xo = 0, yo;
681
682          /* time */
683          if (t->action == CATCH) {
684                if (t->type == Empty) {
685                  now += (int) THROW_NULL_INTERVAL; /* failed catch is short */
686                } else {
687                  now += THROW_CATCH_INTERVAL;     /* successful catch */
688                }
689          } else {
690                now += (int) CATCH_THROW_INTERVAL;  /* throws are very short */
691          }
692          t->start = now;
693
694          /* space */
695          yo = ARMLENGTH;
696          switch (t->posn) {
697          case '-': xo = SX - POSE; break;
698          case '_':
699          case '+': xo = SX + POSE; break;
700          case '=': xo = - SX - POSE; yo += 2 * POSE; break;
701          default: (void) fprintf(stderr, "unexpected posn %c\n", t->posn); break;
702          }
703          t->x = sp->cx + ((t->hand == LEFT) ? xo : -xo);
704          t->y = sp->cy + yo;
705
706          t->status = PTHRATCH;
707        }
708  }
709}
710
711
712/* Private physics functions */
713
714static Spline
715makeSpline(int x0, double dx0, int t0, int x1, double dx1, int t1)
716{
717  Spline s;
718  double a, b, c, d;
719  int x10;
720  double t10;
721
722  x10 = x1 - x0;
723  t10 = t1 - t0;
724  a = ((dx0 + dx1)*t10 - 2*x10) / (t10*t10*t10);
725  b = (3*x10 - (2*dx0 + dx1)*t10) / (t10*t10);
726  c = dx0;
727  d = x0;
728  s.a = a;
729  s.b = -3*a*t0 + b;
730  s.c = (3*a*t0 - 2*b)*t0 + c;
731  s.d = ((-a*t0 + b)*t0 - c)*t0 +d;
732  return s;
733}
734
735static double
736makeSplinePair(Spline *s1, Spline *s2,
737                           int x0, double dx0, int t0,
738                           int x1, int t1,
739                           int x2, double dx2, int t2)
740{
741  int x10, x21;
742  double t21, t10, t20, dx1;
743  x10 = x1 - x0;
744  x21 = x2 - x1;
745  t21 = t2 - t1;
746  t10 = t1 - t0;
747  t20 = t2 - t0;
748  dx1 = (3*x10*t21*t21 + 3*x21*t10*t10 + 3*dx0*t10*t21*t21
749                 - dx2*t10*t10*t21 - 4*dx0*t10*t21*t21) /
750        (2*t10*t21*t20);
751  *s1 = makeSpline(x0, dx0, t0, x1, dx1, t1);
752  *s2 = makeSpline(x1, dx1, t1, x2, dx2, t2);
753  return dx1;
754}
755
756/* Turn abstract timings into physically appropriate ball trajectories. */
757static Bool
758projectile(jugglestruct *sp)
759{
760  Trajectory *t, *n;
761  for (t = sp->head->next; t != sp->head; t = t->next) {
762        if (t->status != PTHRATCH) {
763          continue;
764        }
765        if (t->action == THROW) {
766          if (t->balllink != NULL) {
767                if (t->posn == '_') { /* Bounce once */
768                  double tc, y0, yf, yc, tb, e, i;
769
770                  tc = t->balllink->start - t->start;
771
772                  yf = sp->cy + FY;
773                  y0 = t->y;
774                  yc = t->balllink->y;
775                  e = 1; /* permissible error in yc */
776
777                  /*
778                         tb = time to bounce
779                         yt = height at catch time after one bounce
780                         one or three roots according to timing
781                         find one by interval bisection
782                  */
783                  tb = tc;
784                  for(i = tc / 2; i > 0; i/=2){
785                        double dy, dt, yt;
786                        if(tb == 0){
787                          (void) fprintf(stderr, "div by zero!\n");
788                          break;
789                        }
790                        dy = (yf - y0)/tb + 0.5*sp->Gr*tb;
791                        dt = tc - tb;
792                        yt = -COR*dy*dt + 0.5*sp->Gr*dt*dt + yf;
793                        if(yt > yc + e){
794                          tb-=i;
795                        }else if(yt < yc - e){
796                          tb+=i;
797                        }else{
798                          break;
799                        }
800                  }
801
802                  {
803                        double t0, dy;
804
805                        t->dx = (t->balllink->x - t->x) / tc;
806
807                        /* ball follows parabola down */
808                        INSERT_AFTER(n, t->prev);
809                        n->start = t->start;
810                        n->finish = (int) (t->start + tb);
811                        n->type = Ball;
812                        n->color = t->color;
813                        n->spin = t->spin;
814                        n->degree_offset = t->degree_offset;
815                        n->divisions = t->divisions;
816                        n->status = PREDICTOR;
817
818                        t->dy = (yf - y0)/tb - 0.5*sp->Gr*tb;
819                        t0 = n->start;
820                        /* Represent parabola as a degenerate spline -
821                           linear in x, quadratic in y */
822                        n->xp.a = 0;
823                        n->xp.b = 0;
824                        n->xp.c = t->dx;
825                        n->xp.d = -t->dx*t0 + t->x;
826                        n->yp.a = 0;
827                        n->yp.b = sp->Gr/2;
828                        n->yp.c = -sp->Gr*t0 + t->dy;
829                        n->yp.d = sp->Gr/2*t0*t0 - t->dy*t0 + t->y;
830
831
832                        /* ball follows parabola up */
833                        INSERT_AFTER(n, t->prev);
834                        n->start = (int) (t0 + tb);
835                        n->finish = (int) (t0 + tc);
836                        n->type = Ball;
837                        n->color = t->color;
838                        n->spin = t->spin;
839                        n->degree_offset = t->degree_offset;
840                        n->divisions = t->divisions;
841                        n->status = PREDICTOR;
842
843                        n->xp.a = 0;
844                        n->xp.b = 0;
845                        n->xp.c = t->dx;
846                        n->xp.d = -t->dx*t0 + t->x;
847
848                        dy = (yf - y0)/tb + 0.5*sp->Gr*tb;
849                        t0 = n->start;
850                        /* Represent parabola as a degenerate spline -
851                           linear in x, quadratic in y */
852                        n->yp.a = 0;
853                        n->yp.b = sp->Gr/2;
854                        n->yp.c = -sp->Gr*t0 - COR*dy;
855                        n->yp.d = sp->Gr/2*t0*t0 + COR*dy*t0 + yf;
856                  }
857
858                  t->status = BPREDICTOR;
859
860                } else {
861                  double t0, dt;
862
863                  /* ball follows parabola */
864                  INSERT_AFTER(n, t->prev);
865                  n->start = t->start;
866                  n->finish = t->balllink->start;
867                  n->type = Ball;
868                  n->color = t->color;
869                  n->spin = t->spin;
870                  n->degree_offset = t->degree_offset;
871                  n->divisions = t->divisions;
872                  n->status = PREDICTOR;
873
874                  t0 = n->start;
875                  dt = t->balllink->start - t->start;
876                  t->dx = (t->balllink->x - t->x) / dt;
877                  t->dy = (t->balllink->y - t->y) / dt - sp->Gr/2 * dt;
878
879                  /* Represent parabola as a degenerate spline -
880                         linear in x, quadratic in y */
881                  n->xp.a = 0;
882                  n->xp.b = 0;
883                  n->xp.c = t->dx;
884                  n->xp.d = -t->dx*t0 + t->x;
885                  n->yp.a = 0;
886                  n->yp.b = sp->Gr/2;
887                  n->yp.c = -sp->Gr*t0 + t->dy;
888                  n->yp.d = sp->Gr/2*t0*t0 - t->dy*t0 + t->y;
889
890
891                  t->status = BPREDICTOR;
892                }
893          } else { /* Zero Throw */
894                t->status = BPREDICTOR;
895          }
896        }
897  }
898  return True;
899}
900
901/* Turn abstract hand motions into cubic splines. */
902static void
903hands(jugglestruct *sp)
904{
905  Trajectory *t, *u, *v;
906  for (t = sp->head->next; t != sp->head; t = t->next) {
907        /* no throw => no velocity */
908        if (t->status != BPREDICTOR) {
909          continue;
910        }
911
912        u = t->handlink;
913        if (u == NULL) { /* no next catch */
914          continue;
915        }
916        v = u->handlink;
917        if (v == NULL) { /* no next throw */
918          continue;
919        }
920
921        /* double spline takes hand from throw, thru catch, to
922           next throw */
923
924        t->finish = u->start;
925        t->status = PREDICTOR;
926
927        u->finish = v->start;
928        u->status = PREDICTOR;
929
930        (void) makeSplinePair(&t->xp, &u->xp,
931                                                  t->x, t->dx, t->start,
932                                                  u->x, u->start,
933                                                  v->x, v->dx, v->start);
934        (void) makeSplinePair(&t->yp, &u->yp,
935                                                  t->y, t->dy, t->start,
936                                                  u->y, u->start,
937                                                  v->y, v->dy, v->start);
938
939        t->status = PREDICTOR;
940  }
941}
942
943/* Given target x, y find_elbow puts hand at target if possible,
944 * otherwise makes hand point to the target */
945static void
946find_elbow(jugglestruct *sp, XPoint *h, XPoint *e, int x, int y, int z)
947{
948  double r, h2, t;
949
950  h2 = x*x + y*y + z*z;
951  if (h2 > 4*ARMLENGTH*ARMLENGTH) {
952        t = ARMLENGTH/sqrt(h2);
953        e->x = (short) (t*x);
954        e->y = (short) (t*y);
955        h->x = 2 * e->x;
956        h->y = 2 * e->y;
957  } else {
958        r = sqrt((double)(x*x + z*z));
959        t = sqrt(4 * ARMLENGTH * ARMLENGTH / h2 - 1);
960        e->x = (short) (x*(1 - y*t/r)/2);
961        e->y = (short) ((y + r*t)/2);
962        h->x = x;
963        h->y = y;
964  }
965}
966
967/* NOTE: returned x, y adjusted for arm reach */
968static void
969draw_arm(ModeInfo * mi, Hand side, int *x, int *y)
970{
971  Display *dpy = MI_DISPLAY(mi);
972  Window win = MI_WINDOW(mi);
973  GC gc = MI_GC(mi);
974  jugglestruct *sp = &juggles[MI_SCREEN(mi)];
975
976  int sig = (side == LEFT) ? 1 : -1;
977
978  XSetLineAttributes(dpy, gc,
979                ARMWIDTH, LineSolid, CapRound, JoinRound);
980  if (sp->arm[side][0].x != *x || sp->arm[side][0].y != *y) {
981        XPoint h, e;
982        XSetForeground(dpy, gc, MI_BLACK_PIXEL(mi));
983        find_elbow(sp, &h, &e, *x - sig*SX - sp->cx, *y - SY - sp->cy, SZ);
984        XDrawLines(dpy, win, gc, sp->arm[side], 3, CoordModeOrigin);
985        *x = sp->arm[side][0].x = sp->cx + sig*SX + h.x;
986        *y = sp->arm[side][0].y = sp->cy + SY + h.y;
987        sp->arm[side][1].x = sp->cx + sig*SX + e.x;
988        sp->arm[side][1].y = sp->cy + SY + e.y;
989  }
990  XSetForeground(dpy, gc, MI_WHITE_PIXEL(mi));
991  XDrawLines(dpy, win, gc, sp->arm[side], 3, CoordModeOrigin);
992  XSetLineAttributes(dpy, gc,
993                1, LineSolid, CapNotLast, JoinRound);
994}
995
996static void
997draw_figure(ModeInfo * mi)
998{
999  Display *dpy = MI_DISPLAY(mi);
1000  Window win = MI_WINDOW(mi);
1001  GC gc = MI_GC(mi);
1002  jugglestruct *sp = &juggles[MI_SCREEN(mi)];
1003
1004  XSetLineAttributes(dpy, gc,
1005                ARMWIDTH, LineSolid, CapRound, JoinRound);
1006  XSetForeground(dpy, gc, MI_WHITE_PIXEL(mi));
1007  XDrawLines(dpy, win, gc, sp->figure_path, FIGURE1, CoordModeOrigin);
1008  XDrawSegments(dpy, win, gc, sp->figure_segs, FIGURE2);
1009  XDrawArc(dpy, win, gc,
1010         sp->cx - HED/2, sp->cy + NEY - HED, HED, HED, 0, 64*360);
1011  XSetLineAttributes(dpy, gc,
1012                1, LineSolid, CapNotLast, JoinRound);
1013}
1014
1015
1016/* dumps a human-readable rendition of the current state of the juggle
1017   pipeline to stderr for debugging */
1018#ifdef OLDDEBUG
1019static void
1020dump(jugglestruct *sp)
1021{
1022  Trajectory *t;
1023
1024  for (t = sp->head->next; t != sp->head; t = t->next) {
1025        switch (t->status) {
1026        case THROW:
1027          (void) fprintf(stderr, "T %c%d\n", t->posn, t->height);
1028          break;
1029        case ACTION:
1030          (void) fprintf(stderr, t->action == CATCH?"A %c%c%c\n":"A %c%c%c%d\n",
1031                          t->posn,
1032                          t->hand ? 'R' : 'L',
1033                          (t->action == THROW)?'T':(t->action == CATCH?'C':'N'),
1034                          t->height);
1035          break;
1036        case LINKEDACTION:
1037          (void) fprintf(stderr, "L %c%c%c%d %d\n",
1038                          t->posn,
1039                          t->hand?'R':'L',
1040                          (t->action == THROW)?'T':(t->action == CATCH?'C':'N'),
1041                          t->height, t->color);
1042          break;
1043        case PTHRATCH:
1044          (void) fprintf(stderr, "O %c%c%c%d %d %2d %6d %6d\n", t->posn,
1045                          t->hand?'R':'L',
1046                          (t->action == THROW)?'T':(t->action == CATCH?'C':'N'),
1047                          t->height, t->type, t->color,
1048                          t->start, t->finish);
1049          break;
1050        case PREDICTOR:
1051          (void) fprintf(stderr, "P %c      %2d %6d %6d %g\n",
1052                          t->type == Ball?'b':t->type == Empty?'e':'f',
1053                          t->color,
1054                          t->start, t->finish, t->yp.c);
1055          break;
1056        default:
1057          (void) fprintf(stderr, "status %d not implemented\n", t->status);
1058          break;
1059        }
1060  }
1061}
1062#endif
1063
1064static int get_num_balls(const char *j)
1065{
1066  int balls = 0;
1067  const char *p;
1068  int h = 0;
1069  for (p = j; *p; p++) {
1070        if (*p >= '0' && *p <='9') { /* digit */
1071          h = 10*h + (*p - '0');
1072        } else {
1073          if (h > balls) {
1074                balls = h;
1075          }
1076          h = 0;
1077        }
1078  }
1079  return balls;
1080}
1081
1082#ifdef __cplusplus
1083extern "C" {
1084#endif
1085
1086static int compare_num_balls(const void *p1, const void *p2)
1087{
1088  int i = get_num_balls(((patternstruct*)p1)->pattern);
1089  int j = get_num_balls(((patternstruct*)p2)->pattern);
1090  if (i > j) {
1091        return (1);
1092  } else if (i < j) {
1093        return (-1);
1094  } else {
1095        return (0);
1096  }
1097}
1098
1099#ifdef __cplusplus
1100}
1101#endif
1102
1103/* Public functions */
1104
1105void
1106release_juggle(ModeInfo * mi)
1107{
1108        if (juggles != NULL) {
1109                int screen;
1110
1111                for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
1112                        free_juggle(&juggles[screen]);
1113                (void) free((void *) juggles);
1114                juggles = (jugglestruct *) NULL;
1115        }
1116        if (patternindex != NULL) {
1117                (void) free((void *) patternindex);
1118                patternindex = (PatternIndex *) NULL;
1119        }
1120}
1121
1122void
1123init_juggle(ModeInfo * mi)
1124{
1125        jugglestruct *sp;
1126        int i;
1127        XPoint figure1[FIGURE1];
1128        XSegment figure2[FIGURE2];
1129        if (pattern != NULL && *pattern == '.') {
1130          pattern = NULL;
1131        }
1132        if (pattern == NULL && patternindex == NULL) {
1133          /* pattern list needs indexing */
1134          int nelements = sizeof(portfolio)/sizeof(patternstruct);
1135          int maxballs;
1136          int numpat = 0;
1137
1138          /* sort according to number of balls */
1139          qsort((void*)portfolio, nelements,
1140                        sizeof(patternstruct), compare_num_balls);
1141
1142          /* last pattern has most balls */
1143          maxballs = get_num_balls(portfolio[nelements - 1].pattern);
1144          /* allocate index */
1145          if ((patternindex = (PatternIndex *) calloc(maxballs + 1,
1146                                sizeof (PatternIndex))) == NULL) {
1147                return;
1148          }
1149
1150          /* run through sorted list, indexing start of each group
1151                 and number in group */
1152          maxballs = 1;
1153          for (i = 0; i < nelements; i++) {
1154                int b = get_num_balls(portfolio[i].pattern);
1155                if (b > maxballs) {
1156                  if (MI_IS_VERBOSE(mi)) {
1157                        (void) fprintf(stderr, "%d %d ball pattern%s\n",
1158                                        numpat, maxballs, (numpat == 1) ? "" : "s");
1159                  }
1160                  patternindex[maxballs].number = numpat;
1161                  maxballs = b;
1162                  numpat = 1;
1163                  patternindex[maxballs].start = i;
1164                } else {
1165                  numpat++;
1166                }
1167          }
1168          if (MI_IS_VERBOSE(mi)) {
1169                (void) fprintf(stderr, "%d %d ball pattern%s\n",
1170                                numpat, maxballs, (numpat == 1) ? "" : "s");
1171          }
1172          patternindex[maxballs].number = numpat;
1173        }
1174
1175        if (juggles == NULL) { /* allocate jugglestruct */
1176                if ((juggles = (jugglestruct *) calloc(MI_NUM_SCREENS(mi),
1177                               sizeof (jugglestruct))) == NULL) {
1178                        release_juggle(mi);
1179                        return;
1180                }
1181        }
1182        sp = &juggles[MI_SCREEN(mi)];
1183
1184        sp->count = 0;
1185
1186        if (MI_IS_FULLRANDOM(mi)) {
1187                sp->solid = (Bool) (LRAND() & 1);
1188#ifdef UNI
1189                sp->uni = (Bool) (LRAND() & 1);
1190#endif
1191        } else {
1192                sp->solid = solid;
1193#ifdef UNI
1194                sp->uni = uni;
1195#endif
1196        }
1197
1198        sp->width = MI_WIDTH(mi);
1199        sp->height = MI_HEIGHT(mi);
1200        sp->count = ABS(MI_COUNT(mi));
1201        if (sp->count == 0)
1202                sp->count = 150;
1203        sp->scale = sp->height / 480.0;
1204        /* vary x a little so the juggler does not burn the screen */
1205        sp->cx = sp->width / 2 + RFX + NRAND(LFX - RFX + 1);
1206        sp->cy = sp->height - FY - ((int) sp->uni) * FY / 3; /* raise higher */
1207        /* "7" hits top of screen */
1208        sp->Gr = GRAVITY(sp->cy, 7 * THROW_CATCH_INTERVAL);
1209
1210        figure1[0].x = LHIPX, figure1[0].y = HIPY;
1211        figure1[1].x = 0, figure1[1].y = WSTY;
1212        figure1[2].x = SX, figure1[2].y = SY;
1213        figure1[3].x = -SX, figure1[3].y = SY;
1214        figure1[4].x = 0, figure1[4].y = WSTY;
1215        figure1[5].x = RHIPX, figure1[5].y = HIPY;
1216        figure1[6].x = LHIPX, figure1[6].y = HIPY;
1217        figure2[0].x1 = 0, figure2[0].y1 = SY,
1218          figure2[0].x2 = 0, figure2[0].y2 = NEY;
1219        figure2[1].x1 = LHIPX, figure2[1].y1 = HIPY,
1220          figure2[1].x2 = LFX, figure2[1].y2 = FY;
1221        figure2[2].x1 = RHIPX, figure2[2].y1 = HIPY,
1222          figure2[2].x2 = RFX, figure2[2].y2 = FY;
1223
1224        /* Body Path */
1225        for (i = 0; i <  FIGURE1; i++) {
1226          sp->figure_path[i].x = figure1[i].x + sp->cx;
1227          sp->figure_path[i].y = figure1[i].y + sp->cy;
1228        }
1229        /* Body Segments */
1230        for (i = 0; i < FIGURE2; i++) {
1231          sp->figure_segs[i].x1 = figure2[i].x1 + sp->cx;
1232          sp->figure_segs[i].y1 = figure2[i].y1 + sp->cy;
1233          sp->figure_segs[i].x2 = figure2[i].x2 + sp->cx;
1234          sp->figure_segs[i].y2 = figure2[i].y2 + sp->cy;
1235        }
1236        /* Shoulders */
1237        sp->arm[LEFT][2].x = sp->cx + SX;
1238        sp->arm[LEFT][2].y = sp->cy + SY;
1239        sp->arm[RIGHT][2].x = sp->cx - SX;
1240        sp->arm[RIGHT][2].y = sp->cy + SY;
1241
1242        if (sp->trace == NULL) {
1243          if ((sp->trace = (XPoint *)calloc(trail, sizeof(XPoint))) == NULL) {
1244                free_juggle(sp);
1245                return;
1246          }
1247        }
1248
1249        /* Clear the background. */
1250        MI_CLEARWINDOW(mi);
1251
1252        draw_figure(mi);
1253
1254        /* record start time */
1255        sp->begintime = time(NULL);
1256
1257        free_pattern(sp);
1258
1259        /* create circular list */
1260        INSERT_AFTER_TOP(sp->head, sp->head);
1261
1262        /* generate pattern */
1263        if (pattern == NULL) {
1264
1265#define MAXPAT 10
1266#define MAXREPEAT 30
1267#define CHANGE_BIAS 8 /* larger makes num_ball changes less likely */
1268#define POSITION_BIAS 20 /* larger makes hand movements less likely */
1269
1270          int count = 0;
1271          int num_balls = MINBALLS + NRAND(MAXBALLS - MINBALLS);
1272          while (count < MI_CYCLES(mi)) {
1273                char buf[MAXPAT * 3 + 3], *b = buf;
1274                int maxseen = 0;
1275                int l = NRAND(MAXPAT) + 1;
1276                int t = NRAND(MAXREPEAT) + 1;
1277
1278                { /* vary number of balls */
1279                  int new_balls = num_balls;
1280                  int change;
1281
1282                  if (new_balls == 2) /* Do not juggle 2 that often */
1283                    change = NRAND(2 + CHANGE_BIAS / 4);
1284                  else
1285                    change = NRAND(2 + CHANGE_BIAS);
1286                  switch (change) {
1287                  case 0:
1288                        new_balls++;
1289                        break;
1290                  case 1:
1291                        new_balls--;
1292                        break;
1293                  default:
1294                        break; /* NO-OP */
1295                  }
1296                  if (new_balls < MINBALLS) {
1297                        new_balls += 2;
1298                  }
1299                  if (new_balls > MAXBALLS) {
1300                        new_balls -= 2;
1301                  }
1302                  if (new_balls < num_balls) {
1303                        if (!program(mi, "[*]", 1)) /* lose ball */
1304                                return;
1305                  }
1306                  num_balls = new_balls;
1307                }
1308                count++;
1309
1310                if (NRAND(2) && patternindex[num_balls].number) {
1311                  /* Pick from PortFolio */
1312                  if (!program(mi,
1313                          portfolio[patternindex[num_balls].start +
1314                                  NRAND(patternindex[num_balls].number)].pattern,
1315                                  t))
1316                        return;
1317                } else {
1318                  /* Invent a new pattern */
1319                  *b++='[';
1320                  for(i = 0; i < l; i++){
1321                        int n, m;
1322                        do { /* Triangular Distribution => high values more likely */
1323                          m = NRAND(num_balls + 1);
1324                          n = NRAND(num_balls + 1);
1325                        } while(m >= n);
1326                        if (n == num_balls) {
1327                          maxseen = 1;
1328                        }
1329                        switch(NRAND(6 + POSITION_BIAS)){
1330                        case 0:            /* Inside throw */
1331                          *b++ = '-'; break;
1332                        case 1:            /* Outside throw */
1333                          *b++ = '+'; break;
1334                        case 2:            /* Cross throw */
1335                          *b++ = '='; break;
1336                        case 3:            /* Cross catch */
1337                          *b++ = '&'; break;
1338                        case 4:            /* Cross throw and catch */
1339                          *b++ = 'x'; break;
1340                        case 5:            /* Bounce */
1341                          *b++ = '_'; break;
1342                        default:
1343                          break; /* NO-OP */
1344                        }
1345
1346                        *b++ = n + '0';
1347                        *b++ = ' ';
1348                  }
1349                  *b++ = ']';
1350                  *b = '\0';
1351                  if (maxseen) {
1352                        if (!program(mi, buf, t))
1353                                return;
1354                  }
1355                }
1356          }
1357        } else { /* pattern supplied in height or 'a' notation */
1358          if (!program(mi, pattern, MI_CYCLES(mi)))
1359                return;
1360        }
1361
1362        adam(sp);
1363
1364        if (!part(sp))
1365                return;
1366
1367        lob(mi);
1368
1369        positions(sp);
1370
1371        if (!projectile(sp))
1372                return;
1373
1374        hands(sp);
1375
1376#ifdef OLDDEBUG
1377        dump(sp);
1378#endif
1379}
1380
1381#define CUBIC(s, t) ((((s).a * (t) + (s).b) * (t) + (s).c) * (t) + (s).d)
1382
1383#ifdef SUNOS4
1384/*-
1385 * Workaround SunOS 4 framebuffer bug which causes balls to leave dust
1386 * trace behind when erased
1387 */
1388#define ERASE_BALL(x,y) \
1389        XSetForeground(dpy, gc, MI_BLACK_PIXEL(mi)); \
1390        XFillArc(dpy, window, gc, \
1391                (x) - BALLRADIUS - 2, (y) - BALLRADIUS - 2, \
1392                2*(BALLRADIUS + 2), 2*(BALLRADIUS + 2), 0, 23040)
1393#else
1394
1395#define ERASE_BALL(x,y) \
1396        XSetForeground(dpy, gc, MI_BLACK_PIXEL(mi)); \
1397        XFillArc(dpy, window, gc, \
1398                (x) - BALLRADIUS, (y) - BALLRADIUS, \
1399                2*BALLRADIUS, 2*BALLRADIUS, 0, 23040)
1400#endif
1401
1402static void
1403draw_juggle_ball(ModeInfo *mi, unsigned long color, int x, int y, double degree_offset, int divisions)
1404{
1405        Display    *dpy = MI_DISPLAY(mi);
1406        Window      window = MI_WINDOW(mi);
1407        GC          gc = MI_GC(mi);
1408        jugglestruct *sp = &juggles[MI_SCREEN(mi)];
1409        int offset;
1410
1411        XSetForeground(dpy, gc, color);
1412        if ((color == MI_WHITE_PIXEL(mi)) ||
1413            ((divisions != 2) && (divisions != 4)) || sp->solid) {
1414                XFillArc(dpy, window, gc,
1415                        x - BALLRADIUS, y - BALLRADIUS,
1416                        2*BALLRADIUS, 2*BALLRADIUS,
1417                        0, 23040);
1418                return;
1419        }
1420        offset = (int) (degree_offset * 64);
1421        if (divisions == 4) { /* 90 degree divisions */
1422                XFillArc(dpy, window, gc,
1423                        x - BALLRADIUS, y - BALLRADIUS,
1424                        2*BALLRADIUS, 2*BALLRADIUS,
1425                        offset, 5760);
1426                XFillArc(dpy, window, gc,
1427                        x - BALLRADIUS, y - BALLRADIUS,
1428                        2*BALLRADIUS, 2*BALLRADIUS,
1429                        (offset + 11520) % 23040, 5760);
1430                XSetForeground(dpy, gc, MI_WHITE_PIXEL(mi));
1431                XFillArc(dpy, window, gc,
1432                        x - BALLRADIUS, y - BALLRADIUS,
1433                        2*BALLRADIUS, 2*BALLRADIUS,
1434                        (offset + 5760) % 23040, 5760);
1435                XFillArc(dpy, window, gc,
1436                        x - BALLRADIUS, y - BALLRADIUS,
1437                        2*BALLRADIUS, 2*BALLRADIUS,
1438                        (offset + 17280) % 23040, 5760);
1439        } else { /* 180 degree divisions */
1440                XFillArc(dpy, window, gc,
1441                        x - BALLRADIUS, y - BALLRADIUS,
1442                        2*BALLRADIUS, 2*BALLRADIUS,
1443                        offset, 11520);
1444                XSetForeground(dpy, gc, MI_WHITE_PIXEL(mi));
1445                XFillArc(dpy, window, gc,
1446                        x - BALLRADIUS, y - BALLRADIUS,
1447                        2*BALLRADIUS, 2*BALLRADIUS,
1448                        (offset + 11520) % 23040, 11520);
1449        }
1450        XFlush(dpy);
1451}
1452
1453void
1454draw_juggle(ModeInfo * mi)
1455{
1456        Display    *dpy = MI_DISPLAY(mi);
1457        Window      window = MI_WINDOW(mi);
1458        GC          gc = MI_GC(mi);
1459        Trajectory *traj;
1460        int future = 0;
1461        int length = 0;
1462        jugglestruct *sp;
1463
1464        if (juggles == NULL)
1465                return;
1466        sp = &juggles[MI_SCREEN(mi)];
1467        if (sp->trace == NULL)
1468                return;
1469
1470        MI_IS_DRAWN(mi) = True;
1471
1472        draw_figure(mi);
1473
1474        {
1475          struct timeval tv;
1476# ifdef GETTIMEOFDAY_TWO_ARGS
1477      struct timezone tzp;
1478          gettimeofday(&tv, &tzp);
1479# else
1480          gettimeofday(&tv);
1481# endif
1482
1483          sp->time = (int) ((tv.tv_sec - sp->begintime)*1000 + tv.tv_usec/1000);
1484        }
1485        for (traj = sp->head->next; traj != sp->head; traj = traj->next) {
1486          length++;
1487          if (traj->status != PREDICTOR) {
1488                continue;
1489          }
1490          if (traj->start > future) {
1491                future = traj->start;
1492          }
1493          if (sp->time < traj->start) { /* early */
1494                continue;
1495          } else if (sp->time < traj->finish) { /* working */
1496                int x = (int) CUBIC(traj->xp, sp->time);
1497                int y = (int) CUBIC(traj->yp, sp->time);
1498                unsigned long color;
1499
1500                if (MI_NPIXELS(mi) > 2) {
1501                  color = MI_PIXEL(mi, traj->color);
1502                } else {
1503                  color = MI_WHITE_PIXEL(mi);
1504                }
1505                if (traj->type == Empty || traj->type == Full) {
1506                  draw_arm(mi, traj->hand, &x, &y);
1507                }
1508                if (traj->type == Ball || traj->type == Full) {
1509                  if(trail > 0) {
1510                        ERASE_BALL(sp->trace[sp->traceindex].x,
1511                                sp->trace[sp->traceindex].y);
1512                        sp->trace[sp->traceindex].x = traj->x;
1513                        sp->trace[sp->traceindex].y = traj->y;
1514                        if (++sp->traceindex >= trail) {
1515                          sp->traceindex = 0;
1516                        }
1517                  } else {
1518                        ERASE_BALL(traj->x, traj->y);
1519                  }
1520                  draw_juggle_ball(mi, color, x, y, traj->degree_offset, traj->divisions);
1521                  traj->degree_offset = traj->degree_offset +
1522                    SPIN_DEGREES * traj->spin / sp->count;
1523                  if (traj->degree_offset < 0.0)
1524                        traj->degree_offset += 360.0;
1525                  else if (traj->degree_offset >= 360.0)
1526                        traj->degree_offset -= 360.0;
1527                }
1528                traj->x = x;
1529                traj->y = y;
1530          } else { /* expired */
1531                Trajectory *n = traj;
1532
1533                ERASE_BALL(traj->x, traj->y);
1534                traj=traj->prev;
1535                REMOVE(n);
1536          }
1537        }
1538
1539        /*** FIXME-BEGIN ***/
1540        /* pattern generator would refill here when necessary */
1541#if 1
1542        if (future == 0) {
1543#else
1544        if (sp->count > MI_CYCLES(mi)) { /* pick a new juggle */
1545#endif
1546                        init_juggle(mi);
1547        }
1548        /*** FIXME-END ***/
1549
1550}
1551
1552#endif /* MODE_juggle */
Note: See TracBrowser for help on using the repository browser.