diff options
Diffstat (limited to 'hacks/juggle.c')
| -rw-r--r-- | hacks/juggle.c | 2797 |
1 files changed, 0 insertions, 2797 deletions
diff --git a/hacks/juggle.c b/hacks/juggle.c deleted file mode 100644 index fba0fe5..0000000 --- a/hacks/juggle.c +++ /dev/null @@ -1,2797 +0,0 @@ -/* -*- Mode: C; tab-width: 4 -*- */ -/* juggle */ - -#if 0 -static const char sccsid[] = "@(#)juggle.c 5.10 2003/09/02 xlockmore"; -#endif - -/*- - * Copyright (c) 1996 by Tim Auckland <tda10.geo@yahoo.com> - * - * Permission to use, copy, modify, and distribute this software and its - * documentation for any purpose and without fee is hereby granted, - * provided that the above copyright notice appear in all copies and that - * both that copyright notice and this permission notice appear in - * supporting documentation. - * - * This file is provided AS IS with no warranties of any kind. The author - * shall have no liability with respect to the infringement of copyrights, - * trade secrets or any patents by this file or any part thereof. In no - * event will the author be liable for any lost revenue or profits or - * other special, indirect and consequential damages. - * - * Revision History - * 13-Dec-2004: [TDA] Use -cycles and -count in a rational manner. - * Add -rings, -bballs. Add -describe. Finally made - * live pattern updates possible. Add refill_juggle(), - * change_juggle() and reshape_juggle(). Make - * init_juggle() non-destructive. Reorder erase/draw - * operations. Update xscreensaver xml and manpage. - * 15-Nov-2004: [TDA] Fix all memory leaks. - * 12-Nov-2004: [TDA] Add -torches and another new trail - * implementation, so that different objects can have - * different length trails. - * 11-Nov-2004: [TDA] Clap when all the balls are in the air. - * 10-Nov-2004: [TDA] Display pattern name converted to hight - * notation. - * 31-Oct-2004: [TDA] Add -clubs and new trail implementation. - * 02-Sep-2003: Non-real time to see what is happening without a - * strobe effect for slow machines. - * 01-Nov-2000: Allocation checks - * 1996: Written - */ - -/*- - * TODO - * Implement the anonymously promised -uni option. - */ - - -/* - * Notes on Adam Chalcraft Juggling Notation (used by permission) - * a-> Adam's notation s-> Site swap (Cambridge) notation - * - * To define a map from a-notation to s-notation ("site-swap"), both - * of which look like doubly infinite sequences of natural numbers. In - * s-notation, there is a restriction on what is allowed, namely for - * the sequence s_n, the associated function f(n)=n+s_n must be a - * bijection. In a-notation, there is no restriction. - * - * To go from a-notation to s-notation, you start by mapping each a_n - * to a permutation of N, the natural numbers. - * - * 0 -> the identity - * 1 -> (10) [i.e. f(1)=0, f(0)=1] - * 2 -> (210) [i.e. f(2)=1, f(1)=0, f(0)=2] - * 3 -> (3210) [i.e. f(3)=2, f(2)=1, f(1)=0, f(0)=3] - * etc. - * - * Then for each n, you look at how long 0 takes to get back to 0 - * again and you call this t_n. If a_n=0, for example, then since the - * identity leaves 0 alone, it gets back to 0 in 1 step, so t_n=1. If - * a_n=1, then f(0)=1. Now any further a_n=0 leave 1 alone, but the - * next a_n>0 sends 1 back to 0. Hence t_n is 2 + the number of 0's - * following the 1. Finally, set s_n = t_n - 1. - * - * To give some examples, it helps to have a notation for cyclic - * sequences. By (123), for example, I mean ...123123123123... . Now - * under the a-notation -> s-notation mapping we have some familiar - * examples: - * - * (0)->(0), (1)->(1), (2)->(2) etc. - * (21)->(31), (31)->(51), (41)->(71) etc. - * (10)->(20), (20)->(40), (30)->(60) etc. - * (331)->(441), (312)->(612), (303)->(504), (321)->(531) - * (43)->(53), (434)->(534), (433)->(633) - * (552)->(672) - * - * In general, the number of balls is the *average* of the s-notation, - * and the *maximum* of the a-notation. Another theorem is that the - * minimum values in the a-notation and the s-notation and equal, and - * preserved in the same positions. - * - * The usefulness of a-notation is the fact that there are no - * restrictions on what is allowed. This makes random juggle - * generation much easier. It also makes enumeration very - * easy. Another handy feature is computing changes. Suppose you can - * do (5) and want a neat change up to (771) in s-notation [Mike Day - * actually needed this example!]. Write them both in a-notation, - * which gives (5) and (551). Now concatenate them (in general, there - * may be more than one way to do this, but not in this example), to - * get - * - * ...55555555551551551551551... - * - * Now convert back to s-notation, to get - * - * ...55555566771771771771771... - * - * So the answer is to do two 6 throws and then go straight into - * (771). Coming back down of course, - * - * ...5515515515515515555555555... - * - * converts to - * - * ...7717717717716615555555555... - * - * so the answer is to do a single 661 and then drop straight down to - * (5). - * - * [The number of balls in the generated pattern occasionally changes. - * In order to decrease the number of balls I had to introduce a new - * symbol into the Adam notation, [*] which means 'lose the current - * ball'.] - */ - -/* This code uses so many linked lists it's worth having a built-in - * leak-checker */ -#undef MEMTEST - -#ifdef STANDALONE -# define MODE_juggle -# define DEFAULTS "*delay: 10000 \n" \ - "*count: 200 \n" \ - "*cycles: 1000 \n" \ - "*ncolors: 32 \n" \ - "*font: -*-helvetica-bold-r-normal-*-180-*\n" \ - "*fpsSolid: true\n" \ - -# define release_juggle 0 -# define reshape_juggle 0 -# define juggle_handle_event 0 -# undef SMOOTH_COLORS -# include "xlockmore.h" /* in xscreensaver distribution */ -#else /* STANDALONE */ -# include "xlock.h" /* in xlockmore distribution */ -#endif /* STANDALONE */ - -#ifdef MODE_juggle - -#if 0 -#define XClearWindow(d, w) \ -{ \ - XSetForeground(d, MI_GC(mi), MI_PIXEL(mi, 3)); \ - XFillRectangle(d, w, MI_GC(mi), \ - 0, 0, (unsigned int) MI_WIDTH(mi), (unsigned int) MI_HEIGHT(mi)); \ -} -#endif - -#define DEF_PATTERN "random" /* All patterns */ -#define DEF_TAIL "1" /* No trace */ -#ifdef UNI -/* Maybe a ROLA BOLA would be at a better angle for viewing */ -#define DEF_UNI "False" /* No unicycle */ /* Not implemented yet */ -#endif -#define DEF_REAL "True" -#define DEF_DESCRIBE "True" - -#define DEF_BALLS "True" /* Use Balls */ -#define DEF_CLUBS "True" /* Use Clubs */ -#define DEF_TORCHES "True" /* Use Torches */ -#define DEF_KNIVES "True" /* Use Knives */ -#define DEF_RINGS "True" /* Use Rings */ -#define DEF_BBALLS "True" /* Use Bowling Balls */ - -#ifndef XtNumber -#define XtNumber(arr) ((unsigned int) (sizeof(arr) / sizeof(arr[0]))) -#endif - -static char *pattern; -static int tail; -#ifdef UNI -static Bool uni; -#endif -static Bool real; -static Bool describe; -static Bool balls; -static Bool clubs; -static Bool torches; -static Bool knives; -static Bool rings; -static Bool bballs; -static char *only; - -static XrmOptionDescRec opts[] = -{ - {"-pattern", ".juggle.pattern", XrmoptionSepArg, NULL }, - {"-tail", ".juggle.tail", XrmoptionSepArg, NULL }, -#ifdef UNI - {"-uni", ".juggle.uni", XrmoptionNoArg, "on" }, - {"+uni", ".juggle.uni", XrmoptionNoArg, "off" }, -#endif - {"-real", ".juggle.real", XrmoptionNoArg, "on" }, - {"+real", ".juggle.real", XrmoptionNoArg, "off" }, - {"-describe", ".juggle.describe", XrmoptionNoArg, "on" }, - {"+describe", ".juggle.describe", XrmoptionNoArg, "off" }, - {"-balls", ".juggle.balls", XrmoptionNoArg, "on" }, - {"+balls", ".juggle.balls", XrmoptionNoArg, "off" }, - {"-clubs", ".juggle.clubs", XrmoptionNoArg, "on" }, - {"+clubs", ".juggle.clubs", XrmoptionNoArg, "off" }, - {"-torches", ".juggle.torches", XrmoptionNoArg, "on" }, - {"+torches", ".juggle.torches", XrmoptionNoArg, "off" }, - {"-knives", ".juggle.knives", XrmoptionNoArg, "on" }, - {"+knives", ".juggle.knives", XrmoptionNoArg, "off" }, - {"-rings", ".juggle.rings", XrmoptionNoArg, "on" }, - {"+rings", ".juggle.rings", XrmoptionNoArg, "off" }, - {"-bballs", ".juggle.bballs", XrmoptionNoArg, "on" }, - {"+bballs", ".juggle.bballs", XrmoptionNoArg, "off" }, - {"-only", ".juggle.only", XrmoptionSepArg, NULL }, -}; -static argtype vars[] = -{ - { &pattern, "pattern", "Pattern", DEF_PATTERN, t_String }, - { &tail, "tail", "Tail", DEF_TAIL, t_Int }, -#ifdef UNI - { &uni, "uni", "Uni", DEF_UNI, t_Bool }, -#endif - { &real, "real", "Real", DEF_REAL, t_Bool }, - { &describe, "describe", "Describe", DEF_DESCRIBE, t_Bool }, - { &balls, "balls", "Clubs", DEF_BALLS, t_Bool }, - { &clubs, "clubs", "Clubs", DEF_CLUBS, t_Bool }, - { &torches, "torches", "Torches", DEF_TORCHES, t_Bool }, - { &knives, "knives", "Knives", DEF_KNIVES, t_Bool }, - { &rings, "rings", "Rings", DEF_RINGS, t_Bool }, - { &bballs, "bballs", "BBalls", DEF_BBALLS, t_Bool }, - { &only, "only", "BBalls", " ", t_String }, -}; -static OptionStruct desc[] = -{ - { "-pattern string", "Cambridge Juggling Pattern" }, - { "-tail num", "Trace Juggling Patterns" }, -#ifdef UNI - { "-/+uni", "Unicycle" }, -#endif - { "-/+real", "Real-time" }, - { "-/+describe", "turn on/off pattern descriptions." }, - { "-/+balls", "turn on/off Balls." }, - { "-/+clubs", "turn on/off Clubs." }, - { "-/+torches", "turn on/off Flaming Torches." }, - { "-/+knives", "turn on/off Knives." }, - { "-/+rings", "turn on/off Rings." }, - { "-/+bballs", "turn on/off Bowling Balls." }, - { "-only", "Turn off all objects but the named one." }, -}; - -ENTRYPOINT ModeSpecOpt juggle_opts = - { XtNumber(opts), opts, XtNumber(vars), vars, desc }; - -#ifdef USE_MODULES -ModStruct juggle_description = { - "juggle", "init_juggle", "draw_juggle", (char *) NULL, - "draw_juggle", "change_juggle", "free_juggle", &juggle_opts, - 10000, 200, 1000, 1, 64, 1.0, "", - "Shows a Juggler, juggling", 0, NULL -}; - -#endif - -#ifdef USE_XVMSUTILS -# include <X11/unix_time.h> -#endif - -/* Note: All "lengths" are scaled by sp->scale = MI_HEIGHT/480. All - "thicknesses" are scaled by sqrt(sp->scale) so that they are - proportionally thicker for smaller windows. Objects spinning out - of the plane (such as clubs) fake perspective by compressing their - horizontal coordinates by PERSPEC */ - -/* Figure */ -#define ARMLENGTH 50 -#define ARMWIDTH ((int) (8.0 * sqrt(sp->scale))) -#define POSE 10 -#define BALLRADIUS ARMWIDTH - -#define PERSPEC 0.4 - -/* macros */ -#define GRAVITY(h, t) 4*(double)(h)/((t)*(t)) - -/* Timing based on count. Units are milliseconds. Juggles per second - is: 2000 / THROW_CATCH_INTERVAL + CATCH_THROW_INTERVAL */ - -#define THROW_CATCH_INTERVAL (sp->count) -#define THROW_NULL_INTERVAL (sp->count * 0.5) -#define CATCH_THROW_INTERVAL (sp->count * 0.2) - -/******************************************************************** - * Trace Definitions * - * * - * These record rendering data so that a drawn object can be erased * - * later. Each object has its own Trace list. * - * * - ********************************************************************/ - -typedef struct {double x, y; } DXPoint; -typedef struct trace *TracePtr; -typedef struct trace { - TracePtr next, prev; - double x, y; - double angle; - int divisions; - DXPoint dlast; -#ifdef MEMTEST - char pad[1024]; -#endif -} Trace; - -/******************************************************************* - * Object Definitions * - * * - * These describe the various types of Object that can be juggled * - * * - *******************************************************************/ -typedef void (DrawProc)(ModeInfo*, unsigned long, Trace *); - -static DrawProc show_ball, show_europeanclub, show_torch, show_knife; -static DrawProc show_ring, show_bball; - -typedef enum {BALL, CLUB, TORCH, KNIFE, RING, BBALLS, - NUM_OBJECT_TYPES} ObjType; -#define OBJMIXPROB 20 /* inverse of the chances of using an odd - object in the pattern */ - -static const struct { - DrawProc *draw; /* Object Rendering function */ - int handle; /* Length of object's handle */ - int mintrail; /* Minimum trail length */ - double cor; /* Coefficient of Restitution. perfect bounce = 1 */ - double weight; /* Heavier objects don't get thrown as high */ -} ObjectDefs[] = { - { /* Ball */ - show_ball, - 0, - 1, - 0.9, - 1.0, - }, - { /* Club */ - show_europeanclub, - 15, - 1, - 0.55, /* Clubs don't bounce too well */ - 1.0, - }, - { /* Torch */ - show_torch, - 15, - 20, /* Torches need flames */ - 0, /* Torches don't bounce -- fire risk! */ - 1.0, - }, - { /* Knife */ - show_knife, - 15, - 1, - 0, /* Knives don't bounce */ - 1.0, - }, - { /* Ring */ - show_ring, - 15, - 1, - 0.8, - 1.0, - }, - { /* Bowling Ball */ - show_bball, - 0, - 1, - 0.2, - 5.0, - }, -}; - -/************************** - * Trajectory definitions * - **************************/ - -typedef enum {HEIGHT, ADAM} Notation; -typedef enum {Empty, Full, Ball} Throwable; -typedef enum {LEFT, RIGHT} Hand; -typedef enum {THROW, CATCH} Action; -typedef enum {HAND, ELBOW, SHOULDER} Joint; -typedef enum {ATCH, THRATCH, ACTION, LINKEDACTION, - PTHRATCH, BPREDICTOR, PREDICTOR} TrajectoryStatus; -typedef struct {double a, b, c, d; } Spline; -typedef DXPoint Arm[3]; - -/* A Wander contains a Spline and a time interval. A list of Wanders - * describes the performer's position as he moves around the screen. */ -typedef struct wander *WanderPtr; -typedef struct wander { - WanderPtr next, prev; - double x; - unsigned long finish; - Spline s; -#ifdef MEMTEST - char pad[1024]; -#endif -} Wander; - -/* Object is an arbitrary object being juggled. Each Trajectory - * references an Object ("count" tracks this), and each Object is also - * linked into a global Objects list. Objects may include a Trace - * list for tracking erasures. */ -typedef struct object *ObjectPtr; -typedef struct object { - ObjectPtr next, prev; - - ObjType type; - int color; - int count; /* reference count */ - Bool active; /* Object is in use */ - - Trace *trace; - int tracelen; - int tail; -#ifdef MEMTEST - char pad[1024]; -#endif -} Object; - -/* Trajectory is a segment of juggling action. A list of Trajectories - * defines the juggling performance. The Trajectory list goes through - * multiple processing steps to convert it from basic juggling - * notation into rendering data. */ - -typedef struct trajectory *TrajectoryPtr; -typedef struct trajectory { - TrajectoryPtr prev, next; /* for building list */ - TrajectoryStatus status; - - /* Throw */ - char posn; - int height; - int adam; - char *pattern; - char *name; - - /* Action */ - Hand hand; - Action action; - - /* LinkedAction */ - int color; - Object *object; - int divisions; - double angle, spin; - TrajectoryPtr balllink; - TrajectoryPtr handlink; - - /* PThratch */ - double cx; /* Moving juggler */ - double x, y; /* current position */ - double dx, dy; /* initial velocity */ - - /* Predictor */ - Throwable type; - unsigned long start, finish; - Spline xp, yp; - -#ifdef MEMTEST - char pad[1024]; -#endif -} Trajectory; - - -/******************* - * Pattern Library * - *******************/ - -typedef struct { - const char * pattern; - const char * name; -} patternstruct; - -/* List of popular patterns, in any order */ -/* Patterns should be given in Adam notation so the generator can - concatenate them safely. Null descriptions are ok. Height - notation will be displayed automatically. */ -/* Can't const this because it is qsorted. This *should* be reentrant, - I think... */ -static /*const*/ patternstruct portfolio[] = { - {"[+2 1]", /* +3 1 */ "Typical 2 ball juggler"}, - {"[2 0]", /* 4 0 */ "2 in 1 hand"}, - {"[2 0 1]", /* 5 0 1 */}, - {"[+2 0 +2 0 0]" /* +5 0 +5 0 0 */}, - {"[+2 0 1 2 2]", /* +4 0 1 2 3 */}, - {"[2 0 1 1]", /* 6 0 1 1 */}, - - {"[3]", /* 3 */ "3 cascade"}, - {"[+3]", /* +3 */ "reverse 3 cascade"}, - {"[=3]", /* =3 */ "cascade 3 under arm"}, - {"[&3]", /* &3 */ "cascade 3 catching under arm"}, - {"[_3]", /* _3 */ "bouncing 3 cascade"}, - {"[+3 x3 =3]", /* +3 x3 =3 */ "Mill's mess"}, - {"[3 2 1]", /* 5 3 1" */}, - {"[3 3 1]", /* 4 4 1" */}, - {"[3 1 2]", /* 6 1 2 */ "See-saw"}, - {"[=3 3 1 2]", /* =4 5 1 2 */}, - {"[=3 2 2 3 1 2]", /* =6 2 2 5 1 2 */ "=4 5 1 2 stretched"}, - {"[+3 3 1 3]", /* +4 4 1 3 */ "anemic shower box"}, - {"[3 3 1]", /* 4 4 1 */}, - {"[+3 2 3]", /* +4 2 3 */}, - {"[+3 1]", /* +5 1 */ "3 shower"}, - {"[_3 1]", /* _5 1 */ "bouncing 3 shower"}, - {"[3 0 3 0 3]", /* 5 0 5 0 5 */ "shake 3 out of 5"}, - {"[3 3 3 0 0]", /* 5 5 5 0 0 */ "flash 3 out of 5"}, - {"[3 3 0]", /* 4 5 0 */ "complete waste of a 5 ball juggler"}, - {"[3 3 3 0 0 0 0]", /* 7 7 7 0 0 0 0 */ "3 flash"}, - {"[+3 0 +3 0 +3 0 0]", /* +7 0 +7 0 +7 0 0 */}, - {"[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 */}, - {"[3 0 2 0]", /* 8 0 4 0 */}, - {"[_3 2 1]", /* _5 3 1 */}, - {"[_3 0 1]", /* _8 0 1 */}, - {"[1 _3 1 _3 0 1 _3 0]", /* 1 _7 1 _7 0 1 _7 0 */}, - {"[_3 2 1 _3 1 2 1]", /* _6 3 1 _6 1 3 1 */}, - - {"[4]", /* 4 */ "4 cascade"}, - {"[+4 3]", /* +5 3 */ "4 ball half shower"}, - {"[4 4 2]", /* 5 5 2 */}, - {"[+4 4 4 +4]", /* +4 4 4 +4 */ "4 columns"}, - {"[+4 3 +4]", /* +5 3 +4 */}, - {"[4 3 4 4]", /* 5 3 4 4 */}, - {"[4 3 3 4]", /* 6 3 3 4 */}, - {"[4 3 2 4", /* 6 4 2 4 */}, - {"[+4 1]", /* +7 1 */ "4 shower"}, - {"[4 4 4 4 0]", /* 5 5 5 5 0 */ "learning 5"}, - {"[+4 x4 =4]", /* +4 x4 =4 */ "Mill's mess for 4"}, - {"[+4 2 1 3]", /* +9 3 1 3 */}, - {"[4 4 1 4 1 4]", /* 6 6 1 5 1 5, by Allen Knutson */}, - {"[_4 _4 _4 1 _4 1]", /* _5 _6 _6 1 _5 1 */}, - {"[_4 3 3]", /* _6 3 3 */}, - {"[_4 3 1]", /* _7 4 1 */}, - {"[_4 2 1]", /* _8 3 1 */}, - {"[_4 3 3 3 0]", /* _8 4 4 4 0 */}, - {"[_4 1 3 1]", /* _9 1 5 1 */}, - {"[_4 1 3 1 2]", /* _10 1 6 1 2 */}, - - {"[5]", /* 5 */ "5 cascade"}, - {"[_5 _5 _5 _5 _5 5 5 5 5 5]", /* _5 _5 _5 _5 _5 5 5 5 5 5 */}, - {"[+5 x5 =5]", /* +5 x5 =5 */ "Mill's mess for 5"}, - {"[5 4 4]", /* 7 4 4 */}, - {"[_5 4 4]", /* _7 4 4 */}, - {"[1 2 3 4 5 5 5 5 5]", /* 1 2 3 4 5 6 7 8 9 */ "5 ramp"}, - {"[5 4 5 3 1]", /* 8 5 7 4 1, by Allen Knutson */}, - {"[_5 4 1 +4]", /* _9 5 1 5 */}, - {"[_5 4 +4 +4]", /* _8 4 +4 +4 */}, - {"[_5 4 4 4 1]", /* _9 5 5 5 1 */}, - {"[_5 4 4 5 1]",}, - {"[_5 4 4 +4 4 0]", /*_10 5 5 +5 5 0 */}, - - {"[6]", /* 6 */ "6 cascade"}, - {"[+6 5]", /* +7 5 */}, - {"[6 4]", /* 8 4 */}, - {"[+6 3]", /* +9 3 */}, - {"[6 5 4 4]", /* 9 7 4 4 */}, - {"[+6 5 5 5]", /* +9 5 5 5 */}, - {"[6 0 6]", /* 9 0 9 */}, - {"[_6 0 _6]", /* _9 0 _9 */}, - - {"[_7]", /* _7 */ "bouncing 7 cascade"}, - {"[7]", /* 7 */ "7 cascade"}, - {"[7 6 6 6 6]", /* 11 6 6 6 6 */ "Gatto's High Throw"}, - -}; - - - -typedef struct { int start; int number; } PatternIndex; - -struct patternindex { - int minballs; - int maxballs; - PatternIndex index[XtNumber(portfolio)]; -}; - - -/* Jugglestruct: per-screen global data. The master Wander, Object - * and Trajectory lists are anchored here. */ -typedef struct { - double scale; - Wander *wander; - double cx; - double Gr; - Trajectory *head; - Arm arm[2][2]; - char *pattern; - int count; - int num_balls; - time_t begintime; /* should make 'time' usable for at least 48 days - on a 32-bit machine */ - unsigned long time; /* millisecond timer*/ - ObjType objtypes; - Object *objects; - struct patternindex patternindex; - XFontStruct *mode_font; -} jugglestruct; - -static jugglestruct *juggles = (jugglestruct *) NULL; - -/******************* - * list management * - *******************/ - -#define DUP_OBJECT(n, t) { \ - (n)->object = (t)->object; \ - if((n)->object != NULL) (n)->object->count++; \ -} - -/* t must point to an existing element. t must not be an - expression ending ->next or ->prev */ -#define REMOVE(t) { \ - (t)->next->prev = (t)->prev; \ - (t)->prev->next = (t)->next; \ - free(t); \ -} - -/* t receives element to be created and added to the list. ot must - point to an existing element or be identical to t to start a new - list. Applicable to Trajectories, Objects and Traces. */ -#define ADD_ELEMENT(type, t, ot) \ - if (((t) = (type*)calloc(1,sizeof(type))) != NULL) { \ - (t)->next = (ot)->next; \ - (t)->prev = (ot); \ - (ot)->next = (t); \ - (t)->next->prev = (t); \ - } - -static void -object_destroy(Object* o) -{ - if(o->trace != NULL) { - while(o->trace->next != o->trace) { - Trace *s = o->trace->next; - REMOVE(s); /* Don't eliminate 's' */ - } - free(o->trace); - } - REMOVE(o); -} - -static void -trajectory_destroy(Trajectory *t) { - if(t->name != NULL) free(t->name); - if(t->pattern != NULL) free(t->pattern); - /* Reduce object link count and call destructor if necessary */ - if(t->object != NULL && --t->object->count < 1 && t->object->tracelen == 0) { - object_destroy(t->object); - } - REMOVE(t); /* Unlink and free */ -} - -ENTRYPOINT void -free_juggle(ModeInfo * mi) { - jugglestruct *sp = &juggles[MI_SCREEN(mi)]; - - if (sp->head != NULL) { - while (sp->head->next != sp->head) { - trajectory_destroy(sp->head->next); - } - free(sp->head); - sp->head = (Trajectory *) NULL; - } - if(sp->objects != NULL) { - while (sp->objects->next != sp->objects) { - object_destroy(sp->objects->next); - } - free(sp->objects); - sp->objects = (Object*)NULL; - } - if(sp->wander != NULL) { - while (sp->wander->next != sp->wander) { - Wander *w = sp->wander->next; - REMOVE(w); - } - free(sp->wander); - sp->wander = (Wander*)NULL; - } - if(sp->pattern != NULL) { - free(sp->pattern); - sp->pattern = NULL; - } - if (sp->mode_font!=None) { - XFreeFontInfo(NULL,sp->mode_font,1); - sp->mode_font = None; - } -} - -static Bool -add_throw(ModeInfo *mi, char type, int h, Notation n, const char* name) -{ - jugglestruct *sp = &juggles[MI_SCREEN(mi)]; - Trajectory *t; - - ADD_ELEMENT(Trajectory, t, sp->head->prev); - if(t == NULL){ /* Out of Memory */ - free_juggle(mi); - return False; - } - t->object = NULL; - if(name != NULL) - t->name = strdup(name); - t->posn = type; - if (n == ADAM) { - t->adam = h; - t->height = 0; - t->status = ATCH; - } else { - t->height = h; - t->status = THRATCH; - } - return True; -} - -/* add a Thratch to the performance */ -static Bool -program(ModeInfo *mi, const char *patn, const char *name, int cycles) -{ - const char *p; - int w, h, i, seen; - Notation notation; - char type; - - if (MI_IS_VERBOSE(mi)) { - (void) fprintf(stderr, "juggle[%d]: Programmed: %s x %d\n", - MI_SCREEN(mi), (name == NULL) ? patn : name, cycles); - } - - for(w=i=0; i < cycles; i++, w++) { /* repeat until at least "cycles" throws - have been programmed */ - /* title is the pattern name to be supplied to the first throw of - a sequence. If no name if given, use an empty title so that - the sequences are still delimited. */ - const char *title = (name != NULL)? name : ""; - type=' '; - h = 0; - seen = 0; - notation = HEIGHT; - for(p=patn; *p; p++) { - if (*p >= '0' && *p <='9') { - seen = 1; - h = 10*h + (*p - '0'); - } else { - Notation nn = notation; - switch (*p) { - case '[': /* begin Adam notation */ - notation = ADAM; - break; - case '-': /* Inside throw */ - type = ' '; - break; - case '+': /* Outside throw */ - case '=': /* Cross throw */ - case '&': /* Cross catch */ - case 'x': /* Cross throw and catch */ - case '_': /* Bounce */ - case 'k': /* Kickup */ - type = *p; - break; - case '*': /* Lose ball */ - seen = 1; - h = -1; - /* fall through */ - case ']': /* end Adam notation */ - nn = HEIGHT; - /* fall through */ - case ' ': - if (seen) { - i++; - if (!add_throw(mi, type, h, notation, title)) - return False; - title = NULL; - type=' '; - h = 0; - seen = 0; - } - notation = nn; - break; - default: - if(w == 0) { /* Only warn on first pass */ - (void) fprintf(stderr, - "juggle[%d]: Unexpected pattern instruction: '%c'\n", - MI_SCREEN(mi), *p); - } - break; - } - } - } - if (seen) { /* end of sequence */ - if (!add_throw(mi, type, h, notation, title)) - return False; - title = NULL; - } - } - return True; -} - -/* - ~~~~\~~~~~\~~~ - \\~\\~\~\\\~~~ - \\~\\\\~\\\~\~ - \\\\\\\\\\\~\\ - -[ 3 3 1 3 4 2 3 1 3 3 4 0 2 1 ] - -4 4 1 3 12 2 4 1 4 4 13 0 3 1 - -*/ -#define BOUNCEOVER 10 -#define KICKMIN 7 -#define THROWMAX 20 - -/* Convert Adam notation into heights */ -static void -adam(jugglestruct *sp) -{ - Trajectory *t, *p; - for(t = sp->head->next; t != sp->head; t = t->next) { - if (t->status == ATCH) { - int a = t->adam; - t->height = 0; - for(p = t->next; a > 0; p = p->next) { - if(p == sp->head) { - t->height = -9; /* Indicate end of processing for name() */ - return; - } - if (p->status != ATCH || p->adam < 0 || p->adam>= a) { - a--; - } - t->height++; - } - if(t->height > BOUNCEOVER && t->posn == ' '){ - t->posn = '_'; /* high defaults can be bounced */ - } else if(t->height < 3 && t->posn == '_') { - t->posn = ' '; /* Can't bounce short throws. */ - } - if(t->height < KICKMIN && t->posn == 'k'){ - t->posn = ' '; /* Can't kick short throws */ - } - if(t->height > THROWMAX){ - t->posn = 'k'; /* Use kicks for ridiculously high throws */ - } - t->status = THRATCH; - } - } -} - -/* Discover converted heights and update the sequence title */ -static void -name(jugglestruct *sp) -{ - Trajectory *t, *p; - char buffer[BUFSIZ]; - char *b; - for(t = sp->head->next; t != sp->head; t = t->next) { - if (t->status == THRATCH && t->name != NULL) { - b = buffer; - for(p = t; p == t || p->name == NULL; p = p->next) { - if(p == sp->head || p->height < 0) { /* end of reliable data */ - return; - } - if(p->posn == ' ') { - b += sprintf(b, " %d", p->height); - } else { - b += sprintf(b, " %c%d", p->posn, p->height); - } - if(b - buffer > 500) break; /* otherwise this could eventually - overflow. It'll be too big to - display anyway. */ - } - if(*t->name != 0) { - (void) sprintf(b, ", %s", t->name); - } - free(t->name); /* Don't need name any more, it's been converted - to pattern */ - t->name = NULL; - if(t->pattern != NULL) free(t->pattern); - t->pattern = strdup(buffer); - } - } -} - -/* Split Thratch notation into explicit throws and catches. - Usually Catch follows Throw in same hand, but take care of special - cases. */ - -/* ..n1.. -> .. LTn RT1 LC RC .. */ -/* ..nm.. -> .. LTn LC RTm RC .. */ - -static Bool -part(ModeInfo *mi) -{ - jugglestruct *sp = &juggles[MI_SCREEN(mi)]; - Trajectory *t, *nt, *p; - Hand hand = (LRAND() & 1) ? RIGHT : LEFT; - - for (t = sp->head->next; t != sp->head; t = t->next) { - if (t->status > THRATCH) { - hand = t->hand; - } else if (t->status == THRATCH) { - char posn = '='; - - /* plausibility check */ - if (t->height <= 2 && t->posn == '_') { - t->posn = ' '; /* no short bounces */ - } - if (t->height <= 1 && (t->posn == '=' || t->posn == '&')) { - t->posn = ' '; /* 1's need close catches */ - } - - switch (t->posn) { - /* throw catch */ - case ' ': posn = '-'; t->posn = '+'; break; - case '+': posn = '+'; t->posn = '-'; break; - case '=': posn = '='; t->posn = '+'; break; - case '&': posn = '+'; t->posn = '='; break; - case 'x': posn = '='; t->posn = '='; break; - case '_': posn = '_'; t->posn = '-'; break; - case 'k': posn = 'k'; t->posn = 'k'; break; - default: - (void) fprintf(stderr, "juggle: unexpected posn %c\n", t->posn); - break; - } - hand = (Hand) ((hand + 1) % 2); - t->status = ACTION; - t->hand = hand; - p = t->prev; - - if (t->height == 1 && p != sp->head) { - p = p->prev; /* '1's are thrown earlier than usual */ - } - - - - t->action = CATCH; - ADD_ELEMENT(Trajectory, nt, p); - if(nt == NULL){ - free_juggle(mi); - return False; - } - nt->object = NULL; - nt->status = ACTION; - nt->action = THROW; - nt->height = t->height; - nt->hand = hand; - nt->posn = posn; - - } - } - return True; -} - -static ObjType -choose_object(void) { - ObjType o; - for (;;) { - o = (ObjType)NRAND((ObjType)NUM_OBJECT_TYPES); - if(balls && o == BALL) break; - if(clubs && o == CLUB) break; - if(torches && o == TORCH) break; - if(knives && o == KNIFE) break; - if(rings && o == RING) break; - if(bballs && o == BBALLS) break; - } - return o; -} - -/* Connnect up throws and catches to figure out which ball goes where. - Do the same with the juggler's hands. */ - -static void -lob(ModeInfo *mi) -{ - jugglestruct *sp = &juggles[MI_SCREEN(mi)]; - Trajectory *t, *p; - int h; - for (t = sp->head->next; t != sp->head; t = t->next) { - if (t->status == ACTION) { - if (t->action == THROW) { - if (t->type == Empty) { - /* Create new Object */ - ADD_ELEMENT(Object, t->object, sp->objects); - t->object->count = 1; - t->object->tracelen = 0; - t->object->active = False; - /* Initialise object's circular trace list */ - ADD_ELEMENT(Trace, t->object->trace, t->object->trace); - - if (MI_NPIXELS(mi) > 2) { - t->object->color = 1 + NRAND(MI_NPIXELS(mi) - 2); - } else { -#ifdef STANDALONE - t->object->color = 1; -#else - t->object->color = 0; -#endif - } - - /* Small chance of picking a random object instead of the - current theme. */ - if(NRAND(OBJMIXPROB) == 0) { - t->object->type = choose_object(); - } else { - t->object->type = sp->objtypes; - } - - /* Check to see if we need trails for this object */ - if(tail < ObjectDefs[t->object->type].mintrail) { - t->object->tail = ObjectDefs[t->object->type].mintrail; - } else { - t->object->tail = tail; - } - } - - /* Balls can change divisions at each throw */ - t->divisions = 2 * (NRAND(2) + 1); - - /* search forward for next catch in this hand */ - for (p = t->next; t->handlink == NULL; p = p->next) { - if(p->status < ACTION || p == sp->head) return; - if (p->action == CATCH) { - if (t->handlink == NULL && p->hand == t->hand) { - t->handlink = p; - } - } - } - - if (t->height > 0) { - h = t->height - 1; - - /* search forward for next ball catch */ - for (p = t->next; t->balllink == NULL; p = p->next) { - if(p->status < ACTION || p == sp->head) { - t->handlink = NULL; - return; - } - if (p->action == CATCH) { - if (t->balllink == NULL && --h < 1) { /* caught */ - t->balllink = p; /* complete trajectory */ -# if 0 - if (p->type == Full) { - (void) fprintf(stderr, "juggle[%d]: Dropped %d\n", - MI_SCREEN(mi), t->object->color); - } -#endif - p->type = Full; - DUP_OBJECT(p, t); /* accept catch */ - p->angle = t->angle; - p->divisions = t->divisions; - } - } - } - } - t->type = Empty; /* thrown */ - } else if (t->action == CATCH) { - /* search forward for next throw from this hand */ - for (p = t->next; t->handlink == NULL; p = p->next) { - if(p->status < ACTION || p == sp->head) return; - if (p->action == THROW && p->hand == t->hand) { - p->type = t->type; /* pass ball */ - DUP_OBJECT(p, t); /* pass object */ - p->divisions = t->divisions; - t->handlink = p; - } - } - } - t->status = LINKEDACTION; - } - } -} - -/* Clap when both hands are empty */ -static void -clap(jugglestruct *sp) -{ - Trajectory *t, *p; - for (t = sp->head->next; t != sp->head; t = t->next) { - if (t->status == LINKEDACTION && - t->action == CATCH && - t->type == Empty && - t->handlink != NULL && - t->handlink->height == 0) { /* Completely idle hand */ - - for (p = t->next; p != sp->head; p = p->next) { - if (p->status == LINKEDACTION && - p->action == CATCH && - p->hand != t->hand) { /* Next catch other hand */ - if(p->type == Empty && - p->handlink != NULL && - p->handlink->height == 0) { /* Also completely idle */ - - t->handlink->posn = '^'; /* Move first hand's empty throw */ - p->posn = '^'; /* to meet second hand's empty - catch */ - - } - break; /* Only need first catch */ - } - } - } - } -} - -#define CUBIC(s, t) ((((s).a * (t) + (s).b) * (t) + (s).c) * (t) + (s).d) - -/* Compute single spline from x0 with velocity dx0 at time t0 to x1 - with velocity dx1 at time t1 */ -static Spline -makeSpline(double x0, double dx0, int t0, double x1, double dx1, int t1) -{ - Spline s; - double a, b, c, d; - double x10; - double t10; - - x10 = x1 - x0; - t10 = t1 - t0; - a = ((dx0 + dx1)*t10 - 2*x10) / (t10*t10*t10); - b = (3*x10 - (2*dx0 + dx1)*t10) / (t10*t10); - c = dx0; - d = x0; - s.a = a; - s.b = -3*a*t0 + b; - s.c = (3*a*t0 - 2*b)*t0 + c; - s.d = ((-a*t0 + b)*t0 - c)*t0 +d; - return s; -} - -/* Compute a pair of splines. s1 goes from x0 vith velocity dx0 at - time t0 to x1 at time t1. s2 goes from x1 at time t1 to x2 with - velocity dx2 at time t2. The arrival and departure velocities at - x1, t1 must be the same. */ -static double -makeSplinePair(Spline *s1, Spline *s2, - double x0, double dx0, int t0, - double x1, int t1, - double x2, double dx2, int t2) -{ - double x10, x21, t21, t10, t20, dx1; - x10 = x1 - x0; - x21 = x2 - x1; - t21 = t2 - t1; - t10 = t1 - t0; - t20 = t2 - t0; - dx1 = (3*x10*t21*t21 + 3*x21*t10*t10 + 3*dx0*t10*t21*t21 - - dx2*t10*t10*t21 - 4*dx0*t10*t21*t21) / - (2*t10*t21*t20); - *s1 = makeSpline(x0, dx0, t0, x1, dx1, t1); - *s2 = makeSpline(x1, dx1, t1, x2, dx2, t2); - return dx1; -} - -/* Compute a Ballistic path in a pair of degenerate splines. sx goes - from x at time t at constant velocity dx. sy goes from y at time t - with velocity dy and constant acceleration g. */ -static void -makeParabola(Trajectory *n, - double x, double dx, double y, double dy, double g) -{ - double t = (double)n->start; - n->xp.a = 0; - n->xp.b = 0; - n->xp.c = dx; - n->xp.d = -dx*t + x; - n->yp.a = 0; - n->yp.b = g/2; - n->yp.c = -g*t + dy; - n->yp.d = g/2*t*t - dy*t + y; -} - - - -/* Make juggler wander around the screen */ -static double wander(jugglestruct *sp, unsigned long time) -{ - Wander *w = NULL; - for (w = sp->wander->next; w != sp->wander; w = w->next) { - if (w->finish < sp->time) { /* expired */ - Wander *ww = w; - w = w->prev; - REMOVE(ww); - } else if(w->finish > time) { - break; - } - } - if(w == sp->wander) { /* Need a new one */ - ADD_ELEMENT(Wander, w, sp->wander->prev); - if(w == NULL) { /* Memory problem */ - return 0.0; - } - w->finish = time + 3*THROW_CATCH_INTERVAL + NRAND(10*THROW_CATCH_INTERVAL); - if(time == 0) { - w->x = 0; - } else { - w->x = w->prev->x * 0.9 + NRAND(40) - 20; - } - w->s = makeSpline(w->prev->x, 0.0, w->prev->finish, w->x, 0.0, w->finish); - } - return CUBIC(w->s, time); -} - -#define SX 25 /* Shoulder Width */ - -/* Convert hand position symbols into actual time/space coordinates */ -static void -positions(jugglestruct *sp) -{ - Trajectory *t; - unsigned long now = sp->time; /* Make sure we're not lost in the past */ - for (t = sp->head->next; t != sp->head; t = t->next) { - if (t->status >= PTHRATCH) { - now = t->start; - } else if (t->status == ACTION || t->status == LINKEDACTION) { - /* Allow ACTIONs to be annotated, but we won't mark them ready - for the next stage */ - - double xo = 0, yo; - double sx = SX; - double pose = SX/2; - - /* time */ - if (t->action == CATCH) { /* Throw-to-catch */ - if (t->type == Empty) { - now += (int) THROW_NULL_INTERVAL; /* failed catch is short */ - } else { /* successful catch */ - now += (int)(THROW_CATCH_INTERVAL); - } - } else { /* Catch-to-throw */ - if(t->object != NULL) { - now += (int) (CATCH_THROW_INTERVAL * - ObjectDefs[t->object->type].weight); - } else { - now += (int) (CATCH_THROW_INTERVAL); - } - } - - if(t->start == 0) - t->start = now; - else /* Concatenated performances may need clock resync */ - now = t->start; - - t->cx = wander(sp, t->start); - - /* space */ - yo = 90; - - /* Add room for the handle */ - if(t->action == CATCH && t->object != NULL) - yo -= ObjectDefs[t->object->type].handle; - - switch (t->posn) { - case '-': xo = sx - pose; break; - case '_': - case 'k': - case '+': xo = sx + pose; break; - case '~': - case '=': xo = - sx - pose; yo += pose; break; - case '^': xo = 0; yo += pose*2; break; /* clap */ - default: - (void) fprintf(stderr, "juggle: unexpected posn %c\n", t->posn); - break; - } - - t->angle = (((t->hand == LEFT) ^ - (t->posn == '+' || t->posn == '_' || t->posn == 'k' ))? - -1 : 1) * M_PI/2; - - t->x = t->cx + ((t->hand == LEFT) ? xo : -xo); - t->y = yo; - - /* Only mark complete if it was already linked */ - if(t->status == LINKEDACTION) { - t->status = PTHRATCH; - } - } - } -} - - -/* Private physics functions */ - -/* Compute the spin-rate for a trajectory. Different types of throw - (eg, regular thows, bounces, kicks, etc) have different spin - requirements. - - type = type of object - h = trajectory of throwing hand (throws), or next throwing hand (catches) - old = earlier spin to consider - dt = time span of this trajectory - height = height of ball throw or 0 if based on old spin - turns = full club turns required during this operation - togo = partial club turns required to match hands -*/ -static double -spinrate(ObjType type, Trajectory *h, double old, double dt, - int height, int turns, double togo) -{ - const int dir = (h->hand == LEFT) ^ (h->posn == '+')? -1 : 1; - - if(ObjectDefs[type].handle != 0) { /* Clubs */ - return (dir * turns * 2 * M_PI + togo) / dt; - } else if(height == 0) { /* Balls already spinning */ - return old/2; - } else { /* Balls */ - return dir * NRAND(height*10)/20/ObjectDefs[type].weight * 2 * M_PI / dt; - } -} - - -/* compute the angle at the end of a spinning trajectory */ -static double -end_spin(Trajectory *t) -{ - return t->angle + t->spin * (t->finish - t->start); -} - -/* Sets the initial angle of the catch following hand movement t to - the final angle of the throw n. Also sets the angle of the - subsequent throw to the same angle plus half a turn. */ -static void -match_spins_on_catch(Trajectory *t, Trajectory *n) -{ - if(ObjectDefs[t->balllink->object->type].handle == 0) { - t->balllink->angle = end_spin(n); - if(t->balllink->handlink != NULL) { - t->balllink->handlink->angle = t->balllink->angle + M_PI; - } - } -} - -static double -find_bounce(jugglestruct *sp, - double yo, double yf, double yc, double tc, double cor) -{ - double tb, i, dy = 0; - const double e = 1; /* permissible error in yc */ - - /* - tb = time to bounce - yt = height at catch time after one bounce - one or three roots according to timing - find one by interval bisection - */ - tb = tc; - for(i = tc / 2; i > 0.0001; i/=2){ - double dt, yt; - if(tb == 0){ - (void) fprintf(stderr, "juggle: bounce div by zero!\n"); - break; - } - dy = (yf - yo)/tb + sp->Gr/2*tb; - dt = tc - tb; - yt = -cor*dy*dt + sp->Gr/2*dt*dt + yf; - if(yt < yc + e){ - tb-=i; - }else if(yt > yc - e){ - tb+=i; - }else{ - break; - } - } - if(dy*THROW_CATCH_INTERVAL < -200) { /* bounce too hard */ - tb = -1; - } - return tb; -} - -static Trajectory* -new_predictor(const Trajectory *t, int start, int finish, double angle) -{ - Trajectory *n; - ADD_ELEMENT(Trajectory, n, t->prev); - if(n == NULL){ - return NULL; - } - DUP_OBJECT(n, t); - n->divisions = t->divisions; - n->type = Ball; - n->status = PREDICTOR; - - n->start = start; - n->finish = finish; - n->angle = angle; - return n; -} - -/* Turn abstract timings into physically appropriate object trajectories. */ -static Bool -projectile(jugglestruct *sp) -{ - Trajectory *t; - const int yf = 0; /* Floor height */ - - for (t = sp->head->next; t != sp->head; t = t->next) { - if (t->status != PTHRATCH || t->action != THROW) { - continue; - } else if (t->balllink == NULL) { /* Zero Throw */ - t->status = BPREDICTOR; - } else if (t->balllink->handlink == NULL) { /* Incomplete */ - return True; - } else if(t->balllink == t->handlink) { - /* '2' height - hold on to ball. Don't need to consider - flourishes, 'hands' will do that automatically anyway */ - - t->type = Full; - /* Zero spin to avoid wrist injuries */ - t->spin = 0; - match_spins_on_catch(t, t); - t->dx = t->dy = 0; - t->status = BPREDICTOR; - continue; - } else { - if (t->posn == '_') { /* Bounce once */ - - const int tb = t->start + - find_bounce(sp, t->y, (double) yf, t->balllink->y, - (double) (t->balllink->start - t->start), - ObjectDefs[t->object->type].cor); - - if(tb < t->start) { /* bounce too hard */ - t->posn = '+'; /* Use regular throw */ - } else { - Trajectory *n; /* First (throw) trajectory. */ - double dt; /* Time span of a trajectory */ - double dy; /* Distance span of a follow-on trajectory. - First trajectory uses t->dy */ - /* dx is constant across both trajectories */ - t->dx = (t->balllink->x - t->x) / (t->balllink->start - t->start); - - { /* ball follows parabola down */ - n = new_predictor(t, t->start, tb, t->angle); - if(n == NULL) return False; - dt = n->finish - n->start; - /* Ball rate 4, no flight or matching club turns */ - n->spin = spinrate(t->object->type, t, 0.0, dt, 4, 0, 0.0); - t->dy = (yf - t->y)/dt - sp->Gr/2*dt; - makeParabola(n, t->x, t->dx, t->y, t->dy, sp->Gr); - } - - { /* ball follows parabola up */ - Trajectory *m = new_predictor(t, n->finish, t->balllink->start, - end_spin(n)); - if(m == NULL) return False; - dt = m->finish - m->start; - /* Use previous ball rate, no flight club turns */ - m->spin = spinrate(t->object->type, t, n->spin, dt, 0, 0, - t->balllink->angle - m->angle); - match_spins_on_catch(t, m); - dy = (t->balllink->y - yf)/dt - sp->Gr/2 * dt; - makeParabola(m, t->balllink->x - t->dx * dt, - t->dx, (double) yf, dy, sp->Gr); - } - - t->status = BPREDICTOR; - continue; - } - } else if (t->posn == 'k') { /* Drop & Kick */ - Trajectory *n; /* First (drop) trajectory. */ - Trajectory *o; /* Second (rest) trajectory */ - Trajectory *m; /* Third (kick) trajectory */ - const int td = t->start + 2*THROW_CATCH_INTERVAL; /* Drop time */ - const int tk = t->balllink->start - 5*THROW_CATCH_INTERVAL; /* Kick */ - double dt, dy; - - { /* Fall to ground */ - n = new_predictor(t, t->start, td, t->angle); - if(n == NULL) return False; - dt = n->finish - n->start; - /* Ball spin rate 4, no flight club turns */ - n->spin = spinrate(t->object->type, t, 0.0, dt, 4, 0, - t->balllink->angle - n->angle); - t->dx = (t->balllink->x - t->x) / dt; - t->dy = (yf - t->y)/dt - sp->Gr/2*dt; - makeParabola(n, t->x, t->dx, t->y, t->dy, sp->Gr); - } - - { /* Rest on ground */ - o = new_predictor(t, n->finish, tk, end_spin(n)); - if(o == NULL) return False; - o->spin = 0; - makeParabola(o, t->balllink->x, 0.0, (double) yf, 0.0, 0.0); - } - - /* Kick up */ - { - m = new_predictor(t, o->finish, t->balllink->start, end_spin(o)); - if(m == NULL) return False; - dt = m->finish - m->start; - /* Match receiving hand, ball rate 4, one flight club turn */ - m->spin = spinrate(t->object->type, t->balllink->handlink, 0.0, dt, - 4, 1, t->balllink->angle - m->angle); - match_spins_on_catch(t, m); - dy = (t->balllink->y - yf)/dt - sp->Gr/2 * dt; - makeParabola(m, t->balllink->x, 0.0, (double) yf, dy, sp->Gr); - } - - t->status = BPREDICTOR; - continue; - } - - /* Regular flight, no bounce */ - { /* ball follows parabola */ - double dt; - Trajectory *n = new_predictor(t, t->start, - t->balllink->start, t->angle); - if(n == NULL) return False; - dt = t->balllink->start - t->start; - /* Regular spin */ - n->spin = spinrate(t->object->type, t, 0.0, dt, t->height, t->height/2, - t->balllink->angle - n->angle); - match_spins_on_catch(t, n); - t->dx = (t->balllink->x - t->x) / dt; - t->dy = (t->balllink->y - t->y) / dt - sp->Gr/2 * dt; - makeParabola(n, t->x, t->dx, t->y, t->dy, sp->Gr); - } - - t->status = BPREDICTOR; - } - } - return True; -} - -/* Turn abstract hand motions into cubic splines. */ -static void -hands(jugglestruct *sp) -{ - Trajectory *t, *u, *v; - - for (t = sp->head->next; t != sp->head; t = t->next) { - /* no throw => no velocity */ - if (t->status != BPREDICTOR) { - continue; - } - - u = t->handlink; - if (u == NULL) { /* no next catch */ - continue; - } - v = u->handlink; - if (v == NULL) { /* no next throw */ - continue; - } - - /* double spline takes hand from throw, thru catch, to - next throw */ - - t->finish = u->start; - t->status = PREDICTOR; - - u->finish = v->start; - u->status = PREDICTOR; - - - /* FIXME: These adjustments leave a small glitch when alternating - balls and clubs. Just hope no-one notices. :-) */ - - /* make sure empty hand spin matches the thrown object in case it - had a handle */ - - t->spin = ((t->hand == LEFT)? -1 : 1 ) * - fabs((u->angle - t->angle)/(u->start - t->start)); - - u->spin = ((v->hand == LEFT) ^ (v->posn == '+')? -1 : 1 ) * - fabs((v->angle - u->angle)/(v->start - u->start)); - - (void) makeSplinePair(&t->xp, &u->xp, - t->x, t->dx, t->start, - u->x, u->start, - v->x, v->dx, v->start); - (void) makeSplinePair(&t->yp, &u->yp, - t->y, t->dy, t->start, - u->y, u->start, - v->y, v->dy, v->start); - - t->status = PREDICTOR; - } -} - -/* Given target x, y find_elbow puts hand at target if possible, - * otherwise makes hand point to the target */ -static void -find_elbow(int armlength, DXPoint *h, DXPoint *e, DXPoint *p, DXPoint *s, - int z) -{ - double r, h2, t; - double x = p->x - s->x; - double y = p->y - s->y; - h2 = x*x + y*y + z*z; - if (h2 > 4 * armlength * armlength) { - t = armlength/sqrt(h2); - e->x = t*x + s->x; - e->y = t*y + s->y; - h->x = 2 * t * x + s->x; - h->y = 2 * t * y + s->y; - } else { - r = sqrt((double)(x*x + z*z)); - t = sqrt(4 * armlength * armlength / h2 - 1); - e->x = x*(1 + y*t/r)/2 + s->x; - e->y = (y - r*t)/2 + s->y; - h->x = x + s->x; - h->y = y + s->y; - } -} - - -/* NOTE: returned x, y adjusted for arm reach */ -static void -reach_arm(ModeInfo * mi, Hand side, DXPoint *p) -{ - jugglestruct *sp = &juggles[MI_SCREEN(mi)]; - DXPoint h, e; - find_elbow(40, &h, &e, p, &sp->arm[1][side][SHOULDER], 25); - *p = sp->arm[1][side][HAND] = h; - sp->arm[1][side][ELBOW] = e; -} - -#if DEBUG -/* dumps a human-readable rendition of the current state of the juggle - pipeline to stderr for debugging */ -static void -dump(jugglestruct *sp) -{ - Trajectory *t; - for (t = sp->head->next; t != sp->head; t = t->next) { - switch (t->status) { - case ATCH: - (void) fprintf(stderr, "%p a %c%d\n", (void*)t, t->posn, t->adam); - break; - case THRATCH: - (void) fprintf(stderr, "%p T %c%d %s\n", (void*)t, t->posn, t->height, - t->pattern == NULL?"":t->pattern); - break; - case ACTION: - if (t->action == CATCH) - (void) fprintf(stderr, "%p A %c%cC\n", - (void*)t, t->posn, - t->hand ? 'R' : 'L'); - else - (void) fprintf(stderr, "%p A %c%c%c%d\n", - (void*)t, t->posn, - t->hand ? 'R' : 'L', - (t->action == THROW)?'T':'N', - t->height); - break; - case LINKEDACTION: - (void) fprintf(stderr, "%p L %c%c%c%d %d %p %p\n", - (void*)t, t->posn, - t->hand?'R':'L', - (t->action == THROW)?'T':(t->action == CATCH?'C':'N'), - t->height, t->object == NULL?0:t->object->color, - (void*)t->handlink, (void*)t->balllink); - break; - case PTHRATCH: - (void) fprintf(stderr, "%p O %c%c%c%d %d %2d %6lu %6lu\n", - (void*)t, t->posn, - t->hand?'R':'L', - (t->action == THROW)?'T':(t->action == CATCH?'C':'N'), - t->height, t->type, t->object == NULL?0:t->object->color, - t->start, t->finish); - break; - case BPREDICTOR: - (void) fprintf(stderr, "%p B %c %2d %6lu %6lu %g\n", - (void*)t, t->type == Ball?'b':t->type == Empty?'e':'f', - t->object == NULL?0:t->object->color, - t->start, t->finish, t->yp.c); - break; - case PREDICTOR: - (void) fprintf(stderr, "%p P %c %2d %6lu %6lu %g\n", - (void*)t, t->type == Ball?'b':t->type == Empty?'e':'f', - t->object == NULL?0:t->object->color, - t->start, t->finish, t->yp.c); - break; - default: - (void) fprintf(stderr, "%p: status %d not implemented\n", - (void*)t, t->status); - break; - } - } - (void) fprintf(stderr, "---\n"); -} -#endif - -static int get_num_balls(const char *j) -{ - int balls = 0; - const char *p; - int h = 0; - if (!j) abort(); - for (p = j; *p; p++) { - if (*p >= '0' && *p <='9') { /* digit */ - h = 10*h + (*p - '0'); - } else { - if (h > balls) { - balls = h; - } - h = 0; - } - } - return balls; -} - -#ifdef __cplusplus -extern "C" { -#endif - -static int -compare_num_balls(const void *p1, const void *p2) -{ - int i, j; - i = get_num_balls(((patternstruct*)p1)->pattern); - j = get_num_balls(((patternstruct*)p2)->pattern); - if (i > j) { - return (1); - } else if (i < j) { - return (-1); - } else { - return (0); - } -} - -#ifdef __cplusplus -} -#endif - - -/************************************************************************** - * Rendering Functions * - * * - **************************************************************************/ - -static void -show_arms(ModeInfo * mi, unsigned long color) -{ - jugglestruct *sp = &juggles[MI_SCREEN(mi)]; - unsigned int i, j; - Hand side; - XPoint a[XtNumber(sp->arm[0][0])]; - if(color == MI_BLACK_PIXEL(mi)) { - j = 0; - } else { - j = 1; - } - XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), - ARMWIDTH, LineSolid, CapRound, JoinRound); - XSetForeground(MI_DISPLAY(mi), MI_GC(mi), color); - for(side = LEFT; side <= RIGHT; side = (Hand)((int)side + 1)) { - /* Translate into device coords */ - for(i = 0; i < XtNumber(a); i++) { - a[i].x = (short)(MI_WIDTH(mi)/2 + sp->arm[j][side][i].x*sp->scale); - a[i].y = (short)(MI_HEIGHT(mi) - sp->arm[j][side][i].y*sp->scale); - if(j == 1) - sp->arm[0][side][i] = sp->arm[1][side][i]; - } - XDrawLines(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - a, XtNumber(a), CoordModeOrigin); - } -} - -static void -show_figure(ModeInfo * mi, unsigned long color, Bool init) -{ - jugglestruct *sp = &juggles[MI_SCREEN(mi)]; - XPoint p[7]; - unsigned int i; - - /* +-----+ 9 - | 6 | - 10 +--+--+ - 2 +---+---+ 3 - \ 5 / - \ / - \ / - 1 + - / \ - / \ - 0 +-----+ 4 - | | - | | - | | - 7 + + 8 - */ - - static const XPoint figure[] = { - { 15, 70}, /* 0 Left Hip */ - { 0, 90}, /* 1 Waist */ - { SX, 130}, /* 2 Left Shoulder */ - {-SX, 130}, /* 3 Right Shoulder */ - {-15, 70}, /* 4 Right Hip */ - { 0, 130}, /* 5 Neck */ - { 0, 140}, /* 6 Chin */ - { SX, 0}, /* 7 Left Foot */ - {-SX, 0}, /* 8 Right Foot */ - {-17, 174}, /* 9 Head1 */ - { 17, 140}, /* 10 Head2 */ - }; - XPoint a[XtNumber(figure)]; - - /* Translate into device coords */ - for(i = 0; i < XtNumber(figure); i++) { - a[i].x = (short)(MI_WIDTH(mi)/2 + (sp->cx + figure[i].x)*sp->scale); - a[i].y = (short)(MI_HEIGHT(mi) - figure[i].y*sp->scale); - } - - XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), - ARMWIDTH, LineSolid, CapRound, JoinRound); - XSetForeground(MI_DISPLAY(mi), MI_GC(mi), color); - - i = 0; /* Body */ - p[i++] = a[0]; p[i++] = a[1]; p[i++] = a[2]; - p[i++] = a[3]; p[i++] = a[1]; p[i++] = a[4]; - XDrawLines(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - p, i, CoordModeOrigin); - - i = 0; /* Legs */ - p[i++] = a[7]; p[i++] = a[0]; p[i++] = a[4]; p[i++] = a[8]; - XDrawLines(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - p, i, CoordModeOrigin); - - i = 0; /* Neck */ - p[i++] = a[5]; p[i++] = a[6]; - XDrawLines(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - p, i, CoordModeOrigin); - - /* Head */ - XDrawArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - a[9].x, a[9].y, - a[10].x - a[9].x, a[10].y - a[9].y, 0, 64*360); - sp->arm[1][LEFT][SHOULDER].x = sp->cx + figure[2].x; - sp->arm[1][RIGHT][SHOULDER].x = sp->cx + figure[3].x; - if(init) { - /* Initialise arms */ - unsigned int i; - for(i = 0; i < 2; i++){ - sp->arm[i][LEFT][SHOULDER].y = figure[2].y; - sp->arm[i][LEFT][ELBOW].x = figure[2].x; - sp->arm[i][LEFT][ELBOW].y = figure[1].y; - sp->arm[i][LEFT][HAND].x = figure[0].x; - sp->arm[i][LEFT][HAND].y = figure[1].y; - sp->arm[i][RIGHT][SHOULDER].y = figure[3].y; - sp->arm[i][RIGHT][ELBOW].x = figure[3].x; - sp->arm[i][RIGHT][ELBOW].y = figure[1].y; - sp->arm[i][RIGHT][HAND].x = figure[4].x; - sp->arm[i][RIGHT][HAND].y = figure[1].y; - } - } -} - -static void -show_ball(ModeInfo *mi, unsigned long color, Trace *s) -{ - jugglestruct *sp = &juggles[MI_SCREEN(mi)]; - int offset = (int)(s->angle*64*180/M_PI); - short x = (short)(MI_WIDTH(mi)/2 + s->x * sp->scale); - short y = (short)(MI_HEIGHT(mi) - s->y * sp->scale); - - /* Avoid wrapping */ - if(s->y*sp->scale > MI_HEIGHT(mi) * 2) return; - - XSetForeground(MI_DISPLAY(mi), MI_GC(mi), color); - if (s->divisions == 0 || color == MI_BLACK_PIXEL(mi)) { - XFillArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - x - BALLRADIUS, y - BALLRADIUS, - 2*BALLRADIUS, 2*BALLRADIUS, - 0, 23040); - } else if (s->divisions == 4) { /* 90 degree divisions */ - XFillArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - x - BALLRADIUS, y - BALLRADIUS, - 2*BALLRADIUS, 2*BALLRADIUS, - offset % 23040, 5760); - XFillArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - x - BALLRADIUS, y - BALLRADIUS, - 2*BALLRADIUS, 2*BALLRADIUS, - (offset + 11520) % 23040, 5760); - - XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi)); - XFillArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - x - BALLRADIUS, y - BALLRADIUS, - 2*BALLRADIUS, 2*BALLRADIUS, - (offset + 5760) % 23040, 5760); - XFillArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - x - BALLRADIUS, y - BALLRADIUS, - 2*BALLRADIUS, 2*BALLRADIUS, - (offset + 17280) % 23040, 5760); - } else if (s->divisions == 2) { /* 180 degree divisions */ - XFillArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - x - BALLRADIUS, y - BALLRADIUS, - 2*BALLRADIUS, 2*BALLRADIUS, - offset % 23040, 11520); - - XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi)); - XFillArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - x - BALLRADIUS, y - BALLRADIUS, - 2*BALLRADIUS, 2*BALLRADIUS, - (offset + 11520) % 23040, 11520); - } else { - (void) fprintf(stderr, "juggle[%d]: unexpected divisions: %d\n", - MI_SCREEN(mi), s->divisions); - } -} - -static void -show_europeanclub(ModeInfo *mi, unsigned long color, Trace *s) -{ - jugglestruct *sp = &juggles[MI_SCREEN(mi)]; - XPoint p[4]; - const double sa = sin(s->angle); - const double ca = cos(s->angle); - unsigned int i; - - /* 6 6 - +-+ - / \ - 4 +-----+ 7 - ////////\ - 3 +---------+ 8 - 2 +---------+ 9 - |///////| - 1 +-------+ 10 - | | - | | - | | - | | - | | - | | - +-+ - 0 11 */ - - static const XPoint club[] = { - {-24, 2}, /* 0 */ - {-10, 3}, /* 1 */ - { 1, 6}, /* 2 */ - { 8, 6}, /* 3 */ - { 14, 4}, /* 4 */ - { 16, 3}, /* 5 */ - { 16,-3}, /* 6 */ - { 14,-4}, /* 7 */ - { 8,-6}, /* 8 */ - { 1,-6}, /* 9 */ - {-10,-3}, /* 10 */ - {-24,-2}, /* 11 */ - {-24, 2}, /* 0 close boundary */ - }; - XPoint a[XtNumber(club)]; - - /* Avoid wrapping */ - if(s->y*sp->scale > MI_HEIGHT(mi) * 2) return; - - /* Translate and fake perspective */ - for(i = 0; i < XtNumber(club); i++) { - a[i].x = (short)(MI_WIDTH(mi)/2 + - (s->x + club[i].x*PERSPEC*sa)*sp->scale - - club[i].y*sqrt(sp->scale)*ca); - a[i].y = (short)(MI_HEIGHT(mi) - (s->y - club[i].x*ca)*sp->scale + - club[i].y*sa*sqrt(sp->scale)); - } - - if(color != MI_BLACK_PIXEL(mi)) { - /* Outline in black */ - XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi)); - XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 2, - LineSolid, CapRound, JoinRound); - XDrawLines(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - a, XtNumber(a), CoordModeOrigin); - } - - XSetForeground(MI_DISPLAY(mi), MI_GC(mi), color); - - /* Don't be tempted to optimize erase by drawing all the black in - one X operation. It must use the same ops as the colours to - guarantee a clean erase. */ - - i = 0; /* Colored stripes */ - p[i++] = a[1]; p[i++] = a[2]; - p[i++] = a[9]; p[i++] = a[10]; - XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - p, i, Convex, CoordModeOrigin); - i = 0; - p[i++] = a[3]; p[i++] = a[4]; - p[i++] = a[7]; p[i++] = a[8]; - XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - p, i, Convex, CoordModeOrigin); - - if(color != MI_BLACK_PIXEL(mi)) { - XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi)); - } - - i = 0; /* White center band */ - p[i++] = a[2]; p[i++] = a[3]; p[i++] = a[8]; p[i++] = a[9]; - XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - p, i, Convex, CoordModeOrigin); - - i = 0; /* White handle */ - p[i++] = a[0]; p[i++] = a[1]; p[i++] = a[10]; p[i++] = a[11]; - XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - p, i, Convex, CoordModeOrigin); - - i = 0; /* White tip */ - p[i++] = a[4]; p[i++] = a[5]; p[i++] = a[6]; p[i++] = a[7]; - XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - p, i, Convex, CoordModeOrigin); -} - -#if 0 -static void -show_jugglebugclub(ModeInfo *mi, unsigned long color, Trace *s) -{ - jugglestruct *sp = &juggles[MI_SCREEN(mi)]; - XPoint p[6]; - const double sa = sin(s->angle); - const double ca = cos(s->angle); - unsigned int i; - - /* 4 5 - +-+ - / \ - 3 +-----+ 6 - ////////\ - 2 +/////////+ 7 - |///////| - 1 +-------+ 8 - | | - | | - | | - | | - | | - | | - +-+ - 0 9 */ - - static const XPoint club[] = { - {-24, 2}, /* 0 */ - { -9, 3}, /* 1 */ - { 5, 6}, /* 2 */ - { 11, 4}, /* 3 */ - { 16, 3}, /* 4 */ - { 16,-3}, /* 5 */ - { 11,-4}, /* 6 */ - { 5,-6}, /* 7 */ - { -9,-3}, /* 8 */ - {-24,-2}, /* 9 */ - {-24, 2}, /* 0 close boundary */ - }; - XPoint a[XtNumber(club)]; - - /* Avoid wrapping */ - if(s->y*sp->scale > MI_HEIGHT(mi) * 2) return; - - /* Translate and fake perspective */ - for(i = 0; i < XtNumber(club); i++) { - a[i].x = (short)(MI_WIDTH(mi)/2 + - (s->x + club[i].x*PERSPEC*sa)*sp->scale - - club[i].y*sqrt(sp->scale)*ca); - a[i].y = (short)(MI_HEIGHT(mi) - (s->y - club[i].x*ca)*sp->scale + - club[i].y*sa*sqrt(sp->scale)); - } - - if(color != MI_BLACK_PIXEL(mi)) { - /* Outline in black */ - XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi)); - XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 2, - LineSolid, CapRound, JoinRound); - XDrawLines(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - a, XtNumber(a), CoordModeOrigin); - } - - XSetForeground(MI_DISPLAY(mi), MI_GC(mi), color); - - /* Don't be tempted to optimize erase by drawing all the black in - one X operation. It must use the same ops as the colours to - guarantee a clean erase. */ - - i = 0; /* Coloured center band */ - p[i++] = a[1]; p[i++] = a[2]; p[i++] = a[3]; - p[i++] = a[6]; p[i++] = a[7]; p[i++] = a[8]; - XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - p, i, Convex, CoordModeOrigin); - - if(color != MI_BLACK_PIXEL(mi)) { - XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi)); - } - - i = 0; /* White handle */ - p[i++] = a[0]; p[i++] = a[1]; p[i++] = a[8]; p[i++] = a[9]; - XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - p, i, Convex, CoordModeOrigin); - - i = 0; /* White tip */ - p[i++] = a[3]; p[i++] = a[4]; p[i++] = a[5]; p[i++] = a[6]; - XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - p, i, Convex, CoordModeOrigin); -} -#endif - -static void -show_torch(ModeInfo *mi, unsigned long color, Trace *s) -{ - jugglestruct *sp = &juggles[MI_SCREEN(mi)]; - XPoint head, tail, last; - DXPoint dhead, dlast; - const double sa = sin(s->angle); - const double ca = cos(s->angle); - - const double TailLen = -24; - const double HeadLen = 16; - const short Width = (short)(5 * sqrt(sp->scale)); - - /* - +///+ head - last | - | - | - | - | - + tail - */ - - dhead.x = s->x + HeadLen * PERSPEC * sa; - dhead.y = s->y - HeadLen * ca; - - if(color == MI_BLACK_PIXEL(mi)) { /* Use 'last' when erasing */ - dlast = s->dlast; - } else { /* Store 'last' so we can use it later when s->prev has - gone */ - if(s->prev != s->next) { - dlast.x = s->prev->x + HeadLen * PERSPEC * sin(s->prev->angle); - dlast.y = s->prev->y - HeadLen * cos(s->prev->angle); - } else { - dlast = dhead; - } - s->dlast = dlast; - } - - /* Avoid wrapping (after last is stored) */ - if(s->y*sp->scale > MI_HEIGHT(mi) * 2) return; - - head.x = (short)(MI_WIDTH(mi)/2 + dhead.x*sp->scale); - head.y = (short)(MI_HEIGHT(mi) - dhead.y*sp->scale); - - last.x = (short)(MI_WIDTH(mi)/2 + dlast.x*sp->scale); - last.y = (short)(MI_HEIGHT(mi) - dlast.y*sp->scale); - - tail.x = (short)(MI_WIDTH(mi)/2 + - (s->x + TailLen * PERSPEC * sa)*sp->scale ); - tail.y = (short)(MI_HEIGHT(mi) - (s->y - TailLen * ca)*sp->scale ); - - if(color != MI_BLACK_PIXEL(mi)) { - XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi)); - XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), - Width, LineSolid, CapRound, JoinRound); - XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - head.x, head.y, tail.x, tail.y); - } - XSetForeground(MI_DISPLAY(mi), MI_GC(mi), color); - XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), - Width * 2, LineSolid, CapRound, JoinRound); - - XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - head.x, head.y, last.x, last.y); - -} - -static void -show_knife(ModeInfo *mi, unsigned long color, Trace *s) -{ - jugglestruct *sp = &juggles[MI_SCREEN(mi)]; - unsigned int i; - const double sa = sin(s->angle); - const double ca = cos(s->angle); - - /* - 2 + - |+ 3 - || - 1 +++ 5 - |4| - | | - + 0 - */ - static const XPoint knife[] = { - {-24, 0}, /* 0 */ - { -5,-3}, /* 1 */ - { 16,-3}, /* 2 */ - { 12, 0}, /* 3 */ - { -5, 0}, /* 4 */ - { -5, 3}, /* 5 */ - }; - XPoint a[XtNumber(knife)], p[5]; - - /* Avoid wrapping */ - if(s->y*sp->scale > MI_HEIGHT(mi) * 2) return; - - /* Translate and fake perspective */ - for(i = 0; i < XtNumber(knife); i++) { - a[i].x = (short)(MI_WIDTH(mi)/2 + - (s->x + knife[i].x*PERSPEC*sa)*sp->scale - - knife[i].y*sqrt(sp->scale)*ca*PERSPEC); - a[i].y = (short)(MI_HEIGHT(mi) - (s->y - knife[i].x*ca)*sp->scale + - knife[i].y*sa*sqrt(sp->scale)); - } - - /* Handle */ - XSetForeground(MI_DISPLAY(mi), MI_GC(mi), color); - XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), (short)(4*sqrt(sp->scale)), - LineSolid, CapRound, JoinRound); - XDrawLine(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - a[0].x, a[0].y, a[4].x, a[4].y); - - /* Blade */ - if(color != MI_BLACK_PIXEL(mi)) { - XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi)); - } - i = 0; - p[i++] = a[1]; p[i++] = a[2]; p[i++] = a[3]; p[i++] = a[5]; - XFillPolygon(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - p, i, Convex, CoordModeOrigin); -} - -static void -show_ring(ModeInfo *mi, unsigned long color, Trace *s) -{ - jugglestruct *sp = &juggles[MI_SCREEN(mi)]; - short x = (short)(MI_WIDTH(mi)/2 + s->x * sp->scale); - short y = (short)(MI_HEIGHT(mi) - s->y * sp->scale); - double radius = 15 * sp->scale; - short thickness = (short)(8 * sqrt(sp->scale)); - - /* Avoid wrapping */ - if(s->y*sp->scale > MI_HEIGHT(mi) * 2) return; - - XSetForeground(MI_DISPLAY(mi), MI_GC(mi), color); - XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), - thickness, LineSolid, CapRound, JoinRound); - - XDrawArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - (short)(x - radius*PERSPEC), (short)(y - radius), - (short)(2*radius*PERSPEC), (short)(2*radius), - 0, 23040); -} - - -static void -show_bball(ModeInfo *mi, unsigned long color, Trace *s) -{ - jugglestruct *sp = &juggles[MI_SCREEN(mi)]; - short x = (short)(MI_WIDTH(mi)/2 + s->x * sp->scale); - short y = (short)(MI_HEIGHT(mi) - s->y * sp->scale); - double radius = 12 * sp->scale; - int offset = (int)(s->angle*64*180/M_PI); - int holesize = (int)(3.0*sqrt(sp->scale)); - - /* Avoid wrapping */ - if(s->y*sp->scale > MI_HEIGHT(mi) * 2) return; - - XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi)); - XFillArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - (short)(x - radius), (short)(y - radius), - (short)(2*radius), (short)(2*radius), - 0, 23040); - XSetForeground(MI_DISPLAY(mi), MI_GC(mi), color); - XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), 2, - LineSolid, CapRound, JoinRound); - XDrawArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - (short)(x - radius), (short)(y - radius), - (short)(2*radius), (short)(2*radius), - 0, 23040); - - /* Draw finger holes */ - XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi), holesize, - LineSolid, CapRound, JoinRound); - - XDrawArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - (short)(x - radius*0.5), (short)(y - radius*0.5), - (short)(2*radius*0.5), (short)(2*radius*0.5), - (offset + 960) % 23040, 0); - XDrawArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - (short)(x - radius*0.7), (short)(y - radius*0.7), - (short)(2*radius*0.7), (short)(2*radius*0.7), - (offset + 1920) % 23040, 0); - XDrawArc(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - (short)(x - radius*0.7), (short)(y - radius*0.7), - (short)(2*radius*0.7), (short)(2*radius*0.7), - offset % 23040, 0); -} - -/************************************************************************** - * Public Functions * - * * - **************************************************************************/ - - -/* FIXME: refill_juggle currently just appends new throws to the - * programme. This is fine if the programme is empty, but if there - * are still some trajectories left then it really should take these - * into account */ - -static void -refill_juggle(ModeInfo * mi) -{ - jugglestruct *sp = NULL; - int i; - - if (juggles == NULL) - return; - sp = &juggles[MI_SCREEN(mi)]; - - /* generate pattern */ - if (pattern == NULL) { - -#define MAXPAT 10 -#define MAXREPEAT 300 -#define CHANGE_BIAS 8 /* larger makes num_ball changes less likely */ -#define POSITION_BIAS 20 /* larger makes hand movements less likely */ - - int count = 0; - while (count < MI_CYCLES(mi)) { - char buf[MAXPAT * 3 + 3], *b = buf; - int maxseen = 0; - int l = NRAND(MAXPAT) + 1; - int t = NRAND(MIN(MAXREPEAT, (MI_CYCLES(mi) - count))) + 1; - - { /* vary number of balls */ - int new_balls = sp->num_balls; - int change; - - if (new_balls == 2) /* Do not juggle 2 that often */ - change = NRAND(2 + CHANGE_BIAS / 4); - else - change = NRAND(2 + CHANGE_BIAS); - switch (change) { - case 0: - new_balls++; - break; - case 1: - new_balls--; - break; - default: - break; /* NO-OP */ - } - if (new_balls < sp->patternindex.minballs) { - new_balls += 2; - } - if (new_balls > sp->patternindex.maxballs) { - new_balls -= 2; - } - if (new_balls < sp->num_balls) { - if (!program(mi, "[*]", NULL, 1)) /* lose ball */ - return; - } - sp->num_balls = new_balls; - } - - count += t; - if (NRAND(2) && sp->patternindex.index[sp->num_balls].number) { - /* Pick from PortFolio */ - int p = sp->patternindex.index[sp->num_balls].start + - NRAND(sp->patternindex.index[sp->num_balls].number); - if (!program(mi, portfolio[p].pattern, portfolio[p].name, t)) - return; - } else { - /* Invent a new pattern */ - *b++='['; - for(i = 0; i < l; i++){ - int n, m; - do { /* Triangular Distribution => high values more likely */ - m = NRAND(sp->num_balls + 1); - n = NRAND(sp->num_balls + 1); - } while(m >= n); - if (n == sp->num_balls) { - maxseen = 1; - } - switch(NRAND(5 + POSITION_BIAS)){ - case 0: /* Outside throw */ - *b++ = '+'; break; - case 1: /* Cross throw */ - *b++ = '='; break; - case 2: /* Cross catch */ - *b++ = '&'; break; - case 3: /* Cross throw and catch */ - *b++ = 'x'; break; - case 4: /* Bounce */ - *b++ = '_'; break; - default: - break; /* Inside throw (default) */ - } - - *b++ = n + '0'; - *b++ = ' '; - } - *b++ = ']'; - *b = '\0'; - if (maxseen) { - if (!program(mi, buf, NULL, t)) - return; - } - } - } - } else { /* pattern supplied in height or 'a' notation */ - if (!program(mi, pattern, NULL, MI_CYCLES(mi))) - return; - } - - adam(sp); - - name(sp); - - if (!part(mi)) - return; - - lob(mi); - - clap(sp); - - positions(sp); - - if (!projectile(sp)) { - free_juggle(mi); - return; - } - - hands(sp); -#ifdef DEBUG - if(MI_IS_DEBUG(mi)) dump(sp); -#endif -} - -static void -change_juggle(ModeInfo * mi) -{ - jugglestruct *sp = NULL; - Trajectory *t; - - if (juggles == NULL) - return; - sp = &juggles[MI_SCREEN(mi)]; - - /* Strip pending trajectories */ - for (t = sp->head->next; t != sp->head; t = t->next) { - if(t->start > sp->time || t->finish < sp->time) { - Trajectory *n = t; - t=t->prev; - trajectory_destroy(n); - } - } - - /* Pick the current object theme */ - sp->objtypes = choose_object(); - - refill_juggle(mi); - - /* Clean up the Screen. Don't use MI_CLEARWINDOW(mi), since we - don't all those special effects. */ - XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi)); - - show_figure(mi, MI_WHITE_PIXEL(mi), True); - -} - -ENTRYPOINT void -init_juggle (ModeInfo * mi) -{ - jugglestruct *sp = 0; - int i; - - MI_INIT (mi, juggles); - sp = &juggles[MI_SCREEN(mi)]; - - if (only && *only && strcmp(only, " ")) { - balls = clubs = torches = knives = rings = bballs = False; - if (!strcasecmp (only, "balls")) balls = True; - else if (!strcasecmp (only, "clubs")) clubs = True; - else if (!strcasecmp (only, "torches")) torches = True; - else if (!strcasecmp (only, "knives")) knives = True; - else if (!strcasecmp (only, "rings")) rings = True; - else if (!strcasecmp (only, "bballs")) bballs = True; - else { - (void) fprintf (stderr, - "Juggle: -only must be one of: balls, clubs, torches, knives,\n" - "\t rings, or bballs (not \"%s\")\n", only); -#ifdef STANDALONE /* xlock mustn't exit merely because of a bad argument */ - exit (1); -#endif - } - } - - if (sp->head == 0) { /* first time initializing this juggler */ - - sp->count = ABS(MI_COUNT(mi)); - if (sp->count == 0) - sp->count = 200; - - /* record start time */ - sp->begintime = time(NULL); - if(sp->patternindex.maxballs > 0) { - sp->num_balls = sp->patternindex.minballs + - NRAND(sp->patternindex.maxballs - sp->patternindex.minballs); - } - - show_figure(mi, MI_WHITE_PIXEL(mi), True); /* Draw figure. Also discovers - information about the juggler's - proportions */ - - /* "7" should be about three times the height of the juggler's - shoulders */ - sp->Gr = -GRAVITY(3 * sp->arm[0][RIGHT][SHOULDER].y, - 7 * THROW_CATCH_INTERVAL); - - if(!balls && !clubs && !torches && !knives && !rings && !bballs) - balls = True; /* Have to juggle something! */ - - /* create circular trajectory list */ - ADD_ELEMENT(Trajectory, sp->head, sp->head); - if(sp->head == NULL){ - free_juggle(mi); - return; - } - - /* create circular object list */ - ADD_ELEMENT(Object, sp->objects, sp->objects); - if(sp->objects == NULL){ - free_juggle(mi); - return; - } - - /* create circular wander list */ - ADD_ELEMENT(Wander, sp->wander, sp->wander); - if(sp->wander == NULL){ - free_juggle(mi); - return; - } - (void)wander(sp, 0); /* Initialize wander */ - - sp->pattern = strdup(""); /* Initialise saved pattern with - free-able memory */ - } - - sp = &juggles[MI_SCREEN(mi)]; - - if (pattern && - (!*pattern || - !strcasecmp (pattern, ".") || - !strcasecmp (pattern, "random"))) - pattern = NULL; - - if (pattern == NULL && sp->patternindex.maxballs == 0) { - /* pattern list needs indexing */ - int nelements = XtNumber(portfolio); - int numpat = 0; - - /* sort according to number of balls */ - qsort((void*)portfolio, nelements, - sizeof(portfolio[1]), compare_num_balls); - - /* last pattern has most balls */ - sp->patternindex.maxballs = get_num_balls(portfolio[nelements - 1].pattern); - /* run through sorted list, indexing start of each group - and number in group */ - sp->patternindex.maxballs = 1; - for (i = 0; i < nelements; i++) { - int b = get_num_balls(portfolio[i].pattern); - if (b > sp->patternindex.maxballs) { - sp->patternindex.index[sp->patternindex.maxballs].number = numpat; - if(numpat == 0) sp->patternindex.minballs = b; - sp->patternindex.maxballs = b; - numpat = 1; - sp->patternindex.index[sp->patternindex.maxballs].start = i; - } else { - numpat++; - } - } - sp->patternindex.index[sp->patternindex.maxballs].number = numpat; - } - - /* Set up programme */ - change_juggle(mi); - - /* Clean up the Screen. Don't use MI_CLEARWINDOW(mi), since we may - only be resizing and then we won't all those special effects. */ - XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi)); - - /* Only put things here that won't interrupt the programme during - a window resize */ - - /* Use MIN so that users can resize in interesting ways, eg - narrow windows for tall patterns, etc */ - sp->scale = MIN(MI_HEIGHT(mi)/480.0, MI_WIDTH(mi)/160.0); - - if(describe && !sp->mode_font) { /* Check to see if there's room to describe patterns. */ - char *font = get_string_resource (MI_DISPLAY(mi), "font", "Font"); - sp->mode_font = load_font_retry(MI_DISPLAY(mi), font); - if (font) free (font); - } -} - -ENTRYPOINT void -draw_juggle (ModeInfo * mi) -{ - Trajectory *traj = NULL; - Object *o = NULL; - unsigned long future = 0; - jugglestruct *sp = NULL; - char *pattern = NULL; - double cx; - - if (juggles == NULL) - return; - sp = &juggles[MI_SCREEN(mi)]; - - MI_IS_DRAWN(mi) = True; - -#ifdef HAVE_JWXYZ - /* Don't worry about flicker, trust Quartz's double-buffering. - This is a fast fix for the pixel-turds I can't track down... - */ - XClearWindow(MI_DISPLAY(mi), MI_WINDOW(mi)); -#endif - - /* Update timer */ - if (real) { - struct timeval tv; - (void)gettimeofday(&tv, NULL); - sp->time = (int) ((tv.tv_sec - sp->begintime)*1000 + tv.tv_usec/1000); - } else { - sp->time += MI_DELAY(mi) / 1000; - } - - /* First pass: Move arms and strip out expired elements */ - for (traj = sp->head->next; traj != sp->head; traj = traj->next) { - if (traj->status != PREDICTOR) { - /* Skip any elements that need further processing */ - /* We could remove them, but there shoudn't be many and they - would be needed if we ever got the pattern refiller - working */ - continue; - } - if (traj->start > future) { /* Lookahead to the end of the show */ - future = traj->start; - } - if (sp->time < traj->start) { /* early */ - continue; - } else if (sp->time < traj->finish) { /* working */ - - /* Look for pattern name */ - if(traj->pattern != NULL) { - pattern=traj->pattern; - } - - if (traj->type == Empty || traj->type == Full) { - /* Only interested in hands on this pass */ - double angle = traj->angle + traj->spin * (sp->time - traj->start); - double xd = 0, yd = 0; - DXPoint p; - - /* Find the catching offset */ - if(traj->object != NULL) { - if(ObjectDefs[traj->object->type].handle > 0) { - /* Handles Need to be oriented */ - xd = ObjectDefs[traj->object->type].handle * - PERSPEC * sin(angle); - yd = ObjectDefs[traj->object->type].handle * - cos(angle); - } else { - /* Balls are always caught at the bottom */ - xd = 0; - yd = -4; - } - } - p.x = (CUBIC(traj->xp, sp->time) - xd); - p.y = (CUBIC(traj->yp, sp->time) + yd); - reach_arm(mi, traj->hand, &p); - - /* Store updated hand position */ - traj->x = p.x + xd; - traj->y = p.y - yd; - } - if (traj->type == Ball || traj->type == Full) { - /* Only interested in objects on this pass */ - double x, y; - Trace *s; - - if(traj->type == Full) { - /* Adjusted these in the first pass */ - x = traj->x; - y = traj->y; - } else { - x = CUBIC(traj->xp, sp->time); - y = CUBIC(traj->yp, sp->time); - } - - ADD_ELEMENT(Trace, s, traj->object->trace->prev); - s->x = x; - s->y = y; - s->angle = traj->angle + traj->spin * (sp->time - traj->start); - s->divisions = traj->divisions; - traj->object->tracelen++; - traj->object->active = True; - } - } else { /* expired */ - Trajectory *n = traj; - traj=traj->prev; - trajectory_destroy(n); - } - } - - /* Erase end of trails */ - for (o = sp->objects->next; o != sp->objects; o = o->next) { - Trace *s; - for (s = o->trace->next; - o->trace->next != o->trace && - (o->count == 0 || o->tracelen > o->tail); - s = o->trace->next) { - ObjectDefs[o->type].draw(mi, MI_BLACK_PIXEL(mi), s); - REMOVE(s); - o->tracelen--; - if(o->count <= 0 && o->tracelen <= 0) { - /* Object no longer in use and trail gone */ - Object *n = o; - o = o->prev; - object_destroy(n); - } - if(o->count <= 0) break; /* Allow loop for catch-up, but not clean-up */ - } - } - - show_arms(mi, MI_BLACK_PIXEL(mi)); - cx = wander(sp, sp->time); - /* Reduce flicker by only permitting movements of more than a pixel */ - if(fabs((sp->cx - cx))*sp->scale >= 2.0 ) { - show_figure(mi, MI_BLACK_PIXEL(mi), False); - sp->cx = cx; - } - - show_figure(mi, MI_WHITE_PIXEL(mi), False); - - show_arms(mi, MI_WHITE_PIXEL(mi)); - - /* Draw Objects */ - for (o = sp->objects->next; o != sp->objects; o = o->next) { - if(o->active) { - ObjectDefs[o->type].draw(mi,MI_PIXEL(mi, o->color), o->trace->prev); - o->active = False; - } - } - - - /* Save pattern name so we can erase it when it changes */ - if(pattern != NULL && strcmp(sp->pattern, pattern) != 0 ) { - /* Erase old name */ - XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi)); -# if 0 - XDrawString(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - 0, 20, sp->pattern, strlen(sp->pattern)); -# else - XFillRectangle(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - 0, 0, MI_WIDTH(mi), 25); -# endif - free(sp->pattern); - sp->pattern = strdup(pattern); - - if (MI_IS_VERBOSE(mi)) { - (void) fprintf(stderr, "Juggle[%d]: Running: %s\n", - MI_SCREEN(mi), sp->pattern); - } - } - if(sp->mode_font != None && - XTextWidth(sp->mode_font, sp->pattern, strlen(sp->pattern)) < MI_WIDTH(mi)) { - /* Redraw once a cycle, in case it's obscured or it changed */ - XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_WHITE_PIXEL(mi)); - XDrawImageString(MI_DISPLAY(mi), MI_WINDOW(mi), MI_GC(mi), - 0, 20, sp->pattern, strlen(sp->pattern)); - } - -#ifdef MEMTEST - if((int)(sp->time/10) % 1000 == 0) - (void) fprintf(stderr, "sbrk: %d\n", (int)sbrk(0)); -#endif - - if (future < sp->time + 100 * THROW_CATCH_INTERVAL) { - refill_juggle(mi); - } else if (sp->time > 1<<30) { /* Hard Reset before the clock wraps */ - init_juggle(mi); - } -} - -XSCREENSAVER_MODULE ("Juggle", juggle) - -#endif /* MODE_juggle */ |
