~ubuntu-branches/ubuntu/trusty/xscreensaver/trusty

« back to all changes in this revision

Viewing changes to hacks/glx/juggler3d.c

  • Committer: Bazaar Package Importer
  • Author(s): Robert Ancell
  • Date: 2009-11-30 13:33:13 UTC
  • mfrom: (1.1.8 upstream) (2.1.6 squeeze)
  • Revision ID: james.westby@ubuntu.com-20091130133313-3b5nz2e7hvbb8h3l
Tags: 5.10-3ubuntu1
* Merge with Debian unstable, remaining changes: (LP: #489062)
  - debian/control: add Build-Depends on ubuntu-artwork
  - debian/rules: use /usr/share/backgrounds
  - debian/control: Move xli | xloadimage recommends to suggests
  - debian/split-hacks.config: Use different set of default hacks to Debian
  - debian/source_xscreensaver.py: Add apport hook
  - debian/patches/53_XScreenSaver.ad.in.patch: Use Ubuntu branding

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Juggler3D, Copyright (c) 2005-2008 Brian Apps <brian@jugglesaver.co.uk>
2
 
 *
3
 
 * Permission to use, copy, modify, distribute, and sell this software and its
4
 
 * documentation for any purpose is hereby granted without fee, provided that
5
 
 * the above copyright notice appear in all copies and that both that copyright
6
 
 * notice and this permission notice appear in supporting documentation.  No
7
 
 * representations are made about the suitability of this software for any
8
 
 * purpose.  It is provided "as is" without express or implied warranty. */
9
 
 
 
1
/* juggle, Copyright (c) 1996-2009 Tim Auckland <tda10.geo@yahoo.com>
 
2
 * and Jamie Zawinski <jwz@jwz.org>
 
3
 *
 
4
 * Permission to use, copy, modify, and distribute this software and its
 
5
 * documentation for any purpose and without fee is hereby granted,
 
6
 * provided that the above copyright notice appear in all copies and that
 
7
 * both that copyright notice and this permission notice appear in
 
8
 * supporting documentation.
 
9
 *
 
10
 * This file is provided AS IS with no warranties of any kind.  The author
 
11
 * shall have no liability with respect to the infringement of copyrights,
 
12
 * trade secrets or any patents by this file or any part thereof.  In no
 
13
 * event will the author be liable for any lost revenue or profits or
 
14
 * other special, indirect and consequential damages.
 
15
 *
 
16
 * NOTE: this program was originally called "juggle" and was 2D Xlib.
 
17
 *       There was another program called "juggler3d" that was OpenGL.
 
18
 *       In 2009, jwz converted "juggle" to OpenGL and renamed
 
19
 *       "juggle" to "juggler3d".  The old "juggler3d" hack is gone.
 
20
 *
 
21
 * Revision History
 
22
 * 09-Aug-2009: jwz: converted from Xlib to OpenGL.
 
23
 * 13-Dec-2004: [TDA] Use -cycles and -count in a rational manner.
 
24
 *              Add -rings, -bballs.  Add -describe.  Finally made
 
25
 *              live pattern updates possible.  Add refill_juggle(),
 
26
 *              change_juggle() and reshape_juggle().  Make
 
27
 *              init_juggle() non-destructive.  Reorder erase/draw
 
28
 *              operations.  Update xscreensaver xml and manpage.
 
29
 * 15-Nov-2004: [TDA] Fix all memory leaks.
 
30
 * 12-Nov-2004: [TDA] Add -torches and another new trail
 
31
 *              implementation, so that different objects can have
 
32
 *              different length trails.
 
33
 * 11-Nov-2004: [TDA] Clap when all the balls are in the air.
 
34
 * 10-Nov-2004: [TDA] Display pattern name converted to hight
 
35
 *              notation.
 
36
 * 31-Oct-2004: [TDA] Add -clubs and new trail implementation.
 
37
 * 02-Sep-2003: Non-real time to see what is happening without a
 
38
 *              strobe effect for slow machines.
 
39
 * 01-Nov-2000: Allocation checks
 
40
 * 1996: Written
 
41
 */
 
42
 
 
43
/*-
 
44
 * TODO
 
45
 * Implement the anonymously promised -uni option.
 
46
 */
 
47
 
 
48
 
 
49
/*
 
50
 * Notes on Adam Chalcraft Juggling Notation (used by permission)
 
51
 * a-> Adam's notation  s-> Site swap (Cambridge) notation
 
52
 *
 
53
 * To define a map from a-notation to s-notation ("site-swap"), both
 
54
 * of which look like doubly infinite sequences of natural numbers. In
 
55
 * s-notation, there is a restriction on what is allowed, namely for
 
56
 * the sequence s_n, the associated function f(n)=n+s_n must be a
 
57
 * bijection. In a-notation, there is no restriction.
 
58
 *
 
59
 * To go from a-notation to s-notation, you start by mapping each a_n
 
60
 * to a permutation of N, the natural numbers.
 
61
 *
 
62
 * 0 -> the identity
 
63
 * 1 -> (10) [i.e. f(1)=0, f(0)=1]
 
64
 * 2 -> (210) [i.e. f(2)=1, f(1)=0, f(0)=2]
 
65
 * 3 -> (3210) [i.e. f(3)=2, f(2)=1, f(1)=0, f(0)=3]
 
66
 * etc.
 
67
 *
 
68
 * Then for each n, you look at how long 0 takes to get back to 0
 
69
 * again and you call this t_n. If a_n=0, for example, then since the
 
70
 * identity leaves 0 alone, it gets back to 0 in 1 step, so t_n=1. If
 
71
 * a_n=1, then f(0)=1. Now any further a_n=0 leave 1 alone, but the
 
72
 * next a_n>0 sends 1 back to 0. Hence t_n is 2 + the number of 0's
 
73
 * following the 1. Finally, set s_n = t_n - 1.
 
74
 *
 
75
 * To give some examples, it helps to have a notation for cyclic
 
76
 * sequences. By (123), for example, I mean ...123123123123... . Now
 
77
 * under the a-notation -> s-notation mapping we have some familiar
 
78
 * examples:
 
79
 *
 
80
 * (0)->(0), (1)->(1), (2)->(2) etc.
 
81
 * (21)->(31), (31)->(51), (41)->(71) etc.
 
82
 * (10)->(20), (20)->(40), (30)->(60) etc.
 
83
 * (331)->(441), (312)->(612), (303)->(504), (321)->(531)
 
84
 * (43)->(53), (434)->(534), (433)->(633)
 
85
 * (552)->(672)
 
86
 *
 
87
 * In general, the number of balls is the *average* of the s-notation,
 
88
 * and the *maximum* of the a-notation. Another theorem is that the
 
89
 * minimum values in the a-notation and the s-notation and equal, and
 
90
 * preserved in the same positions.
 
91
 *
 
92
 * The usefulness of a-notation is the fact that there are no
 
93
 * restrictions on what is allowed. This makes random juggle
 
94
 * generation much easier. It also makes enumeration very
 
95
 * easy. Another handy feature is computing changes.  Suppose you can
 
96
 * do (5) and want a neat change up to (771) in s-notation [Mike Day
 
97
 * actually needed this example!]. Write them both in a-notation,
 
98
 * which gives (5) and (551). Now concatenate them (in general, there
 
99
 * may be more than one way to do this, but not in this example), to
 
100
 * get
 
101
 *
 
102
 * ...55555555551551551551551...
 
103
 *
 
104
 * Now convert back to s-notation, to get
 
105
 *
 
106
 * ...55555566771771771771771...
 
107
 *
 
108
 * So the answer is to do two 6 throws and then go straight into
 
109
 * (771).  Coming back down of course,
 
110
 *
 
111
 * ...5515515515515515555555555...
 
112
 *
 
113
 * converts to
 
114
 *
 
115
 * ...7717717717716615555555555...
 
116
 *
 
117
 * so the answer is to do a single 661 and then drop straight down to
 
118
 * (5).
 
119
 *
 
120
 * [The number of balls in the generated pattern occasionally changes.
 
121
 * In order to decrease the number of balls I had to introduce a new
 
122
 * symbol into the Adam notation, [*] which means 'lose the current
 
123
 * ball'.]
 
124
 */
 
125
 
 
126
/* This code uses so many linked lists it's worth having a built-in
 
127
 * leak-checker */
 
128
#undef MEMTEST
 
129
 
 
130
# define DEFAULTS       "*delay:        10000   \n" \
 
131
                        "*count:        200     \n" \
 
132
                        "*cycles:       1000    \n" \
 
133
                        "*ncolors:      32      \n" \
 
134
                        "*titleFont:  -*-helvetica-bold-r-normal-*-180-*\n" \
 
135
                        "*showFPS:      False   \n" \
 
136
                        "*wireframe:    False   \n" \
 
137
 
 
138
# define refresh_juggle 0
10
139
#undef countof
11
140
#define countof(x) (sizeof((x))/sizeof((*x)))
12
141
 
13
 
#define DEFAULTS    \
14
 
    "*delay: 20000\n*showFPS: False\n*wireframe: False\n"
15
 
 
16
 
# define refresh_juggler3d 0
17
 
# define release_juggler3d 0
18
142
#include "xlockmore.h"
 
143
#include "sphere.h"
 
144
#include "tube.h"
 
145
#include "rotator.h"
19
146
#include "gltrackball.h"
 
147
#include "glxfonts.h"
 
148
#include <ctype.h>
20
149
 
21
150
#ifdef USE_GL /* whole file */
22
151
 
23
 
/* A selection of macros to make functions from math.h return single precision
24
 
 * numbers.  Arguably it's better to work at a higher precision and cast it
25
 
 * back but littering the code with casts makes it less readable -- without
26
 
 * the casts you can get tons of warnings from the compiler (particularily
27
 
 * MSVC which enables loss of precision warnings by default) */
28
 
 
29
 
#define cosf(a) (float)(cos((a)))
30
 
#define sinf(a) (float)(sin((a)))
31
 
#define tanf(a) (float)(tan((a)))
32
 
#define sqrtf(a) (float)(sqrt((a)))
33
 
#define powf(a, b) (float)(pow((a), (b)))
34
 
 
35
 
#undef max
36
 
#undef min
37
 
 
38
 
#define max(a, b) ((a) > (b) ? (a) : (b))
39
 
#define min(a, b) ((a) < (b) ? (a) : (b))
40
 
 
41
 
 
42
 
/******************************************************************************
43
 
 *
44
 
 * The code is broadly split into the following parts:
45
 
 *
46
 
 *  - Engine.  The process of determining the position of the juggler and 
47
 
 *        objects being juggled at an arbitrary point in time.  This is
48
 
 *        independent from any drawing code.
49
 
 *  - Sites.  The process of creating a random site swap pattern or parsing
50
 
 *        a Juggle Saver compatible siteswap for use by the engine.  For an
51
 
 *        introduction to juggling site swaps check out
52
 
 *         http://www.jugglingdb.com/
53
 
 *  - Rendering.  OpenGL drawing code that animates the juggler.
54
 
 *  - XScreenSaver.  Interface code to get thing working as a GLX hack.
55
 
 *  
56
 
 *****************************************************************************/
57
 
 
58
 
 
59
 
/*****************************************************************************
60
 
 *
61
 
 * Data structures
62
 
 *
63
 
 *****************************************************************************/
64
 
 
65
 
/* POS is used to represent the position of a hand when it catches or throws
66
 
 * an object; as well as the orientation of the object.  The rotation and
67
 
 * elevation are specified in degrees.  These angles are not normalised so that
68
 
 * it is possible to specify how the object spins and rotates as it is thrown
69
 
 * from the 'From' position to the 'To' position.
70
 
 * 
71
 
 * Because this is the position of the hand some translation is required with
72
 
 * rings and clubs to get the centre of rotation position. */
73
 
 
74
 
typedef struct
75
 
{
76
 
    float x;
77
 
    float y;
78
 
    float z;
79
 
    float Rot;
80
 
    float Elev;
81
 
} POS;
82
 
 
83
 
 
84
 
/* An array of THROW_INFOs are configured with each entry corresponding to the
85
 
 * position in the site swap (In fact we double up odd length patterns to ensure
86
 
 * there is left/right symmetry).  It allows us to quickly determine where an
87
 
 * object and the hands are at a given time.  The information is specified in
88
 
 * terms of throws, and positions where throws aren't present (0's and 2's) are
89
 
 * simply ignored.
90
 
 * 
91
 
 * TotalTime - The count of beats before this object is thrown again.  Typically
92
 
 *    this is the same as the weight of the throw but where an object is held it
93
 
 *    is longer.  e.g. the first throw of the site 64242.7. will be 10, 6 for
94
 
 *    throw and 4 (two 2's) for the carry.
95
 
 * TimeInAir - The weight of the throw.
96
 
 * PrevThrow - zero based index into array of THROW_INFOs of the previous throw.
97
 
 *     e.g. for the throw '8' in the site 345678..... the PrevThrow is 1
98
 
 *    (i.e. the 4)
99
 
 * FromPos, FromVelocity, ToPos, ToVelocity - The position and speeds at the
100
 
 *    start and end of the throw.  These are used to generate a spline while
101
 
 *    carrying an object and while moving the hand from a throw to a catch.
102
 
 * NextForHand - Number of beats before the hand that throws this object will
103
 
 *    throw another object.  This is always going to be at least 2.  When there
104
 
 *    are gaps in the pattern (0's) or holds (2's) NextForHand increases. */
105
 
 
106
 
typedef struct
107
 
{
108
 
    int TotalTime;
109
 
    int TimeInAir;
110
 
    int PrevThrow;
111
 
 
112
 
    POS FromPos;
113
 
    POS FromVelocity;
114
 
    POS ToPos;
115
 
    POS ToVelocity;
116
 
 
117
 
    int NextForHand;
118
 
} THROW_INFO;
119
 
 
120
 
 
121
 
/* OBJECT_POSITION works with the array of THROW_INFOs to allow us to determine
122
 
 * exactly where an object or hand is.
123
 
 *
124
 
 * TimeOffset - The total number of beats expired when the object was thrown.
125
 
 * ThrowIndex - The zero based index into the THROW_INFO array for the current
126
 
 *     throw.
127
 
 * ObjectType - One of the OBJECT_XX defines.
128
 
 * TotalTwist - Only relevant for OBJECT_BALL, this is the total amount the ball
129
 
 *     has twisted while in the air.  When segmented balls are drawn you see a 
130
 
 *     spinning effect similar to what happens when you juggle beanbags.  */
131
 
 
132
 
#define OBJECT_DEFAULT 0
133
 
#define OBJECT_BALL 1
134
 
#define OBJECT_CLUB 2
135
 
#define OBJECT_RING 3
136
 
 
137
 
typedef struct
138
 
{
139
 
    int TimeOffset;
140
 
    int ThrowIndex;
141
 
    float TotalTwist;
142
 
    int ObjectType;
143
 
} OBJECT_POSITION;
144
 
 
145
 
 
146
 
/* PATTERN_INFO is the main structure that holds the information about a 
147
 
 * juggling pattern. 
148
 
 *
149
 
 * pThrowInfo is an array of ThrowLen elements that describes each throw in the
150
 
 *     pattern.
151
 
 * pObjectInfo gives the current position of all objects at a given instant.
152
 
 *     These values are updated as the pattern is animated.
153
 
 * LeftHand and RightHand describe the current positions of each of the 
154
 
 *     juggler's hands.
155
 
 * MaxWeight is the maximum weight of the all throws in pThrowInfo.
156
 
 * Height and Alpha are parameters that describe how objects fall under the
157
 
 *     influence of gravity.  See SetHeightAndAlpha() for the gory details. */
158
 
 
159
 
typedef struct
160
 
{
161
 
    THROW_INFO* pThrowInfo;
162
 
    int ThrowLen;
163
 
    
164
 
    OBJECT_POSITION* pObjectInfo;
165
 
    int Objects;
166
 
 
167
 
    OBJECT_POSITION LeftHand;
168
 
    OBJECT_POSITION RightHand;
169
 
    
170
 
    int MaxWeight;
171
 
 
172
 
    float Height;
173
 
    float Alpha;
174
 
} PATTERN_INFO;
175
 
 
176
 
 
177
 
/* EXT_SITE_INFO is used to initialise a PATTERN_INFO object using a Juggle
178
 
 * Saver compatible site swap.  These contain additional information about the
179
 
 * type of object thrown, the positions of throw and catch etc. */
180
 
 
181
 
#define HAS_FROM_POS 1
182
 
#define HAS_TO_POS 2
183
 
#define HAS_SNATCH 4
184
 
#define HAS_SPINS 8
185
 
 
186
 
typedef struct
187
 
{
188
 
    unsigned Flags;
189
 
    int Weight;
190
 
    int ObjectType;
191
 
    POS FromPos;
192
 
    POS ToPos;
193
 
    float SnatchX;
194
 
    float SnatchY;
195
 
    int Spins;
196
 
} EXT_SITE_INFO;
197
 
 
198
 
 
199
 
/* RENDER_STATE is used to co-ordinate the OpenGL rendering of the juggler and
200
 
 * objects:
201
 
 * pPattern - The pattern to be juggled
202
 
 * CameraElev - The elevation angle (in degrees) that the camera is looking
203
 
 *    along.  0 is horizontal and a +ve angle is looking down.  This value
204
 
 *    should be between -90 and +90.
205
 
 * AspectRatio - Window width to height ratio.
206
 
 * DLStart - The number for the first display list created, any others directly
207
 
 *    follow this.
208
 
 * Time - Animation time (in beats)
209
 
 * TranslateAngle - Cumulative translation (in degrees) for the juggling figure.
210
 
 * SpinAngle- Cumulative spin (in degrees) for the juggling figure.
211
 
 */
212
 
 
213
 
typedef struct
214
 
{
215
 
    PATTERN_INFO* pPattern;
216
 
    float CameraElev;
217
 
    float AspectRatio;
218
 
    int DLStart;
219
 
    
220
 
    float Time;
221
 
    float TranslateAngle;
222
 
    float SpinAngle;
223
 
    
224
 
    trackball_state *trackball;
225
 
    Bool button_down_p;
226
 
 
227
 
} RENDER_STATE;
228
 
 
229
 
 
230
 
/*****************************************************************************
231
 
 *
232
 
 * Engine
233
 
 *
234
 
 ****************************************************************************
235
 
 *
236
 
 * The main purpose of the engine is to work out the exact position of all the
237
 
 * juggling objects and the juggler's hands at any point in time.  The motion
238
 
 * of the objects can be split into two parts: in the air and and being carried.
239
 
 *
240
 
 * While in the air, the motion is governed by a standard parabolic trajectory.
241
 
 * The only minor complication is that the engine has no fixed concept of
242
 
 * gravity, instead it using a term called Alpha that varies according to the
243
 
 * pattern (see SetHeightAndAlpha). 
244
 
 *
245
 
 * The motion while an object is carried comes from fitting a spline through the
246
 
 * catch and throw points and maintaining the catch and throw velocities at
247
 
 * each end.  In the simplest case this boils down to cubic Bezier spline.  The
248
 
 * only wrinkle occurs when a ball is being carried for a long time.  The simple 
249
 
 * cubic spline maths produces a curve that goes miles away -- here we do a
250
 
 * bit of reparameterisation so things stay within sensible bounds.
251
 
 * (On a related note, this scheme is _much_ simpler than the Juggle Saver
252
 
 * one.  Juggle Saver achieves 2nd order continuity and much care is taken
253
 
 * to avoid spline ringing.)
254
 
 * 
255
 
 * The motion of the hands is identical to the ball carrying code. It uses two
256
 
 * splines: one while an object is being carried; and another when it moves from
257
 
 * the previous throw to the next catch.
258
 
 */
259
 
 
260
 
static const float CARRY_TIME = 0.56f;
261
 
static const float PI = 3.14159265358979f;
262
 
 
263
 
 
264
 
/* While a ball is thrown it twists slighty about an axis, this routine gives
265
 
 * the total about of twist for a given ball throw. */
266
 
 
267
 
static float GetBallTwistAmount(const THROW_INFO* pThrow)
268
 
{
269
 
    if (pThrow->FromPos.x > pThrow->ToPos.x)
270
 
        return 18.0f * powf(pThrow->TimeInAir, 1.5);
271
 
    else
272
 
        return -18.0f * powf(pThrow->TimeInAir, 1.5);
273
 
}
274
 
 
275
 
 
276
 
static float NormaliseAngle(float Ang)
277
 
{
278
 
    if (Ang >= 0.0f)
279
 
    {
280
 
        int i = (int) (Ang + 180.0f) / 360;
281
 
        return Ang - 360.0f * i;
282
 
    }
283
 
    else
284
 
    {
285
 
        int i = (int)(180.0f - Ang) / 360;
286
 
        return Ang + i * 360.0f;
287
 
    }
288
 
}
289
 
 
290
 
 
291
 
/* The interpolate routine for ball carrying and hand motion.  We are given the
292
 
 * start (P0) and end (P1) points and the velocities at these points, the task
293
 
 * is to form a function P(t) such that:
294
 
 *    P(0) = P0
295
 
 *    P(TLen) = P1
296
 
 *    P'(0) = V0
297
 
 *    P'(TLen) = V1
298
 
 */
299
 
 
300
 
static POS InterpolatePosition(
301
 
    const POS* pP0, const POS* pV0, const POS* pP1, const POS* pV1,
302
 
    float TLen, float t)
303
 
{
304
 
    POS p;
305
 
    float a, b, c, d, tt, tc;
306
 
    
307
 
    /* The interpolation is based on a simple cubic that achieves 1st order
308
 
     * continuity at the end points.  However the spline can become too long if
309
 
     * the TLen parameter is large.  In this case we cap the curve's length (fix
310
 
     * the shape) and then reparameterise time to achieve the continuity
311
 
     * conditions. */
312
 
 
313
 
    tc = CARRY_TIME;
314
 
    
315
 
    if (TLen > tc)
316
 
    {
317
 
        /* The reparameterisation tt(t) gives:
318
 
         *  tt(0) = 0, tt(TLen) = tc, tt'(0) = 1, tt'(TLen) = 1
319
 
         * and means we can set t = tt(t), TLen = tc and then fall through
320
 
         * to use the normal cubic spline fit.
321
 
         *    
322
 
         * The reparameterisation is based on two piecewise quadratics, one
323
 
         * that goes from t = 0 to t = TLen / 2 and the other, mirrored in
324
 
         * tt and t that goes from t = TLen / 2 to t = TLen.
325
 
         * Because TLen > tc we can arrange for tt to be unique in the range if
326
 
         * we specify the quadratic in tt.  i.e. t = A * tt ^ 2 + B * tt + C.
327
 
         *
328
 
         * Considering the first piece and applying initial conditions.
329
 
         *   tt = 0 when t = 0   =>  C = 0
330
 
         *   tt' = 1 when t = 0   =>  B = 1
331
 
         *   tt = tc / 2 when t = TLen / 2  =>  A = 2 * (TLen - tc) / tc^2
332
 
         *
333
 
         * writing in terms of t
334
 
         *   tt = (-B + (B ^ 2 + 4At) ^ 0.5) / 2A
335
 
         * or
336
 
         *   tt = ((1 + 4At) ^ 0.5 - 1) / 2A */
337
 
        
338
 
        float A = 2.0f * (TLen - tc) / (tc * tc);
339
 
        
340
 
        if (t > TLen / 2.0f)
341
 
            t = tc - (sqrtf(1.0f + 4.0f * A * (TLen - t)) - 1.0f) / (2.0f * A);
342
 
        else
343
 
            t = (sqrtf(1.0f + 4.0f * A * t) - 1.0f) / (2.0f * A);
344
 
        
345
 
        TLen = tc;
346
 
    }
347
 
    
348
 
    /* The cubic spline takes the form:
349
 
     *   P(t) = p0 * a(t) + v0 * b(t) + p1 * c(t) + v1 * d(t)
350
 
     * where p0 is the start point, v0 the start velocity, p1 the end point and
351
 
     * v1 the end velocity.  a(t), b(t), c(t) and d(t) are cubics in t.
352
 
     * We can show that:
353
 
     *
354
 
     *  a(t) = 2 * (t / TLen) ^ 3 - 3 * (t / TLen) ^ 2 + 1
355
 
     *  b(t) = t ^ 3 / TLen ^ 2 - 2 * t ^ 2 / TLen + t
356
 
     *  c(t) = -2 * (t / TLen) ^ 3 + 3 * (t / TLen) ^ 2
357
 
     *  d(t) = t ^ 3 / TLen ^ 2 - t ^ 2 / TLen
358
 
     *
359
 
     * statisfy the boundary conditions:
360
 
     *    P(0) = p0, P(TLen) = p1, P'(0) = v0 and P'(TLen) = v1  */
361
 
    
362
 
    tt = t / TLen;
363
 
    
364
 
    a = tt * tt * (2.0f * tt - 3.0f) + 1.0f;
365
 
    b = t * tt * (tt - 2.0f) + t;
366
 
    c = tt * tt * (3.0f - 2.0f * tt);
367
 
    d = t * tt * (tt - 1.0f);
368
 
 
369
 
    p.x = a * pP0->x + b * pV0->x + c * pP1->x + d * pV1->x;
370
 
    p.y = a * pP0->y + b * pV0->y + c * pP1->y + d * pV1->y;
371
 
    p.z = a * pP0->z + b * pV0->z + c * pP1->z + d * pV1->z;
372
 
 
373
 
    p.Rot = a * NormaliseAngle(pP0->Rot) + b * pV0->Rot + 
374
 
        c * NormaliseAngle(pP1->Rot) + d * pV1->Rot;
375
 
    p.Elev = a * NormaliseAngle(pP0->Elev) + b * pV0->Elev + 
376
 
        c * NormaliseAngle(pP1->Elev) + d * pV1->Elev;
377
 
 
378
 
    return p;
379
 
}
380
 
 
381
 
 
382
 
static POS InterpolateCarry(
383
 
    const THROW_INFO* pThrow, const THROW_INFO* pNext, float t)
384
 
{
385
 
    float CT = CARRY_TIME + pThrow->TotalTime - pThrow->TimeInAir;
386
 
    return InterpolatePosition(&pThrow->ToPos, &pThrow->ToVelocity,
387
 
        &pNext->FromPos, &pNext->FromVelocity, CT, t);
388
 
}
389
 
 
390
 
 
391
 
/* Determine the position of the hand at a point in time. */
392
 
 
393
 
static void GetHandPosition(
394
 
    PATTERN_INFO* pPattern, int RightHand, float Time, POS* pPos)
395
 
{
396
 
    OBJECT_POSITION* pObj = 
397
 
        RightHand == 0 ? &pPattern->LeftHand : &pPattern->RightHand;
398
 
    THROW_INFO* pLastThrow;
399
 
    
400
 
    /* Upon entry, the throw information for the relevant hand may be out of
401
 
     * sync.  Therefore we advance through the pattern if required. */
402
 
 
403
 
    while (pPattern->pThrowInfo[pObj->ThrowIndex].NextForHand + pObj->TimeOffset 
404
 
        <= (int) Time)
405
 
    {
406
 
        int w = pPattern->pThrowInfo[pObj->ThrowIndex].NextForHand;
407
 
        pObj->TimeOffset += w;
408
 
        pObj->ThrowIndex = (pObj->ThrowIndex + w) % pPattern->ThrowLen;
409
 
    }
410
 
 
411
 
    pLastThrow = &pPattern->pThrowInfo[pObj->ThrowIndex];
412
 
 
413
 
    /* The TimeInAir will only ever be 2 or 0 if no object is ever thrown by
414
 
     * this hand.  In normal circumstances, 2's in the site swap are coalesced
415
 
     * and added to TotalTime of the previous throw.  0 is a hole and means that
416
 
     * an object isn't there.  In this case we just hold the hand still. */
417
 
    if (pLastThrow->TimeInAir == 2 || pLastThrow->TimeInAir == 0)
418
 
    {
419
 
        pPos->x = pLastThrow->FromPos.x;
420
 
        pPos->y = pLastThrow->FromPos.y;
421
 
    }
422
 
    else
423
 
    {
424
 
        /* The hand is either moving to catch the next object or carrying the
425
 
         * next object to its next throw position.  The way THROW_INFO is
426
 
         * structured means the relevant information for the object we're going
427
 
         * to catch is held at the point at which it was thrown 
428
 
         * (pNextThrownFrom).  We can't go straight for it and instead have to
429
 
         * look at the object we've about to throw next and work out where it
430
 
         * came from. */
431
 
        
432
 
        THROW_INFO* pNextThrow = &pPattern->pThrowInfo[
433
 
            (pObj->ThrowIndex + pLastThrow->NextForHand) % pPattern->ThrowLen];
434
 
        
435
 
        THROW_INFO* pNextThrownFrom = 
436
 
            &pPattern->pThrowInfo[pNextThrow->PrevThrow];
437
 
        
438
 
        /* tc is a measure of how long the object we're due to catch is being
439
 
         * carried for.  We use this to work out if we've actually caught it at
440
 
         * this moment in time. */
441
 
        
442
 
        float tc = CARRY_TIME + 
443
 
            pNextThrownFrom->TotalTime - pNextThrownFrom->TimeInAir;
444
 
        
445
 
        Time -= pObj->TimeOffset;
446
 
 
447
 
        if (Time > pLastThrow->NextForHand - tc)
448
 
        {
449
 
            /* carrying this ball to it's new location */
450
 
            *pPos = InterpolateCarry(pNextThrownFrom,
451
 
                pNextThrow, (Time - (pLastThrow->NextForHand - tc)));
452
 
        }
453
 
        else
454
 
        {
455
 
            /* going for next catch */
456
 
            *pPos = InterpolatePosition(
457
 
                &pLastThrow->FromPos, &pLastThrow->FromVelocity, 
458
 
                &pNextThrownFrom->ToPos, &pNextThrownFrom->ToVelocity,
459
 
                pLastThrow->NextForHand - tc, Time);
460
 
        }
461
 
    }
462
 
}
463
 
 
464
 
 
465
 
static float SinDeg(float AngInDegrees)
466
 
{
467
 
    return sinf(AngInDegrees * PI / 180.0f);
468
 
}
469
 
 
470
 
 
471
 
static float CosDeg(float AngInDegrees)
472
 
{
473
 
    return cosf(AngInDegrees * PI / 180.0f);
474
 
}
475
 
 
476
 
 
477
 
/* Offset the specified position to get the centre of the object based on the
478
 
 * the handle length and the current orientation */
479
 
 
480
 
static void OffsetHandlePosition(const POS* pPos, float HandleLen, POS* pResult)
481
 
{
482
 
    pResult->x = pPos->x + HandleLen * SinDeg(pPos->Rot) * CosDeg(pPos->Elev);
483
 
    pResult->y = pPos->y + HandleLen * SinDeg(pPos->Elev);
484
 
    pResult->z = pPos->z + HandleLen * CosDeg(pPos->Rot) * CosDeg(pPos->Elev);
485
 
    pResult->Elev = pPos->Elev;
486
 
    pResult->Rot = pPos->Rot;
487
 
}
488
 
 
489
 
 
490
 
static void GetObjectPosition(
491
 
    PATTERN_INFO* pPattern, int Obj, float Time, float HandleLen, POS* pPos)
492
 
{
493
 
    OBJECT_POSITION* pObj = &pPattern->pObjectInfo[Obj];
494
 
    THROW_INFO* pThrow;
495
 
    
496
 
    /* Move through the pattern, if required, such that pThrow corresponds to
497
 
     * the current throw for this object. */
498
 
 
499
 
    while (pPattern->pThrowInfo[pObj->ThrowIndex].TotalTime + pObj->TimeOffset
500
 
        <= (int) Time)
501
 
    {
502
 
        int w = pPattern->pThrowInfo[pObj->ThrowIndex].TotalTime;
503
 
        pObj->TimeOffset += w;
504
 
        pObj->TotalTwist = NormaliseAngle(pObj->TotalTwist + 
505
 
            GetBallTwistAmount(&pPattern->pThrowInfo[pObj->ThrowIndex]));
506
 
        
507
 
        pObj->ThrowIndex = (pObj->ThrowIndex + w) % pPattern->ThrowLen;
508
 
    }
509
 
 
510
 
    pThrow = &pPattern->pThrowInfo[pObj->ThrowIndex];
511
 
 
512
 
    if (pThrow->TimeInAir == 2 || pThrow->TimeInAir == 0)
513
 
    {
514
 
        *pPos = pThrow->FromPos;
515
 
        OffsetHandlePosition(pPos, HandleLen, pPos);
516
 
    }
517
 
    else
518
 
    {
519
 
        float tc = pThrow->TimeInAir - CARRY_TIME;
520
 
        float BallTwist = GetBallTwistAmount(pThrow);
521
 
        Time -= pObj->TimeOffset;
522
 
        if (Time < tc)
523
 
        {
524
 
            /* object in air */
525
 
            POS From, To;
526
 
            float t, b;
527
 
 
528
 
            t = Time / tc;
529
 
            
530
 
            OffsetHandlePosition(&pThrow->FromPos, HandleLen, &From);
531
 
            OffsetHandlePosition(&pThrow->ToPos, HandleLen, &To);
532
 
 
533
 
            b = (To.y - From.y) / tc + pPattern->Alpha * tc;
534
 
            
535
 
            pPos->x = (1.0f - t) * From.x + t * To.x;
536
 
            pPos->z = (1.0f - t) * From.z + t * To.z;
537
 
            pPos->y = -pPattern->Alpha * Time * Time + b * Time + From.y;
538
 
            
539
 
            if (pObj->ObjectType == OBJECT_BALL)
540
 
                pPos->Rot = pObj->TotalTwist + t * BallTwist;
541
 
            else
542
 
            {
543
 
                /* We describe the rotation of a club (or ring) with an
544
 
                 * elevation and rotation but don't include a twist.
545
 
                 * If we ignore twist for the moment, the orientation at a
546
 
                 * rotation of r and an elevation of e can be also be expressed
547
 
                 * by rotating the object a further 180 degrees and sort of
548
 
                 * mirroring the rotation, e.g.:
549
 
                 *    rot = r + 180 and elev = 180 - e
550
 
                 * We can easily show that the maths holds, consider the
551
 
                 * x, y ,z position of the end of a unit length club.
552
 
                 *    y = sin(180 - e) = sin(e)
553
 
                 *    x = cos(180 - e) * sin(r + 180) = -cos(e) * - sin(r)
554
 
                 *    z = cos(180 - e) * cos(r + 180) = -cos(e) * - cos(r)
555
 
                 * When a club is thrown these two potential interpretations
556
 
                 * can produce unexpected results.
557
 
                 * The approach we adopt is that we try and minimise the amount
558
 
                 * of rotation we give a club -- normally this is what happens
559
 
                 * when juggling since it's much easier to spin the club.
560
 
                 *
561
 
                 * When we come to drawing the object the two interpretations
562
 
                 * aren't identical, one causes the object to twist a further
563
 
                 * 180 about its axis.  We avoid the issue by ensuring our
564
 
                 * objects have rotational symmetry of order 2 (e.g. we make
565
 
                 * sure clubs have an even number of stripes) this makes the two
566
 
                 * interpretations appear identical. */
567
 
 
568
 
                float RotAmt = NormaliseAngle(To.Rot - From.Rot);
569
 
 
570
 
                if (RotAmt < -90.0f)
571
 
                {
572
 
                    To.Elev += 180  - 2 * NormaliseAngle(To.Elev);
573
 
                    RotAmt += 180.0f;
574
 
                }
575
 
                else if (RotAmt > 90.0f)
576
 
                {
577
 
                    To.Elev += 180 - 2 * NormaliseAngle(To.Elev);
578
 
                    RotAmt -= 180.0f;
579
 
                }
580
 
 
581
 
                pPos->Rot = From.Rot + t * RotAmt;
582
 
            }
583
 
 
584
 
            pPos->Elev = (1.0f - t) * From.Elev + t * To.Elev;
585
 
 
586
 
        }
587
 
        else
588
 
        {
589
 
            THROW_INFO* pNextThrow = &pPattern->pThrowInfo[
590
 
                   (pObj->ThrowIndex + pThrow->TotalTime) % pPattern->ThrowLen];
591
 
 
592
 
            *pPos = InterpolateCarry(pThrow, pNextThrow, Time - tc);
593
 
 
594
 
            if (pObj->ObjectType == OBJECT_BALL)
595
 
                pPos->Rot = pObj->TotalTwist + BallTwist;
596
 
 
597
 
            OffsetHandlePosition(pPos, HandleLen, pPos);
598
 
        }
599
 
    }
600
 
}
601
 
 
602
 
 
603
 
/* Alpha is used to represent the acceleration due to gravity (in fact
604
 
 * 2 * Alpha is the acceleration).  Alpha is adjusted according to the pattern
605
 
 * being juggled.  My preference is to slow down patterns with lots of objects
606
 
 * -- they move too fast in realtime.  Also I prefer to see a balance between
607
 
 * the size of the figure and the height of objects thrown -- juggling patterns
608
 
 * with large numbers of objects under real gravity can mean balls are lobbed
609
 
 * severe heights.  Adjusting Alpha achieves both these goals.
610
 
 *
611
 
 * Basically we pick a height we'd like to see the biggest throw reach and then
612
 
 * adjust Alpha to meet this. */
613
 
 
614
 
static void SetHeightAndAlpha(PATTERN_INFO* pPattern, 
615
 
    const int* Site, const EXT_SITE_INFO* pExtInfo, int Len)
616
 
{
617
 
    float H;
618
 
    int MaxW = 5;
619
 
    int i;
620
 
    
621
 
    if (Site != NULL)
622
 
    {
623
 
        for (i = 0; i < Len; i++)
624
 
            MaxW = max(MaxW, Site[i]);
625
 
    }
626
 
    else
627
 
    {
628
 
        for (i = 0; i < Len; i++)
629
 
            MaxW = max(MaxW, pExtInfo[i].Weight);
630
 
    }
631
 
    
632
 
    /* H is the ideal max height we'd like our objects to reach.  The formula
633
 
     * was developed by trial and error and was simply stolen from Juggle Saver.
634
 
     * Alpha is then calculated from the classic displacement formula:
635
 
     *   s = 0.5at^2 + ut  (where a = 2 * Alpha)
636
 
     * We know u (the velocity) is zero at the peak, and the object should fall
637
 
     * H units in half the time of biggest throw weight.
638
 
     * Finally we determine the proper height the max throw reaches since this
639
 
     * may not be H because capping may be applied (e.g. for max weights less
640
 
     * than 5). */
641
 
    
642
 
    H = 8.0f * powf(MaxW / 2.0f, 0.8f) + 5.0f;
643
 
    pPattern->Alpha = (2.0f * H) / powf(max(5, MaxW) - CARRY_TIME, 2.0f);
644
 
    pPattern->Height = pPattern->Alpha * powf((MaxW - CARRY_TIME) * 0.5f, 2);
645
 
}
646
 
 
647
 
 
648
 
/* Where positions and spin info is not specified, generate suitable default
649
 
 * values. */
650
 
 
651
 
static int GetDefaultSpins(int Weight)
652
 
{
653
 
    if (Weight < 3)
654
 
        return 0;
655
 
    else if (Weight < 4)
656
 
        return 1;
657
 
    else if (Weight < 7)
658
 
        return 2;
659
 
    else
660
 
        return 3;
661
 
}
662
 
 
663
 
 
664
 
static void GetDefaultFromPosition(unsigned char Side, int Weight, POS* pPos)
665
 
{
666
 
    if (Weight > 4 && Weight % 2 != 0)
667
 
        pPos->x = Side ?  -0.06f : 0.06f;
668
 
    else if (Weight == 0 || Weight == 2)
669
 
        pPos->x = Side ? 1.6f :  -1.6f;
670
 
    else
671
 
        pPos->x = Side? 0.24f :  -0.24f;
672
 
 
673
 
    pPos->y = (Weight == 2 || Weight == 0) ? -0.25f : 0.0f;
674
 
 
675
 
    pPos->Rot = (Weight % 2 == 0 ? -23.5f : 27.0f) * (Side ? -1.0f : 1.0f);
676
 
 
677
 
    pPos->Elev = Weight == 1 ? -30.0f : 0.0f;
678
 
    pPos->z = 0.0f;
679
 
}
680
 
 
681
 
 
682
 
static void GetDefaultToPosition(unsigned char Side, int Weight, POS* pPos)
683
 
{
684
 
    if (Weight == 1)
685
 
        pPos->x = Side ?  -1.0f : 1.0f;
686
 
    else if (Weight % 2 == 0)
687
 
        pPos->x = Side ? 2.8f :  -2.8f;
688
 
    else
689
 
        pPos->x = Side?  -3.1f : 3.1f;
690
 
 
691
 
    pPos->y = -0.5f;
692
 
 
693
 
    pPos->Rot = (Side ? -35.0f : 35.0f) * (Weight % 2 == 0 ? -1.0f : 1.0f);
694
 
    
695
 
    if (Weight < 2)
696
 
        pPos->Elev = -30.0f;
697
 
 
698
 
    else if (Weight < 4)
699
 
        pPos->Elev = 360.0f - 50.0f;
700
 
    else if (Weight < 7)
701
 
        pPos->Elev = 720.0f - 50.0f;
702
 
    else
703
 
        pPos->Elev = 360.0f * GetDefaultSpins(Weight) - 50.0f;
704
 
    pPos->z = 0.0f;
705
 
}
706
 
 
707
 
 
708
 
/* Update the members of PATTERN_INFO for a given juggling pattern.  The pattern
709
 
 * can come from an ordinary siteswap (Site != NULL) or from a Juggle Saver
710
 
 * compatible pattern that contains, position and object info etc. 
711
 
 * We assume that patterns are valid and have at least one object (a site of
712
 
 * zeros is invalid).  The ones we generate randomly are safe. */
713
 
 
714
 
static void InitPatternInfo(PATTERN_INFO* pPattern,
715
 
    const int* Site, const EXT_SITE_INFO* pExtInfo, int Len)
716
 
{
717
 
    /* Double up on the length of the site if it's of an odd length. 
718
 
     * This way we can store position information: even indices are on one
719
 
     * side and odds are on the other. */
720
 
    int InfoLen = Len % 2 == 1 ? Len * 2 : Len;
721
 
    int i;
722
 
    THROW_INFO* pInfo = (THROW_INFO*) calloc(InfoLen, sizeof(THROW_INFO));
723
 
    int Objects = 0;
724
 
    unsigned char* pUsed;
725
 
    
726
 
    pPattern->MaxWeight = 0;
727
 
    pPattern->ThrowLen = InfoLen;
728
 
    pPattern->pThrowInfo = pInfo;
729
 
    
730
 
    SetHeightAndAlpha(pPattern, Site, pExtInfo, Len);
731
 
 
732
 
    /* First pass through we assign the things we know about for sure just by
733
 
     * looking at the throw weight at this position.  This includes TimeInAir;
734
 
     * the throw and catch positions; and throw and catch velocities.
735
 
     * Other information, like the total time for the throw (i.e. when the
736
 
     * object is thrown again) relies on how the rest of the pattern is 
737
 
     * structured and we defer this task for successive passes and just make
738
 
     * guesses at this stage. */
739
 
    
740
 
    for (i = 0; i < InfoLen; i++)
741
 
    {
742
 
        float t1;
743
 
        int w = pExtInfo != NULL ? pExtInfo[i % Len].Weight : Site[i % Len];
744
 
 
745
 
        pInfo[i].TotalTime = pInfo[i].TimeInAir = w;
746
 
        pInfo[(w + i) % Len].PrevThrow = i;
747
 
 
748
 
        /* work out where we are throwing this object from and where it's going
749
 
         * to land. */
750
 
 
751
 
        if (pExtInfo == NULL || (pExtInfo[i % Len].Flags & HAS_FROM_POS) == 0)
752
 
            GetDefaultFromPosition(i % 2, w, &pInfo[i].FromPos);
753
 
        else
754
 
            pInfo[i].FromPos = pExtInfo[i % Len].FromPos;
755
 
 
756
 
        if (pExtInfo == NULL || (pExtInfo[i % Len].Flags & HAS_TO_POS) == 0)
757
 
            GetDefaultToPosition(i % 2, w, &pInfo[i].ToPos);
758
 
        else
759
 
            pInfo[i].ToPos = pExtInfo[i % Len].ToPos;
760
 
 
761
 
        /* calculate the velocity the object is moving at the start and end
762
 
         * points -- this information is used to interpolate the hand position
763
 
         * and to determine how the object is moved while it's carried to the 
764
 
         * next throw position.
765
 
         *
766
 
         * The throw motion is governed by a parabola of the form:
767
 
         *   y(t) = a * t ^ 2 + b * t + c
768
 
         * Assuming at the start of the throw y(0) = y0; when it's caught
769
 
         * y(t1) = y1; and the accelation is -2.0 * alpha the equation can be
770
 
         * rewritten as:
771
 
         *   y(t) = -alpha * t ^ 2 + (alpha * t1 + (y1 - y0) / t1) * t + y0
772
 
         * making the velocity:
773
 
         *   y'(t) = -2.0 * alpha * t + (alpha * t1 + (y1 - y0) / t1)
774
 
         * To get the y component of velocity first we determine t1, which is
775
 
         * the throw weight minus the time spent carrying the object.  Then
776
 
         * perform the relevant substitutions into the above.
777
 
         * (note: y'(t) = y'(0) - 2.0 * alpha * t)
778
 
         * 
779
 
         * The velocity in the x direction is constant and can be simply
780
 
         * obtained from:
781
 
         *   x' = (x1 - x0) / t1
782
 
         * where x0 and x1 are the start and end x-positions respectively.
783
 
         */
784
 
 
785
 
        t1 = w - CARRY_TIME;
786
 
 
787
 
        pInfo[i].FromVelocity.y = pPattern->Alpha * t1 + 
788
 
            (pInfo[i].ToPos.y - pInfo[i].FromPos.y) / t1;
789
 
        pInfo[i].ToVelocity.y = 
790
 
            pInfo[i].FromVelocity.y - 2.0f * pPattern->Alpha * t1;
791
 
        pInfo[i].FromVelocity.x = pInfo[i].ToVelocity.x = 
792
 
            (pInfo[i].ToPos.x - pInfo[i].FromPos.x) / t1;
793
 
        pInfo[i].FromVelocity.z = pInfo[i].ToVelocity.z = 
794
 
            (pInfo[i].ToPos.z - pInfo[i].FromPos.z) / t1;
795
 
        pInfo[i].FromVelocity.Rot = pInfo[i].ToVelocity.Rot =
796
 
            (pInfo[i].ToPos.Rot - pInfo[i].FromPos.Rot) / t1;
797
 
        pInfo[i].FromVelocity.Elev = pInfo[i].ToVelocity.Elev =
798
 
            (pInfo[i].ToPos.Elev - pInfo[i].FromPos.Elev) / t1;
799
 
 
800
 
 
801
 
        if (pExtInfo != NULL && (pExtInfo[i % Len].Flags & HAS_SNATCH) != 0)
802
 
        {
803
 
            pInfo[i].ToVelocity.x = pExtInfo[i % Len].SnatchX;
804
 
            pInfo[i].ToVelocity.y = pExtInfo[i % Len].SnatchY;
805
 
        }
806
 
 
807
 
        if (pExtInfo != NULL && (pExtInfo[i % Len].Flags & HAS_SPINS) != 0)
808
 
        {
809
 
            pInfo[i].ToPos.Elev = 360.0f * pExtInfo[i % Len].Spins +
810
 
                NormaliseAngle(pInfo[i].ToPos.Elev);
811
 
        }
812
 
 
813
 
        Objects += w;
814
 
        if (w > pPattern->MaxWeight)
815
 
            pPattern->MaxWeight = w;
816
 
    }
817
 
 
818
 
    Objects /= InfoLen;
819
 
 
820
 
    /* Now we go through again and work out exactly how long it is before the
821
 
     * object is thrown again (ie. the TotalTime) typically this is the same
822
 
     * as the time in air, however when we have a throw weight of '2' it's
823
 
     * treated as a hold and we increase the total time accordingly. */
824
 
 
825
 
    for (i = 0; i < InfoLen; i++)
826
 
    {
827
 
        if (pInfo[i].TimeInAir != 2)
828
 
        {
829
 
            int Next = pInfo[i].TimeInAir + i;
830
 
            while (pInfo[Next % InfoLen].TimeInAir == 2)
831
 
            {
832
 
                Next += 2;
833
 
                pInfo[i].TotalTime += 2;
834
 
            }
835
 
 
836
 
            /* patch up the Prev index.  We don't bother to see if this
837
 
             * is different from before since it's always safe to reassign it */
838
 
            pInfo[Next % InfoLen].PrevThrow = i;
839
 
        }
840
 
    }
841
 
 
842
 
    /* then we work our way through again figuring out where the hand goes to
843
 
     * catch something as soon as it has thrown the current object. */
844
 
 
845
 
    for (i = 0; i < InfoLen; i++)
846
 
    {
847
 
        if (pInfo[i].TimeInAir != 0 && pInfo[i].TimeInAir != 2)
848
 
        {
849
 
            /* what we're trying to calculate is how long the hand that threw
850
 
             * the current object has to wait before it throws another.
851
 
             * Typically this is two beats later.  However '0' in the site swap
852
 
             * represents a gap in a catch, and '2' represents a hold.  We skip
853
 
             * over these until we reach the point where a ball is actually
854
 
             * thrown. */
855
 
            int Wait = 2;
856
 
            while (pInfo[(i + Wait) % InfoLen].TimeInAir == 2 || 
857
 
                pInfo[(i + Wait) % InfoLen].TimeInAir == 0)
858
 
            {
859
 
                Wait += 2;
860
 
            }
861
 
            pInfo[i].NextForHand = Wait;
862
 
        }
863
 
        else
864
 
        {
865
 
            /* Be careful to ensure the current weight isn't one we're trying
866
 
             * to step over; otherwise we could potentially end up in an 
867
 
             * infinite loop.  The value we assign may end up being used
868
 
             * in patterns with infinite gaps (e.g. 60) or infinite holds
869
 
             * (e.g. 62) in both cases, setting a wait of 2 ensures things
870
 
             * are well behaved. */
871
 
            pInfo[i].NextForHand = 2;
872
 
        }
873
 
    }
874
 
 
875
 
    /* Now work out the starting positions for the objects.  To do this we
876
 
     * unweave the initial throws so we can pick out the individual threads. */
877
 
 
878
 
    pUsed = (unsigned char*) 
879
 
        malloc(sizeof(unsigned char) * pPattern->MaxWeight);
880
 
    pPattern->Objects = Objects;
881
 
    pPattern->pObjectInfo = (OBJECT_POSITION*) calloc(
882
 
        Objects, sizeof(OBJECT_POSITION));
883
 
 
884
 
    for (i = 0; i < pPattern->MaxWeight; i++)
885
 
        pUsed[i] = 0;
886
 
 
887
 
    for (i = 0; i < pPattern->MaxWeight; i++)
888
 
    {
889
 
        int w = pInfo[i % InfoLen].TimeInAir;
890
 
        if (pUsed[i] == 0 &&  w != 0)
891
 
        {
892
 
            Objects--;
893
 
            pPattern->pObjectInfo[Objects].TimeOffset = i;
894
 
            pPattern->pObjectInfo[Objects].ThrowIndex = i % InfoLen;
895
 
            pPattern->pObjectInfo[Objects].TotalTwist = 0.0f;
896
 
 
897
 
            if (pExtInfo != NULL && 
898
 
                pExtInfo[i % Len].ObjectType != OBJECT_DEFAULT)
899
 
            {
900
 
                pPattern->pObjectInfo[Objects].ObjectType =
901
 
                    pExtInfo[i % Len].ObjectType;
902
 
            }
903
 
            else
904
 
            {
905
 
                pPattern->pObjectInfo[Objects].ObjectType = (1 + random() % 3);
906
 
            }
907
 
        }
908
 
 
909
 
        if (w + i < pPattern->MaxWeight)
910
 
            pUsed[w + i] = 1;
911
 
        
912
 
    }
913
 
 
914
 
    pPattern->LeftHand.TimeOffset = pPattern->LeftHand.ThrowIndex = 0;
915
 
    pPattern->RightHand.TimeOffset = pPattern->RightHand.ThrowIndex = 1;
916
 
    
917
 
    free(pUsed);
918
 
}
919
 
 
920
 
 
921
 
static void ReleasePatternInfo(PATTERN_INFO* pPattern)
922
 
{
923
 
    free(pPattern->pObjectInfo);
924
 
    free(pPattern->pThrowInfo);
925
 
}
926
 
 
927
 
 
928
 
/*****************************************************************************
929
 
 *
930
 
 * Sites
931
 
 *
932
 
 ****************************************************************************/
933
 
 
934
 
/* Generate a random site swap.  We assume that MaxWeight >= ObjCount and
935
 
 * Len >= MaxWeight. */
936
 
 
937
 
static int* Generate(int Len, int MaxWeight, int ObjCount)
938
 
{
939
 
    int* Weight = (int*) calloc(Len, sizeof(int));
940
 
    int* Used = (int*) calloc(Len, sizeof(int));
941
 
    int* Options = (int*) calloc(MaxWeight + 1, sizeof(int));
942
 
    int nOpts;
943
 
    int i, j;
944
 
 
945
 
    for (i = 0; i < Len; i++)
946
 
        Weight[i] = Used[i] = -1;
947
 
    
948
 
    /* Pick out a unique the starting position for each object.  -2 is put in
949
 
     * the Used array to signify this is a starting position. */
950
 
 
951
 
    while (ObjCount > 0)
952
 
    {
953
 
        nOpts = 0;
954
 
        for (j = 0; j < MaxWeight; j++)
955
 
        {
956
 
            if (Used[j] == -1)
957
 
                Options[nOpts++] = j;
958
 
        }
959
 
 
960
 
        Used[Options[random() % nOpts]] = -2;
961
 
        ObjCount--;
962
 
    }
963
 
    
964
 
    /* Now work our way through the pattern moving throws into an available
965
 
     * landing positions. */
966
 
    for (i = 0; i < Len; i++)
967
 
    {
968
 
        if (Used[i] == -1)
969
 
        {
970
 
            /* patch up holes in the pattern to zeros */
971
 
            Used[i] = 1;
972
 
            Weight[i] = 0;
973
 
        }
974
 
        else
975
 
        {
976
 
            /* Work out the possible places where a throw can land and pick a 
977
 
             * weight at random. */
978
 
            int w;
979
 
            nOpts = 0;
980
 
 
981
 
            for (j = 0 ; j <= MaxWeight; j++)
982
 
            {
983
 
                if (Used[(i + j) % Len] == -1)
984
 
                    Options[nOpts++] = j;
985
 
            }
986
 
            
987
 
            w = Options[random() % nOpts];
988
 
            Weight[i] = w;
989
 
            
990
 
            /* For starting throws make position available for a throw to land.
991
 
             * Because Len >= MaxWeight these positions will only be filled when
992
 
             * a throw wraps around the end of the site swap and therefore we
993
 
             * can guarantee the all the object threads will be tied up. */
994
 
            if (Used[i] == -2)
995
 
                Used[i] = -1;
996
 
            
997
 
            Used[(i + w) % Len] = 1;
998
 
        }
999
 
    }
1000
 
 
1001
 
    free(Options);
1002
 
    free(Used);
1003
 
    return Weight;
1004
 
}
1005
 
 
1006
 
 
1007
 
/* Routines to parse the Juggle Saver patterns.  These routines are a bit yucky
1008
 
 * and make the big assumption that the patterns are well formed.  This is fine
1009
 
 * as it stands because only 'good' ones are used but if the code is ever
1010
 
 * extended to read arbitrary patterns (say from a file) then these routines
1011
 
 * need to be beefed up. */
1012
 
 
1013
 
/* The position text looks something like (x,y,z[,rot[,elev]])
1014
 
 * where the stuff in square brackets is optional */
1015
 
 
1016
 
static unsigned char ParsePositionText(const char** ppch, POS* pPos)
1017
 
{
1018
 
    const char* pch = *ppch;
1019
 
    unsigned char OK;
1020
 
    char szTemp[32];
1021
 
    char* pOut;
1022
 
    float* Nums[4];
1023
 
    int i;
1024
 
    
1025
 
    Nums[0] = &pPos->x;
1026
 
    Nums[1] = &pPos->y;
1027
 
    Nums[2] = &pPos->Rot;
1028
 
    Nums[3] = &pPos->Elev;
1029
 
 
1030
 
 
1031
 
    while (*pch == ' ')
1032
 
        pch++;
1033
 
    
1034
 
    OK = *pch == '(';
1035
 
    
1036
 
    if (OK)
1037
 
        pch++;
1038
 
 
1039
 
    for (i = 0; OK && i < 4; i++)
1040
 
    {
1041
 
        pOut = szTemp;
1042
 
        while (*pch == ' ')
1043
 
            pch++;
1044
 
        while (*pch != ',' && *pch != '\0' && *pch != ')' && *pch != ' ')
1045
 
            *pOut++ = *pch++;
1046
 
        *pOut = '\0';
1047
 
 
1048
 
        if (szTemp[0] != '\0')
1049
 
            *Nums[i] = (float) atof(szTemp);
1050
 
 
1051
 
        while (*pch == ' ')
1052
 
            pch++;
1053
 
 
1054
 
        if (i < 3)
1055
 
        {
1056
 
            if (*pch == ',')
1057
 
                pch++;
1058
 
            else if (*pch == ')')
1059
 
                break;
1060
 
            else
1061
 
                OK = 0;
1062
 
        }
1063
 
    }
1064
 
 
1065
 
    if (OK)
1066
 
    {
1067
 
        while (*pch == ' ')
1068
 
            pch++;        
1069
 
        if (*pch == ')')
1070
 
            pch++;
1071
 
        else
1072
 
            OK = 0;
1073
 
    }
1074
 
 
1075
 
    *ppch = pch;
1076
 
 
1077
 
    return OK;
1078
 
}
1079
 
 
1080
 
 
1081
 
static EXT_SITE_INFO* ParsePattern(const char* Site, int* pLen)
1082
 
{
1083
 
    const char* pch = Site;
1084
 
    int Len = 0;
1085
 
    EXT_SITE_INFO* pInfo = NULL;
1086
 
    unsigned char OK = 1;
1087
 
 
1088
 
    while (OK && *pch != 0)
1089
 
    {
1090
 
        EXT_SITE_INFO Info;
1091
 
        Info.Flags = 0;
1092
 
 
1093
 
        while (*pch == ' ') pch++;
1094
 
 
1095
 
        OK = *pch != '\0';
1096
 
 
1097
 
        if (OK)
1098
 
            Info.Weight = *pch >= 'A' ? *pch + 10 - 'A' : *pch - '0';
1099
 
 
1100
 
        /* parse object type */
1101
 
        if (OK)
1102
 
        {
1103
 
            pch++;
1104
 
            while (*pch == ' ') pch++;
1105
 
 
1106
 
            if (*pch == 'b' || *pch == 'B')
1107
 
            {
1108
 
                Info.ObjectType = OBJECT_BALL;
1109
 
                pch++;
1110
 
            }
1111
 
            else if (*pch == 'c' || *pch == 'C')
1112
 
            {
1113
 
                Info.ObjectType = OBJECT_CLUB;
1114
 
                pch++;
1115
 
            }
1116
 
            else if (*pch == 'r' || *pch == 'R')
1117
 
            {
1118
 
                Info.ObjectType = OBJECT_RING;
1119
 
                pch++;
1120
 
            }
1121
 
            else if (*pch == 'd' || *pch == 'D')
1122
 
            {
1123
 
                Info.ObjectType = OBJECT_DEFAULT;
1124
 
                pch++;
1125
 
            }
1126
 
            else
1127
 
            {
1128
 
                Info.ObjectType = OBJECT_DEFAULT;
1129
 
            }
1130
 
        }
1131
 
 
1132
 
        /* Parse from position */
1133
 
        if (OK)
1134
 
        {
1135
 
            while (*pch == ' ') pch++;
1136
 
            if (*pch == '@')
1137
 
            {
1138
 
                pch++;
1139
 
                GetDefaultFromPosition(Len % 2, Info.Weight, &Info.FromPos);
1140
 
                Info.Flags |= HAS_FROM_POS;
1141
 
                OK = ParsePositionText(&pch, &Info.FromPos);
1142
 
            }
1143
 
        }
1144
 
 
1145
 
        /* Parse to position */
1146
 
        if (OK)
1147
 
        {
1148
 
            while (*pch == ' ') pch++;
1149
 
            if (*pch == '>')
1150
 
            {
1151
 
                pch++;
1152
 
                GetDefaultToPosition(Len % 2, Info.Weight, &Info.ToPos);
1153
 
                Info.Flags |= HAS_TO_POS;
1154
 
                OK = ParsePositionText(&pch, &Info.ToPos);
1155
 
            }
1156
 
        }
1157
 
 
1158
 
        /* Parse snatch */
1159
 
        if (OK)
1160
 
        {
1161
 
            while (*pch == ' ') pch++;
1162
 
            if (*pch == '/')
1163
 
            {
1164
 
                POS Snatch;
1165
 
                pch++;
1166
 
                Info.Flags |= HAS_SNATCH;
1167
 
                OK = ParsePositionText(&pch, &Snatch);
1168
 
                Info.SnatchX = Snatch.x;
1169
 
                Info.SnatchY = Snatch.y;
1170
 
            }
1171
 
        }
1172
 
 
1173
 
        /* Parse Spins */
1174
 
        if (OK)
1175
 
        {
1176
 
            while (*pch == ' ') pch++;
1177
 
            if (*pch == '*')
1178
 
            {
1179
 
                pch++;
1180
 
                OK = 0;
1181
 
                Info.Spins = 0;
1182
 
                while (*pch >= '0' && *pch <= '9')
1183
 
                {
1184
 
                    OK = 1;
1185
 
                    Info.Spins = Info.Spins * 10 + *pch - '0';
1186
 
                    pch++;
1187
 
                }
1188
 
            }
1189
 
            else
1190
 
                Info.Spins = GetDefaultSpins(Info.Weight);
1191
 
 
1192
 
            Info.Flags |= HAS_SPINS;
1193
 
        }
1194
 
 
1195
 
        if (OK)
1196
 
        {
1197
 
            if (pInfo == NULL)
1198
 
                pInfo = (EXT_SITE_INFO*) malloc(sizeof(EXT_SITE_INFO));
1199
 
            else
1200
 
                pInfo = (EXT_SITE_INFO*) realloc(pInfo, (Len + 1) * sizeof(EXT_SITE_INFO));
1201
 
 
1202
 
            pInfo[Len] = Info;
1203
 
            Len++;
1204
 
        }
1205
 
    }
1206
 
 
1207
 
    if (!OK && pInfo != NULL)
1208
 
    {
1209
 
        free(pInfo);
1210
 
        pInfo = NULL;
1211
 
    }
1212
 
 
1213
 
    *pLen = Len;
1214
 
 
1215
 
    return pInfo;
1216
 
}
1217
 
 
1218
 
 
1219
 
/*****************************************************************************
1220
 
 *
1221
 
 *  Juggle Saver Patterns
1222
 
 *
1223
 
 *****************************************************************************
1224
 
 *
1225
 
 * This is a selection of some of the more interesting patterns from taken
1226
 
 * from the Juggle Saver sites.txt file.  I've only used patterns that I
1227
 
 * originally created.
1228
 
 */
1229
 
 
1230
 
static const char* PatternText[] =
1231
 
{
1232
 
    "9b@(-2.5,0,-70,40)>(2.5,0,70)*2 1b@(1,0,10)>(-1,0,-10)",
1233
 
    
1234
 
    "3B@(1,-0.4)>(2,4.2)/(-2,1)3B@(-1.8,4.4)>(-2.1,0)",
1235
 
    
1236
 
    "7c@(-2,0,-20)>(1.2,0,-5)7c@(2,0,20)>(-1.2,0,5)",
1237
 
    
1238
 
    "3b@(-0.5,0)>(1.5,0) 3b@(0.5,0)>(-1.5,0) 3r@(-2.5,3,-90,80)>(2,1,90,30)"
1239
 
    "3b@(0.5,0)>(-1.5,0) 3b@(-0.5,0)>(1.5,0) 3r@(2.5,3,90,80)>(-2,1,-90,30)",
1240
 
    
1241
 
    "5c@(2,1.9,10)>(-1,1,10)5c@(2,1.8,10)>(-0.5,1.6,10)/(5,-1)"
1242
 
    "5c@(1.6,0.2,10)>(0,-1,10)/(9,-2)5c@(-2,1.9,-10)>(1,1,-10)"
1243
 
    "5c@(-2,1.8,-10)>(0.5,1.6,-10)/(-5,-1)5@(-1.6,0.2,-10)>(0,-1,-10)/(-9,-2)",
1244
 
    
1245
 
    "3c@(-1.5,0,0)>(-1.5,1,0)3c@(1.5,-0.2,0)>(1.5,-0.1,0)3c@(0,-0.5,0)>(0,1,0)"
1246
 
    "3@(-1.5,2,0)>(-1.5,-1,0)3@(1.5,0,0)>(1.5,1,0)3@(0,0,0)>(0,-0.5,0)",
1247
 
    
1248
 
    "9c@(-2.5,0,-70,40)>(2.5,0,70)*2 1c@(1,0,10)>(-1,0,-10)*0",
1249
 
    
1250
 
    "3c@(2,0.5,60,0)>(1.5,4,60,80)/(-6,-12)"
1251
 
    "3c@(-2,0.5,-60,0)>(-1.5,4,-60,80)/(6,-12)",
1252
 
    
1253
 
    "3c@(-0.2,0)>(1,0)3c@(0.2,0)>(-1,0)3c@(-2.5,2,-85,30)>(2.5,2,85,40)*2 "
1254
 
    "3@(0.2,0)>(-1,0) 3@(-0.2,0)>(1,0) 3@(2.5,2,85,30)>(-2.5,2,-85,40)*2",
1255
 
    
1256
 
    "3c@(-0.5,-0.5,20,-30)>(2.6,4.3,60,60)/(0,1)*1 "
1257
 
    "3c@(1.6,5.6,60,80)>(-2.6,0,-80)*0",
1258
 
    
1259
 
    "5c@(-0.3,0,10)>(1.2,0,10) 5c@(0.3,0,-10)>(-1.2,0,-10)"
1260
 
    "5c@(-0.3,0,10)>(1.2,0,10) 5c@(0.3,0,-10)>(-1.2,0,-10)"
1261
 
    "5c@(-3,3.5,-65,80)>(3,2.5,65) 5c@(0.3,0,-10)>(-1.2,0,-10)"
1262
 
    "5@(-0.3,0,10)>(1.2,0,10) 5@(0.3,0,-10)>(-1.2,0,-10)"
1263
 
    "5@(-0.3,0,10)>(1.2,0,10)5@(3,3.5,65,80)>(-3,2.5,-65)"
1264
 
};
1265
 
 
1266
 
 
1267
 
/*****************************************************************************
1268
 
 *
1269
 
 * Rendering
1270
 
 *
1271
 
 *****************************************************************************/
1272
 
 
1273
 
static const float FOV = 70.0f;
1274
 
static const float BodyCol[] = {0.6f, 0.6f, 0.45f, 1.0f};
1275
 
static const float HandleCol[] = {0.45f, 0.45f, 0.45f, 1.0f};
1276
 
static const float LightPos[] = {0.0f, 200.0f, 400.0f, 1.0f};
1277
 
static const float LightDiff[] = {1.0f, 1.0f, 1.0f, 0.0f};
1278
 
static const float LightAmb[] = {0.02f, 0.02f, 0.02f, 0.0f};
1279
 
static const float ShoulderPos[3] = {0.95f, 2.1f, 1.7f};
1280
 
static const float DiffCol[] = {1.0f, 0.0f, 0.0f, 1.0f};
1281
 
static const float SpecCol[] = {1.0f, 1.0f, 1.0f, 1.0f};
1282
 
 
1283
 
static const float BallRad = 0.34f;
1284
 
static const float UArmLen = 1.9f;
1285
 
static const float LArmLen = 2.3f;
1286
 
 
1287
 
#define DL_BALL 0
1288
 
#define DL_CLUB 1
1289
 
#define DL_RING 2
1290
 
#define DL_TORSO 3
1291
 
#define DL_FOREARM 4
1292
 
#define DL_UPPERARM 5
1293
 
 
1294
 
static const float AltCols[][4] =
1295
 
{
1296
 
    {0.0f, 0.7f, 0.0f, 1.0f},
1297
 
    {0.0f, 0.0f, 0.9f, 1.0f},
1298
 
    {0.0f, 0.9f, 0.9f, 1.0f},
1299
 
    {0.45f, 0.0f, 0.9f, 1.0f},
1300
 
    {0.9f, 0.45f, 0.0f, 1.0f},
1301
 
    {0.0f, 0.45f, 0.9f, 1.0f},
1302
 
    {0.9f, 0.0f, 0.9f, 1.0f},
1303
 
    {0.9f, 0.9f, 0.0f, 1.0f},
1304
 
    {0.9f, 0.0f, 0.45f, 1.0f},
1305
 
    {0.45f, 0.15f, 0.6f, 1.0f}, 
1306
 
    {0.9f, 0.0f, 0.0f, 1.0f},
1307
 
    {0.0f, 0.9f, 0.45f, 1.0f},
1308
 
};
1309
 
 
1310
 
static const float Cols[][4] =
1311
 
{
1312
 
    {0.9f, 0.0f, 0.0f, 1.0f},  /*  0 */
1313
 
    {0.0f, 0.7f, 0.0f, 1.0f},  /*  1 */
1314
 
    {0.0f, 0.0f, 0.9f, 1.0f},  /*  2 */
1315
 
    {0.0f, 0.9f, 0.9f, 1.0f},  /*  3 */
1316
 
    {0.9f, 0.0f, 0.9f, 1.0f},  /*  4 */
1317
 
    {0.9f, 0.9f, 0.0f, 1.0f},  /*  5 */
1318
 
    {0.9f, 0.45f, 0.0f, 1.0f}, /*  6 */
1319
 
    {0.9f, 0.0f, 0.45f, 1.0f}, /*  7 */
1320
 
    {0.45f, 0.9f, 0.0f, 1.0f}, /*  8 */
1321
 
    {0.0f, 0.9f, 0.45f, 1.0f}, /*  9 */
1322
 
    {0.45f, 0.0f, 0.9f, 1.0f}, /* 10 */
1323
 
    {0.0f, 0.45f, 0.9f, 1.0f}, /* 11 */
1324
 
};
1325
 
 
1326
 
static int InitGLDisplayLists(void);
1327
 
 
1328
 
 
1329
 
static void InitGLSettings(RENDER_STATE* pState, int WireFrame)
1330
 
{
1331
 
    memset(pState, 0, sizeof(RENDER_STATE));
1332
 
    
1333
 
    pState->trackball = gltrackball_init ();
1334
 
 
1335
 
    if (WireFrame)
1336
 
        glPolygonMode(GL_FRONT, GL_LINE);
1337
 
    
1338
 
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
1339
 
 
1340
 
    glLightfv(GL_LIGHT0, GL_POSITION, LightPos);
1341
 
    glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiff);
1342
 
    glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmb);
1343
 
    
1344
 
    glEnable(GL_SMOOTH);
1345
 
    glEnable(GL_LIGHTING);
1346
 
    glEnable(GL_LIGHT0);
1347
 
 
1348
 
    glDepthFunc(GL_LESS);
1349
 
    glEnable(GL_DEPTH_TEST);
1350
 
 
1351
 
    glCullFace(GL_BACK);
1352
 
    glEnable(GL_CULL_FACE);
1353
 
    
1354
 
    pState->DLStart = InitGLDisplayLists();
1355
 
}
1356
 
 
1357
 
 
1358
 
static void SetCamera(RENDER_STATE* pState)
1359
 
{
1360
 
    /* Try to work out a sensible place to put the camera so that more or less
1361
 
     * the whole juggling pattern fits into the screen. We assume that the
1362
 
         * pattern is height limited (i.e. if we get the height right then the width
1363
 
         * will be OK).  This is a pretty good assumption given that the screen
1364
 
         * tends to wider than high, and that a juggling pattern is normally much
1365
 
         * higher than wide.
1366
 
     *
1367
 
     * If I could draw a diagram here then it would be much easier to
1368
 
     * understand but my ASCII-art skills just aren't up to it.  
1369
 
     *
1370
 
     * Basically we estimate a bounding volume for the juggler and objects 
1371
 
     * throughout the pattern.  We don't fully account for the fact that the
1372
 
     * juggler moves across the stage in an epicyclic-like motion and instead
1373
 
     * use the near and far planes in x-y (with z = +/- w).  We also
1374
 
     * assume that the scene is centred at x=0, this reduces our task to finding
1375
 
     * a bounding rectangle.  Finally we need to make an estimate of the
1376
 
     * height - for this we work out the max height of a standard throw or max
1377
 
     * weight from the pattern; we then do a bit of adjustment to account for
1378
 
     * a throw occurring at non-zero y values.
1379
 
     *
1380
 
     * Next we work out the best way to fit this rectangle into the perspective
1381
 
     * transform.  Based on the angle of elevation (+ve angle looks down) and
1382
 
     * the FOV we can work out whether it's the near or far corners that are
1383
 
     * the extreme points.  And then trace back from them to find the eye
1384
 
     * point.
1385
 
     *
1386
 
     */
1387
 
     
1388
 
    float ElevRad = pState->CameraElev * PI / 180.0f;
1389
 
    float w = 3.0f;
1390
 
    float cy, cz;
1391
 
    float ey, ez;
1392
 
    float d;
1393
 
    float H = 0.0f;
1394
 
    int i;
1395
 
    float a;
1396
 
    
1397
 
    float tz, ty, ta;
1398
 
    float bz, by, ba;
1399
 
    const PATTERN_INFO* pPattern = pState->pPattern;
1400
 
 
1401
 
    glMatrixMode(GL_PROJECTION);
1402
 
    glLoadIdentity();
1403
 
        
1404
 
    for (i = 0; i < pPattern->ThrowLen; i++)
1405
 
        H = max(H, pPattern->pThrowInfo[i].FromPos.y);
1406
 
        
1407
 
    H += pPattern->Height;
1408
 
    
1409
 
    ElevRad = pState->CameraElev * PI / 180.0f;
1410
 
    
1411
 
    /* ta is the angle from a point on the top of the bounding area to the eye
1412
 
     * similarly ba is the angle from a point on the bottom. */
1413
 
    ta = (pState->CameraElev  - (FOV - 10.0f) / 2.0f) * PI / 180.0f;
1414
 
    ba = (pState->CameraElev  + (FOV - 10.0f) / 2.0f) * PI / 180.0f;
1415
 
 
1416
 
    /* tz and bz hold the z location of the top and bottom extreme points.
1417
 
     * For the top, if the angle to the eye location is positive then the
1418
 
     * extreme point is with far z corner (the camera looks in -ve z).
1419
 
     * The logic is reserved for the bottom. */
1420
 
    tz = ta >= 0.0f ? -w : w;
1421
 
    bz = ba >= 0.0f ? w : -w;
1422
 
    
1423
 
    ty = H;
1424
 
    by = -1.0f;
1425
 
    
1426
 
    /* Solve of the eye location by using a bit of geometry.
1427
 
     * We know the eye lies on intersection of two lines.  One comes from the
1428
 
     * top and other from the bottom. Giving two equations:
1429
 
     *   ez = tz + a * cos(ta) = bz + b * cos(ba)
1430
 
     *   ey = ty + a * sin(ta) = by + b * sin(ba)
1431
 
     * We don't bother to solve for b and use Crammer's rule to get
1432
 
     *         | bz-tz  -cos(ba) |
1433
 
     *         | by-ty  -sin(ba) |     
1434
 
     *   a =  ----------------------
1435
 
     *        | cos(ta)   -cos(ba) |
1436
 
     *        | sin(ta)   -sin(ba) |
1437
 
     */
1438
 
    d = cosf(ba) * sinf(ta) - cosf(ta) * sinf(ba);
1439
 
    a = (cosf(ba) * (by - ty) - sinf(ba) * (bz - tz)) / d;
1440
 
    
1441
 
    ey = ty + a * sinf(ta);
1442
 
    ez = tz + a * cosf(ta);
1443
 
    
1444
 
    /* now work back from the eye point to get the lookat location */
1445
 
    cz = 0.0;
1446
 
    cy = ey - ez * tanf(ElevRad);
1447
 
    
1448
 
    /* use the distance from the eye to the scene centre to get a measure
1449
 
     * of what the far clipping should be.  We then add on a bit more to be 
1450
 
     * comfortable */
1451
 
    d = sqrtf(ez * ez + (cy - ey) * (cy - ey));
1452
 
    
1453
 
    gluPerspective(FOV, pState->AspectRatio, 0.1f, d + 20.0f);
1454
 
    gluLookAt(0.0, ey, ez, 0.0, cy, cz, 0.0, 1.0, 0.0);
1455
 
 
1456
 
    glMatrixMode(GL_MODELVIEW);
1457
 
}
1458
 
 
1459
 
 
1460
 
static void ResizeGL(RENDER_STATE* pState, int w, int h)
1461
 
{
1462
 
    glViewport(0, 0, w, h);
1463
 
    pState->AspectRatio = (float) w / h;
1464
 
    SetCamera(pState);
1465
 
}
1466
 
 
1467
 
 
1468
 
/* Determine the angle at the vertex of a triangle given the length of the
1469
 
 * three sides. */
1470
 
 
1471
 
static double CosineRule(double a, double b, double c)
1472
 
{
1473
 
    double cosang = (a * a + b * b - c * c) / (2 * a * b);
1474
 
    /* If lengths don't form a proper triangle return something sensible.
1475
 
     * This typically happens with patterns where the juggler reaches too 
1476
 
     * far to get hold of an object. */
1477
 
    if (cosang < -1.0 || cosang > 1.0)
1478
 
        return 0;
1479
 
    else
1480
 
        return 180.0 * acos(cosang) / PI;
1481
 
}
1482
 
 
1483
 
 
1484
 
/* Spheres for the balls are generated by subdividing each triangle face into
1485
 
 * four smaller triangles.  We start with an octahedron (8 sides) and repeat the
1486
 
 * process a number of times.  The result is a mesh that can be split into four
1487
 
 * panels (like beanbags) and is smoother than the normal stacks and slices
1488
 
 * approach. */
1489
 
 
1490
 
static void InterpolateVertex(
1491
 
    const float* v1, const float* v2, float t, float* result)
1492
 
{
1493
 
    result[0] = v1[0] * (1.0f - t) + v2[0] * t;
1494
 
    result[1] = v1[1] * (1.0f - t) + v2[1] * t;
1495
 
    result[2] = v1[2] * (1.0f - t) + v2[2] * t;
1496
 
}
1497
 
 
1498
 
 
1499
 
static void SetGLVertex(const float* v, float rad)
1500
 
{
1501
 
    float Len = sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
1502
 
 
1503
 
    if (Len >= 1.0e-10f)
1504
 
    {
1505
 
        glNormal3f(v[0] / Len, v[1] / Len, v[2] / Len);
1506
 
        glVertex3f(rad * v[0] / Len, rad * v[1] / Len, rad * v[2] / Len);
1507
 
    }
1508
 
    else
1509
 
        glVertex3fv(v);
1510
 
}
1511
 
 
1512
 
 
1513
 
static void SphereSegment(
1514
 
    const float* v1, const float* v2, const float* v3, float r, int Levels)
1515
 
{
1516
 
    int i, j;
1517
 
 
1518
 
    for (i = 0; i < Levels; i++)
1519
 
    {
1520
 
        float A[3], B[3], C[3], D[3];
1521
 
        
1522
 
        InterpolateVertex(v3, v1, (float) i / Levels, D);
1523
 
        InterpolateVertex(v3, v1, (float)(i + 1) / Levels, A);
1524
 
        InterpolateVertex(v3, v2, (float)(i + 1) / Levels, B);
1525
 
        InterpolateVertex(v3, v2, (float) i / Levels, C);
1526
 
 
1527
 
        glBegin(GL_TRIANGLE_STRIP);
1528
 
 
1529
 
        SetGLVertex(B, r);
1530
 
        SetGLVertex(C, r);
1531
 
        
1532
 
        for (j = 1; j <= i; j++)
1533
 
        {
1534
 
            float v[3];
1535
 
 
1536
 
            InterpolateVertex(B, A, (float) j / (i + 1), v);
1537
 
            SetGLVertex(v, r);
1538
 
 
1539
 
            InterpolateVertex(C, D, (float) j / i, v);
1540
 
            SetGLVertex(v, r);
1541
 
        }
1542
 
 
1543
 
        SetGLVertex(A, r);
1544
 
        
1545
 
        glEnd();
1546
 
    }
1547
 
}
1548
 
 
1549
 
 
1550
 
/* OK, this function is a bit of misnomer, it only draws half a sphere.  Indeed
1551
 
 * it draws two panels and allows us to colour this one way,  then draw the
1552
 
 * same shape again rotated 90 degrees in a different colour.  Resulting in what
1553
 
 * looks like a four-panel beanbag in two complementary colours. */
1554
 
 
1555
 
static void DrawSphere(float rad)
1556
 
{
1557
 
    int Levels = 4;
1558
 
    float v1[3], v2[3], v3[3];
1559
 
    
1560
 
    v1[0] = 1.0f, v1[1] = 0.0f; v1[2] = 0.0f;
1561
 
    v2[0] = 0.0f, v2[1] = 1.0f; v2[2] = 0.0f;
1562
 
    v3[0] = 0.0f, v3[1] = 0.0f; v3[2] = 1.0f;
1563
 
    SphereSegment(v1, v2, v3, rad, Levels);
1564
 
    
1565
 
    v2[1] = -1.0f;
1566
 
    SphereSegment(v2, v1, v3, rad, Levels);
1567
 
    
1568
 
    v1[0] = v3[2] = -1.0f;
1569
 
    SphereSegment(v2, v1, v3, rad, Levels);
1570
 
 
1571
 
    v2[1] = 1.0f;
1572
 
    SphereSegment(v1, v2, v3, rad, Levels);
1573
 
}
1574
 
 
1575
 
 
1576
 
static void DrawRing(void)
1577
 
{
1578
 
    const int Facets = 22;
1579
 
    const float w = 0.1f;
1580
 
    GLUquadric* pQuad = gluNewQuadric();
1581
 
    glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
1582
 
    glTranslatef(0.0f, 0.0f, -w / 2.0f);
1583
 
 
1584
 
    gluCylinder(pQuad, 1.0f, 1.0f, w, Facets, 1);
1585
 
    gluQuadricOrientation(pQuad, GLU_INSIDE);
1586
 
 
1587
 
    gluCylinder(pQuad, 0.7f, 0.7f, w, Facets, 1);
1588
 
    gluQuadricOrientation(pQuad, GLU_OUTSIDE);
1589
 
 
1590
 
    glTranslatef(0.0f, 0.0f, w);
1591
 
    gluDisk(pQuad, 0.7, 1.0f, Facets, 1);
1592
 
 
1593
 
    glRotatef(180.0f, 0.0f, 1.0f, 0.0f);
1594
 
    glTranslatef(0.0f, 0.0f, w);
1595
 
    gluDisk(pQuad, 0.7, 1.0f, Facets, 1);
1596
 
 
1597
 
    gluDeleteQuadric(pQuad);
1598
 
}
1599
 
 
1600
 
 
1601
 
/* The club follows a 'circus club' design i.e. it has stripes running down the
1602
 
 * body.  The club is draw such that the one stripe uses the current material
1603
 
 * and the second stripe the standard silver colour. */
1604
 
 
1605
 
static void DrawClub(void)
1606
 
{
1607
 
    const float r[4] = {0.06f, 0.1f, 0.34f, 0.34f / 2.0f};
1608
 
    const float z[4] = {-0.4f, 0.6f, 1.35f, 2.1f};
1609
 
    float na[4];
1610
 
    const int n = 18;
1611
 
    int i, j;
1612
 
    GLUquadric* pQuad;
1613
 
 
1614
 
    na[0] = (float) atan((r[1] - r[0]) / (z[1] - z[0]));
1615
 
    na[1] = (float) atan((r[2] - r[1]) / (z[2] - z[1]));
1616
 
    na[2] = (float) atan((r[3] - r[1]) / (z[3] - z[1]));
1617
 
    na[3] = (float) atan((r[3] - r[2]) / (z[3] - z[2]));
1618
 
 
1619
 
    for (i = 0; i < n; i += 2)
1620
 
    {
1621
 
        float a1 = i * PI * 2.0f / n;
1622
 
        float a2 = (i + 1) * PI * 2.0f / n;
1623
 
 
1624
 
        glBegin(GL_TRIANGLE_STRIP);
1625
 
            for (j = 1; j < 4; j++)
1626
 
            {
1627
 
                glNormal3f(cosf(na[j]) * cosf(a1),
1628
 
                    cosf(na[j]) * sinf(a1), sinf(na[j]));
1629
 
 
1630
 
                glVertex3f(r[j] * cosf(a1), r[j] * sinf(a1), z[j]);
1631
 
 
1632
 
                glNormal3f(cosf(na[j]) * cosf(a2),
1633
 
                    cosf(na[j]) * sinf(a2),    sinf(na[j]));
1634
 
 
1635
 
                glVertex3f(r[j] * cosf(a2), r[j] * sinf(a2), z[j]);
1636
 
            }
1637
 
        glEnd();
1638
 
    }
1639
 
 
1640
 
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, HandleCol);
1641
 
 
1642
 
    for (i = 1; i < n; i += 2)
1643
 
    {
1644
 
        float a1 = i * PI * 2.0f / n;
1645
 
        float a2 = (i + 1) * PI * 2.0f / n;
1646
 
 
1647
 
        glBegin(GL_TRIANGLE_STRIP);
1648
 
            for (j = 1; j < 4; j++)
1649
 
            {
1650
 
                glNormal3f(cosf(na[j]) * cosf(a1),
1651
 
                    cosf(na[j]) * sinf(a1),    sinf(na[j]));
1652
 
 
1653
 
                glVertex3f(r[j] * cosf(a1), r[j] * sinf(a1), z[j]);
1654
 
 
1655
 
                glNormal3f(cosf(na[j]) * cosf(a2),
1656
 
                    cosf(na[j]) * sinf(a2), sinf(na[j]));
1657
 
 
1658
 
                glVertex3f(r[j] * cosf(a2), r[j] * sinf(a2), z[j]);
1659
 
            }
1660
 
        glEnd();
1661
 
    }
1662
 
 
1663
 
    pQuad = gluNewQuadric();
1664
 
    glTranslatef(0.0f, 0.0f, z[0]);
1665
 
    gluCylinder(pQuad, r[0], r[1], z[1] - z[0], n, 1);
1666
 
 
1667
 
    glTranslatef(0.0f, 0.0f, z[3] - z[0]);
1668
 
    gluDisk(pQuad, 0.0, r[3], n, 1);
1669
 
    glRotatef(180.0f, 0.0f, 1.0f, 0.0f);
1670
 
    glTranslatef(0.0f, 0.0f, z[3] - z[0]);
1671
 
    gluDisk(pQuad, 0.0, r[0], n, 1);
1672
 
    gluDeleteQuadric(pQuad);
1673
 
}
1674
 
 
1675
 
 
1676
 
/* In total 6 display lists are used.  There are created based on the DL_
1677
 
 * constants defined earlier.  The function returns the index of the first
1678
 
 * display list, all others can be calculated based on an offset from there. */
1679
 
 
1680
 
static int InitGLDisplayLists(void)
1681
 
{
1682
 
    int s = glGenLists(6);
1683
 
    GLUquadric* pQuad;
1684
 
 
1685
 
    glNewList(s + DL_BALL, GL_COMPILE);
1686
 
    DrawSphere(BallRad);
1687
 
    glEndList();
1688
 
 
1689
 
    glNewList(s + DL_CLUB, GL_COMPILE);
1690
 
    DrawClub();
1691
 
    glEndList();
1692
 
 
1693
 
    glNewList(s + DL_RING, GL_COMPILE);
1694
 
    DrawRing();
1695
 
    glEndList();
1696
 
    
1697
 
    pQuad =  gluNewQuadric();
1698
 
    gluQuadricNormals(pQuad, GLU_SMOOTH);    
1699
 
    
1700
 
    glNewList(s + DL_TORSO, GL_COMPILE);
1701
 
        glPushMatrix();
1702
 
            glTranslatef(ShoulderPos[0], ShoulderPos[1], -ShoulderPos[2]);
1703
 
            glRotatef(-90.0f, 0.0f, 1.0f, 0.0f);
1704
 
            gluCylinder(pQuad, 0.3, 0.3, ShoulderPos[0] * 2, 18, 1);
1705
 
        glPopMatrix();
1706
 
 
1707
 
        glPushMatrix();
1708
 
            glTranslatef(0.0f, -1.0f, -ShoulderPos[2]);
1709
 
            glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
1710
 
            gluCylinder(pQuad, 0.3, 0.3, ShoulderPos[1] + 1.0f, 18, 1);
1711
 
            glRotatef(180.0f, 1.0f, 0.0f, 0.0f);
1712
 
            gluDisk(pQuad, 0.0, 0.3, 18, 1);
1713
 
        glPopMatrix();
1714
 
        
1715
 
        /* draw the head */
1716
 
        glPushMatrix();
1717
 
            glTranslatef(0.0f, ShoulderPos[1] + 1.0f, -ShoulderPos[2]);
1718
 
            glRotatef(-30.0f, 1.0f, 0.0f, 0.0f);
1719
 
            gluCylinder(pQuad, 0.5, 0.5, 0.3, 15, 1);
1720
 
            
1721
 
            glPushMatrix();
1722
 
                glRotatef(180.0f, 1.0f, 0.0f, 0.0f);
1723
 
                glRotatef(180.0f, 0.0f, 0.0f, 1.0f);
1724
 
                gluDisk(pQuad, 0.0, 0.5, 15, 1);
1725
 
            glPopMatrix(); 
1726
 
                
1727
 
            glTranslatef(0.0f, 0.0f, .3f);
1728
 
            gluDisk(pQuad, 0.0, 0.5, 15, 1);
1729
 
        glPopMatrix();        
1730
 
    glEndList();
1731
 
    
1732
 
    glNewList(s + DL_UPPERARM, GL_COMPILE);
1733
 
        gluQuadricNormals(pQuad, GLU_SMOOTH);
1734
 
        gluQuadricDrawStyle(pQuad, GLU_FILL);
1735
 
        gluSphere(pQuad, 0.3, 12, 8);
1736
 
 
1737
 
        gluCylinder(pQuad, 0.3, 0.3, UArmLen, 12, 1); 
1738
 
        glTranslatef(0.0f, 0.0f, UArmLen);
1739
 
        gluSphere(pQuad, 0.3, 12, 8);
1740
 
    glEndList();
1741
 
 
1742
 
    glNewList(s + DL_FOREARM, GL_COMPILE);
1743
 
        gluCylinder(pQuad, 0.3, 0.3 / 2.0f, LArmLen, 12, 1);
1744
 
        glTranslatef(0.0f, 0.0f, LArmLen);
1745
 
        gluDisk(pQuad, 0, 0.3 / 2.0f, 18, 1);
1746
 
    glEndList();
1747
 
 
1748
 
    gluDeleteQuadric(pQuad);
1749
 
    return s;
1750
 
}
1751
 
 
1752
 
 
1753
 
/* Drawing the arm requires connecting the upper and fore arm between the
1754
 
 * shoulder and hand position.  Thinking about things kinematically by treating
1755
 
 * the shoulder and elbow as ball joints then, provided the arm can stretch far
1756
 
 * enough, there's a infnite number of ways to position the elbow.  Basically
1757
 
 * it's possible to fix and hand and shoulder and then rotate the elbow a full
1758
 
 * 360 degrees.  Clearly human anatomy isn't like this and picking a natural
1759
 
 * elbow position can be complex.  We chicken out and assume that poking the
1760
 
 * elbow out by 20 degrees from the lowest position gives a reasonably looking
1761
 
 * orientation. */
1762
 
 
1763
 
static void DrawArm(RENDER_STATE* pState, float TimePos, int Left)
1764
 
{
1765
 
    POS Pos;
1766
 
    float x, y, len, len2, ang, ang2;
1767
 
    
1768
 
    GetHandPosition(pState->pPattern, Left, TimePos, &Pos);
1769
 
 
1770
 
    x = Pos.x + (Left ? -ShoulderPos[0] : ShoulderPos[0]);
1771
 
    y = Pos.y - ShoulderPos[1];
1772
 
 
1773
 
 
1774
 
    len = sqrtf(x * x + y * y + ShoulderPos[2] * ShoulderPos[2]);
1775
 
    len2 = sqrtf(x * x + ShoulderPos[2] * ShoulderPos[2]);
1776
 
 
1777
 
    ang = (float) CosineRule(UArmLen, len, LArmLen);
1778
 
    ang2 = (float) CosineRule(UArmLen, LArmLen, len);
1779
 
 
1780
 
    if (ang == 0.0 && ang2 == 0)
1781
 
        ang2 = 180.0;
1782
 
 
1783
 
 
1784
 
    glPushMatrix();
1785
 
        glTranslatef(Left ? ShoulderPos[0] : -ShoulderPos[0], ShoulderPos[1],
1786
 
            -ShoulderPos[2]);
1787
 
        glRotatef((float)(180.0f * asin(x / len2) / 3.14f), 0.0f, 1.0f, 0.0f);
1788
 
        glRotatef((float)(-180.f * asin(y / len) / 3.14), 1.0f, 0.0f, 0.0f);
1789
 
        glRotatef(Left ? 20.0f : -20.0f, 0.0f, 0.0f, 1.0f);
1790
 
        glRotatef((float) ang, 1.0f, 0.0f, 0.0f);
1791
 
        glCallList(DL_UPPERARM + pState->DLStart);
1792
 
 
1793
 
        glRotatef((float)(ang2 - 180.0), 1.0f, 0.0f, 0.f);
1794
 
        glCallList(DL_FOREARM + pState->DLStart);
1795
 
    glPopMatrix();
1796
 
}
1797
 
 
1798
 
 
1799
 
static void DrawGLScene(RENDER_STATE* pState)
1800
 
{
1801
 
    float Time = pState->Time;
1802
 
    int nCols = sizeof(Cols) / sizeof(Cols[0]);
1803
 
    int i;
1804
 
 
1805
 
    PATTERN_INFO* pPattern = pState->pPattern;
1806
 
 
1807
 
    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
1808
 
 
1809
 
    glMatrixMode(GL_MODELVIEW);
1810
 
    glLoadIdentity();
1811
 
    glTranslatef(5.0f * sinf(pState->TranslateAngle), 0.0f, 0.0f);
1812
 
 
1813
 
    gltrackball_rotate (pState->trackball);
1814
 
 
1815
 
    glRotatef(pState->SpinAngle, 0.0f, 1.0f, 0.0f);
1816
 
    glTranslatef(0.0, 0.0, -1.0f);
1817
 
 
1818
 
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, DiffCol);
1819
 
    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, SpecCol);
1820
 
    glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 60.0f);
1821
 
 
1822
 
    for (i = 0; i < pPattern->Objects; i++)
1823
 
    {
1824
 
        POS ObjPos;
1825
 
        
1826
 
        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, Cols[i % nCols]);
1827
 
        glPushMatrix();
1828
 
 
1829
 
        switch (pPattern->pObjectInfo[i].ObjectType)
1830
 
        {
1831
 
            case OBJECT_CLUB:
1832
 
                GetObjectPosition(pPattern, i, Time, 1.0f, &ObjPos);
1833
 
                glTranslatef(ObjPos.x, ObjPos.y, ObjPos.z);
1834
 
                glRotatef(ObjPos.Rot, 0.0f, 1.0f, 0.0f);
1835
 
                glRotatef(ObjPos.Elev, -1.0f, 0.0f, 0.0f);
1836
 
                glTranslatef(0.0f, 0.0f, -1.0f);
1837
 
                glCallList(DL_CLUB + pState->DLStart);
1838
 
                break;
1839
 
 
1840
 
            case OBJECT_RING:
1841
 
                GetObjectPosition(pPattern, i, Time, 1.0f, &ObjPos);
1842
 
                glTranslatef(ObjPos.x, ObjPos.y, ObjPos.z);
1843
 
                glRotatef(ObjPos.Rot, 0.0f, 1.0f, 0.0f);
1844
 
                glRotatef(ObjPos.Elev, -1.0f, 0.0f, 0.0f);
1845
 
                glCallList(DL_RING + pState->DLStart);
1846
 
                break;
1847
 
 
1848
 
            default:
1849
 
                GetObjectPosition(pPattern, i, Time, 0.0f, &ObjPos);
1850
 
                glTranslatef(ObjPos.x, ObjPos.y, ObjPos.z);        
1851
 
                glRotatef(ObjPos.Rot, 0.6963f, 0.6963f, 0.1742f);
1852
 
                glCallList(DL_BALL + pState->DLStart);
1853
 
                glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
1854
 
                glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, 
1855
 
                    AltCols[i % nCols]);
1856
 
                glCallList(DL_BALL + pState->DLStart);
1857
 
                break;
1858
 
        }
1859
 
 
1860
 
        glPopMatrix();
1861
 
    }
1862
 
 
1863
 
    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, BodyCol);
1864
 
    glCallList(DL_TORSO + pState->DLStart);
1865
 
    DrawArm(pState, Time, 1);
1866
 
    DrawArm(pState, Time, 0);
1867
 
}
1868
 
 
1869
 
 
1870
 
static int RandInRange(int Min, int Max)
1871
 
{
1872
 
    return Min + random() % (1 + Max - Min);
1873
 
}
1874
 
 
1875
 
 
1876
 
static void UpdatePattern(
1877
 
    RENDER_STATE* pState, int MinBalls, int MaxBalls, 
1878
 
    int MinHeightInc, int MaxHeightInc)
1879
 
{
1880
 
    if (pState->pPattern != NULL)
1881
 
        ReleasePatternInfo(pState->pPattern);
1882
 
    
1883
 
    pState->pPattern = (PATTERN_INFO*) malloc(sizeof(PATTERN_INFO));
1884
 
    
1885
 
    if ((random() % 3) == 1)
1886
 
    {    
1887
 
        int ExtSiteLen;
1888
 
        int n = random() % (sizeof(PatternText) / sizeof(PatternText[0]));
1889
 
        EXT_SITE_INFO* pExtInfo = ParsePattern(PatternText[n], &ExtSiteLen);
1890
 
        InitPatternInfo(pState->pPattern, NULL, pExtInfo, ExtSiteLen);
1891
 
        free(pExtInfo);
1892
 
    }
1893
 
    else
1894
 
    {
1895
 
        int* pRand;
1896
 
        int ballcount, maxweight;
1897
 
        const int RandPatternLen = 1500;
1898
 
        
1899
 
        ballcount = RandInRange(MinBalls, MaxBalls);
1900
 
        maxweight = ballcount  + RandInRange(MinHeightInc, MaxHeightInc);
1901
 
        
1902
 
        pRand = Generate(RandPatternLen, maxweight, ballcount);
1903
 
        InitPatternInfo(pState->pPattern, pRand, NULL, RandPatternLen);
1904
 
        free(pRand);
1905
 
    }
1906
 
    
1907
 
    pState->CameraElev = 50.0f - random() % 90;
1908
 
    pState->TranslateAngle = random() % 360;
1909
 
    pState->SpinAngle = random() % 360;
1910
 
    pState->Time = 50.0f;
1911
 
    SetCamera(pState);
1912
 
}
1913
 
 
1914
 
 
1915
 
/*******************************************************************************
1916
 
 *
1917
 
 *  XScreenSaver Configuration
1918
 
 *
1919
 
 ******************************************************************************/
1920
 
 
1921
 
typedef struct
1922
 
{
1923
 
    GLXContext* glxContext;
1924
 
    RENDER_STATE RenderState;
1925
 
    float CurrentFrameRate;
1926
 
    unsigned FramesSinceSync;
1927
 
    unsigned LastSyncTime;
1928
 
} JUGGLER3D_CONFIG;
1929
 
 
1930
 
 
1931
 
#define DEF_MAX_OBJS            "8"
1932
 
#define DEF_MIN_OBJS            "3"
1933
 
#define DEF_MAX_HINC            "6"
1934
 
#define DEF_MIN_HINC            "2"
1935
 
#define DEF_JUGGLE_SPEED        "2.2"
1936
 
#define DEF_TRANSLATE_SPEED     "0.1"
1937
 
#define DEF_SPIN_SPEED          "20.0"
1938
 
 
1939
 
static JUGGLER3D_CONFIG* pConfigInfo = NULL;
1940
 
static int MaxObjects;
1941
 
static int MinObjects;
1942
 
static int MaxHeightInc;
1943
 
static int MinHeightInc;
1944
 
static float SpinSpeed;
1945
 
static float TranslateSpeed;
1946
 
static float JuggleSpeed;
1947
 
 
1948
 
static XrmOptionDescRec opts[] =
1949
 
{
1950
 
    {"-spin", ".spinSpeed", XrmoptionSepArg, 0},
1951
 
    {"-trans", ".translateSpeed", XrmoptionSepArg, 0},
1952
 
    {"-speed", ".juggleSpeed", XrmoptionSepArg, 0},
1953
 
    {"-maxobjs", ".maxObjs", XrmoptionSepArg, 0},
1954
 
    {"-minobjs", ".minObjs", XrmoptionSepArg, 0},
1955
 
    {"-maxhinc", ".maxHinc", XrmoptionSepArg, 0},
1956
 
    {"-minhinc", ".minHinc", XrmoptionSepArg, 0},
1957
 
};
1958
 
 
1959
 
 
1960
 
static argtype vars[] = 
1961
 
{
1962
 
    {&MaxObjects, "maxObjs", "MaxObjs", DEF_MAX_OBJS, t_Int},
1963
 
    {&MinObjects, "minObjs", "MinObjs", DEF_MIN_OBJS, t_Int},
1964
 
    {&MaxHeightInc, "maxHinc", "MaxHinc", DEF_MAX_HINC, t_Int},
1965
 
    {&MinHeightInc, "minHinc", "MinHinc", DEF_MIN_HINC, t_Int},
1966
 
    {&JuggleSpeed, "juggleSpeed", "JuggleSpeed", DEF_JUGGLE_SPEED, t_Float},
1967
 
    {&TranslateSpeed, "translateSpeed", "TranslateSpeed", DEF_TRANSLATE_SPEED, t_Float},
1968
 
    {&SpinSpeed, "spinSpeed", "SpinSpeed", DEF_SPIN_SPEED, t_Float},
1969
 
};
1970
 
 
1971
 
 
1972
 
ENTRYPOINT ModeSpecOpt juggler3d_opts = {countof(opts), opts, countof(vars), vars};
1973
 
 
1974
 
 
1975
 
ENTRYPOINT void reshape_juggler3d(ModeInfo *mi, int width, int height)
1976
 
{
1977
 
    JUGGLER3D_CONFIG* pConfig = &pConfigInfo[MI_SCREEN(mi)];
1978
 
    ResizeGL(&pConfig->RenderState, width, height);
1979
 
}
1980
 
 
1981
 
 
1982
 
ENTRYPOINT void init_juggler3d(ModeInfo* mi)
1983
 
{
1984
 
    JUGGLER3D_CONFIG* pConfig;
1985
 
    
1986
 
    if (pConfigInfo == NULL)
1987
 
    {
1988
 
        /* Apply suitable bounds checks to the input parameters */
1989
 
        MaxObjects = max(3, min(MaxObjects, 36));
1990
 
        MinObjects = max(3, min(MinObjects, MaxObjects));
1991
 
 
1992
 
        MaxHeightInc = max(1, min(MaxHeightInc, 32));
1993
 
        MinHeightInc = max(1, min(MinHeightInc, MaxHeightInc));
1994
 
            
1995
 
        pConfigInfo = (JUGGLER3D_CONFIG*) calloc(
1996
 
            MI_NUM_SCREENS(mi), sizeof(JUGGLER3D_CONFIG));
1997
 
        if (pConfigInfo == NULL)
1998
 
        {
1999
 
            fprintf(stderr, "%s: out of memory\n", progname);
2000
 
            exit(1);
2001
 
        }
2002
 
    }
2003
 
    
2004
 
    pConfig = &pConfigInfo[MI_SCREEN(mi)];
2005
 
    pConfig->glxContext = init_GL(mi);
2006
 
    pConfig->CurrentFrameRate = 0.0f;
2007
 
    pConfig->FramesSinceSync = 0;
2008
 
    pConfig->LastSyncTime = 0;
2009
 
    InitGLSettings(&pConfig->RenderState, MI_IS_WIREFRAME(mi));
2010
 
 
2011
 
    UpdatePattern(&pConfig->RenderState, MinObjects, MaxObjects, 
2012
 
        MinHeightInc, MaxHeightInc);
2013
 
    
2014
 
    reshape_juggler3d(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
2015
 
}
2016
 
 
2017
 
 
2018
 
ENTRYPOINT void draw_juggler3d(ModeInfo* mi)
2019
 
{
2020
 
    JUGGLER3D_CONFIG* pConfig = &pConfigInfo[MI_SCREEN(mi)];
2021
 
    Display* pDisplay = MI_DISPLAY(mi);
2022
 
    Window hwnd = MI_WINDOW(mi);
2023
 
 
2024
 
    if (pConfig->glxContext == NULL)
2025
 
        return;
2026
 
 
2027
 
    glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(pConfig->glxContext));
2028
 
    
2029
 
    /* While drawing, keep track of the rendering speed so we can adjust the
2030
 
     * animation speed so things appear consistent.  The basis of the this
2031
 
     * code comes from the frame rate counter (fps.c) but has been modified
2032
 
     * so that it reports the initial frame rate earlier (after 0.02 secs
2033
 
     * instead of 1 sec). */
2034
 
    
2035
 
    if (pConfig->FramesSinceSync >=  1 * (int) pConfig->CurrentFrameRate)
2036
 
    {
2037
 
        struct timeval tvnow;
2038
 
        unsigned now;
2039
 
            
2040
 
        # ifdef GETTIMEOFDAY_TWO_ARGS
2041
 
            struct timezone tzp;
2042
 
            gettimeofday(&tvnow, &tzp);
2043
 
        # else
2044
 
            gettimeofday(&tvnow);
2045
 
        # endif
2046
 
        
2047
 
        now = (unsigned) (tvnow.tv_sec * 1000000 + tvnow.tv_usec);
2048
 
        if (pConfig->FramesSinceSync == 0)
2049
 
        {
2050
 
            pConfig->LastSyncTime = now;
2051
 
        }
2052
 
        else
2053
 
        {
2054
 
            unsigned Delta = now - pConfig->LastSyncTime;
2055
 
            if (Delta > 20000)
2056
 
            {
2057
 
                pConfig->LastSyncTime = now;
2058
 
                pConfig->CurrentFrameRate = 
2059
 
                    (pConfig->FramesSinceSync * 1.0e6f) / Delta;
2060
 
                pConfig->FramesSinceSync = 0;
2061
 
            }
2062
 
        }
2063
 
    }
2064
 
    
2065
 
    pConfig->FramesSinceSync++;
2066
 
    
2067
 
    if (pConfig->RenderState.Time > 150.0f)
2068
 
    {
2069
 
        UpdatePattern(&pConfig->RenderState, MinObjects, MaxObjects, 
2070
 
            MinHeightInc, MaxHeightInc);
2071
 
    }
2072
 
    DrawGLScene(&pConfig->RenderState);
2073
 
    
2074
 
    if (pConfig->CurrentFrameRate > 1.0e-6f)
2075
 
    {
2076
 
        pConfig->RenderState.Time += JuggleSpeed / pConfig->CurrentFrameRate;
2077
 
        pConfig->RenderState.SpinAngle += SpinSpeed / pConfig->CurrentFrameRate;
2078
 
        pConfig->RenderState.TranslateAngle += 
2079
 
            TranslateSpeed / pConfig->CurrentFrameRate;
2080
 
    }
2081
 
    
2082
 
    if (mi->fps_p)
2083
 
        do_fps(mi);
2084
 
  
2085
 
    glFinish();
2086
 
    glXSwapBuffers(pDisplay, hwnd);
2087
 
}
2088
 
 
2089
 
 
2090
 
ENTRYPOINT Bool juggler3d_handle_event(ModeInfo* mi, XEvent* pEvent)
2091
 
{
2092
 
  JUGGLER3D_CONFIG* pConfig = &pConfigInfo[MI_SCREEN(mi)];
2093
 
  RENDER_STATE* pState = &pConfig->RenderState;
2094
 
 
2095
 
    if (pEvent->xany.type == ButtonPress &&
2096
 
        pEvent->xbutton.button == Button1)
2097
 
    {
2098
 
      pState->button_down_p = True;
2099
 
      gltrackball_start (pState->trackball,
2100
 
                         pEvent->xbutton.x, pEvent->xbutton.y,
2101
 
                         MI_WIDTH (mi), MI_HEIGHT (mi));
2102
 
      return True;
2103
 
    }
2104
 
    else if (pEvent->xany.type == ButtonRelease &&
2105
 
             pEvent->xbutton.button == Button1)
2106
 
    {
2107
 
      pState->button_down_p = False;
2108
 
      return True;
2109
 
    }
2110
 
    else if (pEvent->xany.type == ButtonPress &&
2111
 
             (pEvent->xbutton.button == Button4 ||
2112
 
              pEvent->xbutton.button == Button5 ||
2113
 
              pEvent->xbutton.button == Button6 ||
2114
 
              pEvent->xbutton.button == Button7))
2115
 
    {
2116
 
      gltrackball_mousewheel (pState->trackball, pEvent->xbutton.button, 2,
2117
 
                              !pEvent->xbutton.state);
2118
 
      return True;
2119
 
    }
2120
 
    else if (pEvent->xany.type == MotionNotify &&
2121
 
             pState->button_down_p)
2122
 
    {
2123
 
      gltrackball_track (pState->trackball,
2124
 
                         pEvent->xmotion.x, pEvent->xmotion.y,
2125
 
                         MI_WIDTH (mi), MI_HEIGHT (mi));
2126
 
      return True;
2127
 
    }
2128
 
    else if (pEvent->xany.type == KeyPress)
2129
 
    {
2130
 
        char str[20];
2131
 
        KeySym Key = 0;
2132
 
        int count = XLookupString(&pEvent->xkey, str, 20, &Key, 0);
2133
 
        str[count] = '\0';
2134
 
        if (*str == ' ')
2135
 
        {
2136
 
            UpdatePattern(&pConfig->RenderState, MinObjects, MaxObjects, 
2137
 
                MinHeightInc, MaxHeightInc);
2138
 
        }
2139
 
    }
2140
 
    
2141
 
    return False;
2142
 
}
2143
 
 
2144
 
XSCREENSAVER_MODULE ("Juggler3D", juggler3d)
2145
 
 
2146
 
#endif
 
152
#define DEF_PATTERN "random" /* All patterns */
 
153
#define DEF_TAIL "1" /* No trace */
 
154
#ifdef UNI
 
155
/* Maybe a ROLA BOLA would be at a better angle for viewing */
 
156
#define DEF_UNI "False" /* No unicycle */ /* Not implemented yet */
 
157
#endif
 
158
#define DEF_REAL "True"
 
159
#define DEF_DESCRIBE "True"
 
160
 
 
161
#define DEF_BALLS "True" /* Use Balls */
 
162
#define DEF_CLUBS "True" /* Use Clubs */
 
163
#define DEF_TORCHES "True" /* Use Torches */
 
164
#define DEF_KNIVES "True" /* Use Knives */
 
165
#define DEF_RINGS "True" /* Use Rings */
 
166
#define DEF_BBALLS "True" /* Use Bowling Balls */
 
167
 
 
168
static char *pattern;
 
169
static int tail;
 
170
#ifdef UNI
 
171
static Bool uni;
 
172
#endif
 
173
static Bool real;
 
174
static Bool describe;
 
175
static Bool balls;
 
176
static Bool clubs;
 
177
static Bool torches;
 
178
static Bool knives;
 
179
static Bool rings;
 
180
static Bool bballs;
 
181
static char *only;
 
182
 
 
183
static XrmOptionDescRec opts[] = {
 
184
  {"-pattern",  ".juggle.pattern",  XrmoptionSepArg, NULL  },
 
185
  {"-tail",     ".juggle.tail",     XrmoptionSepArg, NULL  },
 
186
#ifdef UNI
 
187
  {"-uni",      ".juggle.uni",      XrmoptionNoArg,  "on"  },
 
188
  {"+uni",      ".juggle.uni",      XrmoptionNoArg,  "off" },
 
189
#endif
 
190
  {"-real",     ".juggle.real",     XrmoptionNoArg,  "on"  },
 
191
  {"+real",     ".juggle.real",     XrmoptionNoArg,  "off" },
 
192
  {"-describe", ".juggle.describe", XrmoptionNoArg,  "on"  },
 
193
  {"+describe", ".juggle.describe", XrmoptionNoArg,  "off" },
 
194
  {"-balls",    ".juggle.balls",    XrmoptionNoArg,  "on"  },
 
195
  {"+balls",    ".juggle.balls",    XrmoptionNoArg,  "off" },
 
196
  {"-clubs",    ".juggle.clubs",    XrmoptionNoArg,  "on"  },
 
197
  {"+clubs",    ".juggle.clubs",    XrmoptionNoArg,  "off" },
 
198
  {"-torches",  ".juggle.torches",  XrmoptionNoArg,  "on"  },
 
199
  {"+torches",  ".juggle.torches",  XrmoptionNoArg,  "off" },
 
200
  {"-knives",   ".juggle.knives",   XrmoptionNoArg,  "on"  },
 
201
  {"+knives",   ".juggle.knives",   XrmoptionNoArg,  "off" },
 
202
  {"-rings",    ".juggle.rings",    XrmoptionNoArg,  "on"  },
 
203
  {"+rings",    ".juggle.rings",    XrmoptionNoArg,  "off" },
 
204
  {"-bballs",   ".juggle.bballs",   XrmoptionNoArg,  "on"  },
 
205
  {"+bballs",   ".juggle.bballs",   XrmoptionNoArg,  "off" },
 
206
  {"-only",     ".juggle.only",     XrmoptionSepArg, NULL  },
 
207
};
 
208
 
 
209
static argtype vars[] = {
 
210
  { &pattern,  "pattern",  "Pattern",  DEF_PATTERN,  t_String },
 
211
  { &tail,     "tail",     "Tail",     DEF_TAIL,     t_Int    },
 
212
#ifdef UNI
 
213
  { &uni,      "uni",      "Uni",      DEF_UNI,      t_Bool   },
 
214
#endif
 
215
  { &real,     "real",     "Real",     DEF_REAL,     t_Bool   },
 
216
  { &describe, "describe", "Describe", DEF_DESCRIBE, t_Bool   },
 
217
  { &balls,    "balls",    "Clubs",    DEF_BALLS,    t_Bool   },
 
218
  { &clubs,    "clubs",    "Clubs",    DEF_CLUBS,    t_Bool   },
 
219
  { &torches,  "torches",  "Torches",  DEF_TORCHES,  t_Bool   },
 
220
  { &knives,   "knives",   "Knives",   DEF_KNIVES,   t_Bool   },
 
221
  { &rings,    "rings",    "Rings",    DEF_RINGS,    t_Bool   },
 
222
  { &bballs,   "bballs",   "BBalls",   DEF_BBALLS,   t_Bool   },
 
223
  { &only,     "only",     "BBalls",   " ",          t_String },
 
224
};
 
225
 
 
226
static OptionStruct desc[] =
 
227
{
 
228
  { "-pattern string", "Cambridge Juggling Pattern" },
 
229
  { "-tail num",       "Trace Juggling Patterns" },
 
230
#ifdef UNI
 
231
  { "-/+uni",          "Unicycle" },
 
232
#endif
 
233
  { "-/+real",         "Real-time" },
 
234
  { "-/+describe",     "turn on/off pattern descriptions." },
 
235
  { "-/+balls",        "turn on/off Balls." },
 
236
  { "-/+clubs",        "turn on/off Clubs." },
 
237
  { "-/+torches",      "turn on/off Flaming Torches." },
 
238
  { "-/+knives",       "turn on/off Knives." },
 
239
  { "-/+rings",        "turn on/off Rings." },
 
240
  { "-/+bballs",       "turn on/off Bowling Balls." },
 
241
  { "-only",           "Turn off all objects but the named one." },
 
242
};
 
243
 
 
244
ENTRYPOINT ModeSpecOpt juggle_opts =
 
245
 {countof(opts), opts, countof(vars), vars, desc};
 
246
 
 
247
 
 
248
/* Note: All "lengths" are scaled by sp->scale = MI_HEIGHT/480.  All
 
249
   "thicknesses" are scaled by sqrt(sp->scale) so that they are
 
250
   proportionally thicker for smaller windows.  Objects spinning out
 
251
   of the plane (such as clubs) fake perspective by compressing their
 
252
   horizontal coordinates by PERSPEC  */
 
253
 
 
254
/* Figure */
 
255
#define ARMLENGTH 50
 
256
#define ARMWIDTH ((int) (8.0 * sqrt(sp->scale)))
 
257
#define POSE 10
 
258
#define BALLRADIUS ARMWIDTH
 
259
 
 
260
/* build all the models assuming a 480px high scene */
 
261
#define SCENE_HEIGHT 480
 
262
#define SCENE_WIDTH ((int)(SCENE_HEIGHT*(MI_WIDTH(mi)/(float)MI_HEIGHT(mi))))
 
263
 
 
264
/*#define PERSPEC  0.4*/
 
265
 
 
266
/* macros */
 
267
#define GRAVITY(h, t) 4*(double)(h)/((t)*(t))
 
268
 
 
269
/* Timing based on count.  Units are milliseconds.  Juggles per second
 
270
       is: 2000 / THROW_CATCH_INTERVAL + CATCH_THROW_INTERVAL */
 
271
 
 
272
#define THROW_CATCH_INTERVAL (sp->count)
 
273
#define THROW_NULL_INTERVAL  (sp->count * 0.5)
 
274
#define CATCH_THROW_INTERVAL (sp->count * 0.2)
 
275
 
 
276
/********************************************************************
 
277
 * Trace Definitions                                                *
 
278
 *                                                                  *
 
279
 * These record rendering data so that a drawn object can be erased *
 
280
 * later.  Each object has its own Trace list.                      *
 
281
 *                                                                  *
 
282
 ********************************************************************/
 
283
 
 
284
typedef struct {double x, y; } DXPoint;
 
285
typedef struct trace *TracePtr;
 
286
typedef struct trace {
 
287
  TracePtr next, prev;
 
288
  double x, y;
 
289
  double angle;
 
290
  int divisions;
 
291
  DXPoint dlast;
 
292
#ifdef MEMTEST
 
293
  char pad[1024];
 
294
#endif
 
295
} Trace;
 
296
 
 
297
/*******************************************************************
 
298
 * Object Definitions                                              *
 
299
 *                                                                 *
 
300
 * These describe the various types of Object that can be juggled  *
 
301
 *                                                                 *
 
302
 *******************************************************************/
 
303
typedef int (DrawProc)(ModeInfo*, unsigned long, Trace *);
 
304
 
 
305
static DrawProc show_ball, show_europeanclub, show_torch, show_knife;
 
306
static DrawProc show_ring, show_bball;
 
307
 
 
308
typedef enum {BALL, CLUB, TORCH, KNIFE, RING, BBALLS,
 
309
              NUM_OBJECT_TYPES} ObjType;
 
310
 
 
311
#define OBJMIXPROB 20   /* inverse of the chances of using an odd
 
312
                                                   object in the pattern */
 
313
 
 
314
static const GLfloat body_color_1[4] = { 0.9, 0.7, 0.5, 1 };
 
315
static const GLfloat body_color_2[4] = { 0.6, 0.4, 0.2, 1 };
 
316
 
 
317
static const struct {
 
318
  DrawProc *draw;                           /* Object Rendering function */
 
319
  int       handle;                         /* Length of object's handle */
 
320
  int       mintrail;                            /* Minimum trail length */
 
321
  double    cor;      /* Coefficient of Restitution.  perfect bounce = 1 */
 
322
  double    weight;          /* Heavier objects don't get thrown as high */
 
323
} ObjectDefs[] = {
 
324
  { /* Ball */
 
325
        show_ball,
 
326
        0,
 
327
        1,
 
328
        0.9,
 
329
        1.0,
 
330
  },
 
331
  { /* Club */
 
332
        show_europeanclub,
 
333
        15,
 
334
        1,
 
335
        0.55, /* Clubs don't bounce too well */
 
336
        1.0,
 
337
  },
 
338
  { /* Torch */
 
339
        show_torch,
 
340
        15,
 
341
        20, /* Torches need flames */
 
342
        0, /* Torches don't bounce -- fire risk! */
 
343
        1.0,
 
344
  },
 
345
  { /* Knife */
 
346
        show_knife,
 
347
        15,
 
348
        1,
 
349
        0, /* Knives don't bounce */
 
350
        1.0,
 
351
  },
 
352
  { /* Ring */
 
353
        show_ring,
 
354
        15,
 
355
        1,
 
356
        0.8,
 
357
        1.0,
 
358
  },
 
359
  { /* Bowling Ball */
 
360
        show_bball,
 
361
        0,
 
362
        1,
 
363
        0.2,
 
364
        5.0,
 
365
  },
 
366
};
 
367
 
 
368
/**************************
 
369
 * Trajectory definitions *
 
370
 **************************/
 
371
 
 
372
typedef enum {HEIGHT, ADAM} Notation;
 
373
typedef enum {Empty, Full, Ball} Throwable;
 
374
typedef enum {LEFT, RIGHT} Hand;
 
375
typedef enum {THROW, CATCH} Action;
 
376
typedef enum {HAND, ELBOW, SHOULDER} Joint;
 
377
typedef enum {ATCH, THRATCH, ACTION, LINKEDACTION,
 
378
                          PTHRATCH, BPREDICTOR, PREDICTOR} TrajectoryStatus;
 
379
typedef struct {double a, b, c, d; } Spline;
 
380
typedef DXPoint Arm[3];
 
381
 
 
382
 
 
383
/* Object is an arbitrary object being juggled.  Each Trajectory
 
384
 * references an Object ("count" tracks this), and each Object is also
 
385
 * linked into a global Objects list.  Objects may include a Trace
 
386
 * list for tracking erasures. */
 
387
typedef struct object *ObjectPtr;
 
388
typedef struct object {
 
389
  ObjectPtr next, prev;
 
390
 
 
391
  ObjType type;
 
392
  int     color;
 
393
  int     count; /* reference count */
 
394
  Bool    active; /* Object is in use */
 
395
 
 
396
  Trace  *trace;
 
397
  int     tracelen;
 
398
  int     tail;
 
399
#ifdef MEMTEST
 
400
  char pad[1024];
 
401
#endif
 
402
} Object;
 
403
 
 
404
/* Trajectory is a segment of juggling action.  A list of Trajectories
 
405
 * defines the juggling performance.  The Trajectory list goes through
 
406
 * multiple processing steps to convert it from basic juggling
 
407
 * notation into rendering data. */
 
408
 
 
409
typedef struct trajectory *TrajectoryPtr;
 
410
typedef struct trajectory {
 
411
  TrajectoryPtr prev, next;  /* for building list */
 
412
  TrajectoryStatus status;
 
413
 
 
414
  /* Throw */
 
415
  char posn;
 
416
  int height;
 
417
  int adam;
 
418
  char *pattern;
 
419
  char *name;
 
420
 
 
421
  /* Action */
 
422
  Hand hand;
 
423
  Action action;
 
424
 
 
425
  /* LinkedAction */
 
426
  int color;
 
427
  Object *object;
 
428
  int divisions;
 
429
  double angle, spin;
 
430
  TrajectoryPtr balllink;
 
431
  TrajectoryPtr handlink;
 
432
 
 
433
  /* PThratch */
 
434
  double cx; /* Moving juggler */
 
435
  double x, y; /* current position */
 
436
  double dx, dy; /* initial velocity */
 
437
 
 
438
  /* Predictor */
 
439
  Throwable type;
 
440
  unsigned long start, finish;
 
441
  Spline xp, yp;
 
442
 
 
443
#ifdef MEMTEST
 
444
  char pad[1024];
 
445
#endif
 
446
} Trajectory;
 
447
 
 
448
 
 
449
/*******************
 
450
 * Pattern Library *
 
451
 *******************/
 
452
 
 
453
typedef struct {
 
454
  const char * pattern;
 
455
  const char * name;
 
456
} patternstruct;
 
457
 
 
458
/* List of popular patterns, in any order */
 
459
/* Patterns should be given in Adam notation so the generator can
 
460
   concatenate them safely.  Null descriptions are ok.  Height
 
461
   notation will be displayed automatically.  */
 
462
/* Can't const this because it is qsorted.  This *should* be reentrant,
 
463
   I think... */
 
464
static /*const*/ patternstruct portfolio[] = {
 
465
  {"[+2 1]", /* +3 1 */ "Typical 2 ball juggler"},
 
466
  {"[2 0]", /* 4 0 */ "2 in 1 hand"},
 
467
  {"[2 0 1]", /* 5 0 1 */},
 
468
  {"[+2 0 +2 0 0]" /* +5 0 +5 0 0 */},
 
469
  {"[+2 0 1 2 2]", /* +4 0 1 2 3 */},
 
470
  {"[2 0 1 1]", /* 6 0 1 1 */},
 
471
 
 
472
  {"[3]", /* 3 */ "3 cascade"},
 
473
  {"[+3]", /* +3 */ "reverse 3 cascade"},
 
474
  {"[=3]", /* =3 */ "cascade 3 under arm"},
 
475
  {"[&3]", /* &3 */ "cascade 3 catching under arm"},
 
476
  {"[_3]", /* _3 */ "bouncing 3 cascade"},
 
477
  {"[+3 x3 =3]", /* +3 x3 =3 */ "Mill's mess"},
 
478
  {"[3 2 1]", /* 5 3 1" */},
 
479
  {"[3 3 1]", /* 4 4 1" */},
 
480
  {"[3 1 2]", /* 6 1 2 */ "See-saw"},
 
481
  {"[=3 3 1 2]", /* =4 5 1 2 */},
 
482
  {"[=3 2 2 3 1 2]", /* =6 2 2 5 1 2 */ "=4 5 1 2 stretched"},
 
483
  {"[+3 3 1 3]", /* +4 4 1 3 */ "anemic shower box"},
 
484
  {"[3 3 1]", /* 4 4 1 */},
 
485
  {"[+3 2 3]", /* +4 2 3 */},
 
486
  {"[+3 1]", /* +5 1 */ "3 shower"},
 
487
  {"[_3 1]", /* _5 1 */ "bouncing 3 shower"},
 
488
  {"[3 0 3 0 3]", /* 5 0 5 0 5 */ "shake 3 out of 5"},
 
489
  {"[3 3 3 0 0]", /* 5 5 5 0 0 */ "flash 3 out of 5"},
 
490
  {"[3 3 0]", /* 4 5 0 */ "complete waste of a 5 ball juggler"},
 
491
  {"[3 3 3 0 0 0 0]", /* 7 7 7 0 0 0 0 */ "3 flash"},
 
492
  {"[+3 0 +3 0 +3 0 0]", /* +7 0 +7 0 +7 0 0 */},
 
493
  {"[3 2 2 0 3 2 0 2 3 0 2 2 0]", /* 7 3 3 0 7 3 0 3 7 0 3 3 0 */},
 
494
  {"[3 0 2 0]", /* 8 0 4 0 */},
 
495
  {"[_3 2 1]", /* _5 3 1 */},
 
496
  {"[_3 0 1]", /* _8 0 1 */},
 
497
  {"[1 _3 1 _3 0 1 _3 0]", /* 1 _7 1 _7 0 1 _7 0 */},
 
498
  {"[_3 2 1 _3 1 2 1]", /* _6 3 1 _6 1 3 1 */},
 
499
 
 
500
  {"[4]", /* 4 */ "4 cascade"},
 
501
  {"[+4 3]", /* +5 3 */ "4 ball half shower"},
 
502
  {"[4 4 2]", /* 5 5 2 */},
 
503
  {"[+4 4 4 +4]", /* +4 4 4 +4 */ "4 columns"},
 
504
  {"[+4 3 +4]", /* +5 3 +4 */},
 
505
  {"[4 3 4 4]", /* 5 3 4 4 */},
 
506
  {"[4 3 3 4]", /* 6 3 3 4 */},
 
507
  {"[4 3 2 4", /* 6 4 2 4 */},
 
508
  {"[+4 1]", /* +7 1 */ "4 shower"},
 
509
  {"[4 4 4 4 0]", /* 5 5 5 5 0 */ "learning 5"},
 
510
  {"[+4 x4 =4]", /* +4 x4 =4 */ "Mill's mess for 4"},
 
511
  {"[+4 2 1 3]", /* +9 3 1 3 */},
 
512
  {"[4 4 1 4 1 4]", /* 6 6 1 5 1 5, by Allen Knutson */},
 
513
  {"[_4 _4 _4 1 _4 1]", /* _5 _6 _6 1 _5 1 */},
 
514
  {"[_4 3 3]", /* _6 3 3 */},
 
515
  {"[_4 3 1]", /* _7 4 1 */},
 
516
  {"[_4 2 1]", /* _8 3 1 */},
 
517
  {"[_4 3 3 3 0]", /* _8 4 4 4 0 */},
 
518
  {"[_4 1 3 1]", /* _9 1 5 1 */},
 
519
  {"[_4 1 3 1 2]", /* _10 1 6 1 2 */},
 
520
 
 
521
  {"[5]", /* 5 */ "5 cascade"},
 
522
  {"[_5 _5 _5 _5 _5 5 5 5 5 5]", /* _5 _5 _5 _5 _5 5 5 5 5 5 */},
 
523
  {"[+5 x5 =5]", /* +5 x5 =5 */ "Mill's mess for 5"},
 
524
  {"[5 4 4]", /* 7 4 4 */},
 
525
  {"[_5 4 4]", /* _7 4 4 */},
 
526
  {"[1 2 3 4 5 5 5 5 5]", /* 1 2 3 4 5 6 7 8 9 */ "5 ramp"},
 
527
  {"[5 4 5 3 1]", /* 8 5 7 4 1, by Allen Knutson */},
 
528
  {"[_5 4 1 +4]", /* _9 5 1 5 */},
 
529
  {"[_5 4 +4 +4]", /* _8 4 +4 +4 */},
 
530
  {"[_5 4 4 4 1]", /* _9 5 5 5 1 */},
 
531
  {"[_5 4 4 5 1]",},
 
532
  {"[_5 4 4 +4 4 0]", /*_10 5 5 +5 5 0 */},
 
533
 
 
534
  {"[6]", /* 6 */ "6 cascade"},
 
535
  {"[+6 5]", /* +7 5 */},
 
536
  {"[6 4]", /* 8 4 */},
 
537
  {"[+6 3]", /* +9 3 */},
 
538
  {"[6 5 4 4]", /* 9 7 4 4 */},
 
539
  {"[+6 5 5 5]", /* +9 5 5 5 */},
 
540
  {"[6 0 6]", /* 9 0 9 */},
 
541
  {"[_6 0 _6]", /* _9 0 _9 */},
 
542
 
 
543
  {"[_7]", /* _7 */ "bouncing 7 cascade"},
 
544
  {"[7]", /* 7 */ "7 cascade"},
 
545
  {"[7 6 6 6 6]", /* 11 6 6 6 6 */ "Gatto's High Throw"},
 
546
 
 
547
};
 
548
 
 
549
 
 
550
 
 
551
typedef struct { int start; int number; } PatternIndex;
 
552
 
 
553
struct patternindex {
 
554
  int minballs;
 
555
  int maxballs;
 
556
  PatternIndex index[countof(portfolio)];
 
557
};
 
558
 
 
559
 
 
560
/* Jugglestruct: per-screen global data.  The master Object
 
561
 * and Trajectory lists are anchored here. */
 
562
typedef struct {
 
563
  GLXContext *glx_context;
 
564
  rotator *rot;
 
565
  trackball_state *trackball;
 
566
  Bool button_down_p;
 
567
 
 
568
  double        scale;
 
569
  double        cx;
 
570
  double        Gr;
 
571
  Trajectory   *head;
 
572
  Arm           arm[2][2];
 
573
  char         *pattern;
 
574
  int           count;
 
575
  int           num_balls;
 
576
  time_t        begintime; /* should make 'time' usable for at least 48 days
 
577
                                                        on a 32-bit machine */
 
578
  unsigned long time; /* millisecond timer*/
 
579
  ObjType       objtypes;
 
580
  Object       *objects;
 
581
  struct patternindex patternindex;
 
582
 
 
583
  XFontStruct *mode_font;
 
584
  GLuint font_dlist;
 
585
} jugglestruct;
 
586
 
 
587
static jugglestruct *juggles = (jugglestruct *) NULL;
 
588
 
 
589
/*******************
 
590
 * list management *
 
591
 *******************/
 
592
 
 
593
#define DUP_OBJECT(n, t) { \
 
594
  (n)->object = (t)->object; \
 
595
  if((n)->object != NULL) (n)->object->count++; \
 
596
}
 
597
 
 
598
/* t must point to an existing element.  t must not be an
 
599
   expression ending ->next or ->prev */
 
600
#define REMOVE(t) { \
 
601
  (t)->next->prev = (t)->prev; \
 
602
  (t)->prev->next = (t)->next; \
 
603
  free(t); \
 
604
}
 
605
 
 
606
/* t receives element to be created and added to the list.  ot must
 
607
   point to an existing element or be identical to t to start a new
 
608
   list. Applicable to Trajectories, Objects and Traces. */
 
609
#define ADD_ELEMENT(type, t, ot) \
 
610
  if (((t) = (type*)calloc(1,sizeof(type))) != NULL) { \
 
611
    (t)->next = (ot)->next; \
 
612
    (t)->prev = (ot); \
 
613
    (ot)->next = (t); \
 
614
    (t)->next->prev = (t); \
 
615
  }
 
616
 
 
617
static void
 
618
object_destroy(Object* o)
 
619
{
 
620
  if(o->trace != NULL) {
 
621
        while(o->trace->next != o->trace) {
 
622
          Trace *s = o->trace->next;
 
623
          REMOVE(s); /* Don't eliminate 's' */
 
624
        }
 
625
        free(o->trace);
 
626
  }
 
627
  REMOVE(o);
 
628
}
 
629
 
 
630
static void
 
631
trajectory_destroy(Trajectory *t) {
 
632
  if(t->name != NULL) free(t->name);
 
633
  if(t->pattern != NULL) free(t->pattern);
 
634
  /* Reduce object link count and call destructor if necessary */
 
635
  if(t->object != NULL && --t->object->count < 1 && t->object->tracelen == 0) {
 
636
        object_destroy(t->object);
 
637
  }
 
638
  REMOVE(t); /* Unlink and free */
 
639
}
 
640
 
 
641
static void
 
642
free_juggle(jugglestruct *sp) {
 
643
  if (sp->head != NULL) {
 
644
        while (sp->head->next != sp->head) {
 
645
          trajectory_destroy(sp->head->next);
 
646
        }
 
647
        free(sp->head);
 
648
        sp->head = (Trajectory *) NULL;
 
649
  }
 
650
  if(sp->objects != NULL) {
 
651
        while (sp->objects->next != sp->objects) {
 
652
          object_destroy(sp->objects->next);
 
653
        }
 
654
        free(sp->objects);
 
655
        sp->objects = (Object*)NULL;
 
656
  }
 
657
  if(sp->pattern != NULL) {
 
658
        free(sp->pattern);
 
659
        sp->pattern = NULL;
 
660
  }
 
661
  if (sp->mode_font!=None) {
 
662
        XFreeFontInfo(NULL,sp->mode_font,1);
 
663
        sp->mode_font = None;
 
664
  }
 
665
}
 
666
 
 
667
static Bool
 
668
add_throw(jugglestruct *sp, char type, int h, Notation n, const char* name)
 
669
{
 
670
  Trajectory *t;
 
671
 
 
672
  ADD_ELEMENT(Trajectory, t, sp->head->prev);
 
673
  if(t == NULL){ /* Out of Memory */
 
674
        free_juggle(sp);
 
675
        return False;
 
676
  }
 
677
  t->object = NULL;
 
678
  if(name != NULL)
 
679
        t->name = strdup(name);
 
680
  t->posn = type;
 
681
  if (n == ADAM) {
 
682
        t->adam = h;
 
683
        t->height = 0;
 
684
        t->status = ATCH;
 
685
  } else {
 
686
        t->height = h;
 
687
        t->status = THRATCH;
 
688
  }
 
689
  return True;
 
690
}
 
691
 
 
692
/* add a Thratch to the performance */
 
693
static Bool
 
694
program(ModeInfo *mi, const char *patn, const char *name, int cycles)
 
695
{
 
696
  jugglestruct *sp = &juggles[MI_SCREEN(mi)];
 
697
  const char *p;
 
698
  int w, h, i, seen;
 
699
  Notation notation;
 
700
  char type;
 
701
 
 
702
  if (MI_IS_VERBOSE(mi)) {
 
703
        (void) fprintf(stderr, "juggle[%d]: Programmed: %s x %d\n",
 
704
                                   MI_SCREEN(mi), (name == NULL) ? patn : name, cycles);
 
705
  }
 
706
 
 
707
  for(w=i=0; i < cycles; i++, w++) { /* repeat until at least "cycles" throws
 
708
                                                                                have been programmed */
 
709
        /* title is the pattern name to be supplied to the first throw of
 
710
           a sequence.  If no name if given, use an empty title so that
 
711
           the sequences are still delimited. */
 
712
        const char *title = (name != NULL)? name : "";
 
713
        type=' ';
 
714
        h = 0;
 
715
        seen = 0;
 
716
        notation = HEIGHT;
 
717
        for(p=patn; *p; p++) {
 
718
          if (*p >= '0' && *p <='9') {
 
719
                seen = 1;
 
720
                h = 10*h + (*p - '0');
 
721
          } else {
 
722
                Notation nn = notation;
 
723
                switch (*p) {
 
724
                case '[':            /* begin Adam notation */
 
725
                  notation = ADAM;
 
726
                  break;
 
727
                case '-':            /* Inside throw */
 
728
                  type = ' ';
 
729
                  break;
 
730
                case '+':            /* Outside throw */
 
731
                case '=':            /* Cross throw */
 
732
                case '&':            /* Cross catch */
 
733
                case 'x':            /* Cross throw and catch */
 
734
                case '_':            /* Bounce */
 
735
                case 'k':            /* Kickup */
 
736
                  type = *p;
 
737
                  break;
 
738
                case '*':            /* Lose ball */
 
739
                  seen = 1;
 
740
                  h = -1;
 
741
                  /* fall through */
 
742
                case ']':             /* end Adam notation */
 
743
                  nn = HEIGHT;
 
744
                  /* fall through */
 
745
                case ' ':
 
746
                  if (seen) {
 
747
                        i++;
 
748
                        if (!add_throw(sp, type, h, notation, title))
 
749
                                return False;
 
750
                        title = NULL;
 
751
                        type=' ';
 
752
                        h = 0;
 
753
                        seen = 0;
 
754
                  }
 
755
                  notation = nn;
 
756
                  break;
 
757
                default:
 
758
                  if(w == 0) { /* Only warn on first pass */
 
759
                        (void) fprintf(stderr,
 
760
                                                   "juggle[%d]: Unexpected pattern instruction: '%c'\n",
 
761
                                                   MI_SCREEN(mi), *p);
 
762
                  }
 
763
                  break;
 
764
                }
 
765
          }
 
766
        }
 
767
        if (seen) { /* end of sequence */
 
768
          if (!add_throw(sp, type, h, notation, title))
 
769
                return False;
 
770
          title = NULL;
 
771
        }
 
772
  }
 
773
  return True;
 
774
}
 
775
 
 
776
/*
 
777
 ~~~~\~~~~~\~~~
 
778
 \\~\\~\~\\\~~~
 
779
 \\~\\\\~\\\~\~
 
780
 \\\\\\\\\\\~\\
 
781
 
 
782
[ 3 3 1 3 4 2 3 1 3 3 4 0 2 1 ]
 
783
 
 
784
4 4 1 3 12 2 4 1 4 4 13 0 3 1
 
785
 
 
786
*/
 
787
#define BOUNCEOVER 10
 
788
#define KICKMIN 7
 
789
#define THROWMAX 20
 
790
 
 
791
/* Convert Adam notation into heights */
 
792
static void
 
793
adam(jugglestruct *sp)
 
794
{
 
795
  Trajectory *t, *p;
 
796
  for(t = sp->head->next; t != sp->head; t = t->next) {
 
797
        if (t->status == ATCH) {
 
798
          int a = t->adam;
 
799
          t->height = 0;
 
800
          for(p = t->next; a > 0; p = p->next) {
 
801
                if(p == sp->head) {
 
802
                  t->height = -9; /* Indicate end of processing for name() */
 
803
                  return;
 
804
                }
 
805
                if (p->status != ATCH || p->adam < 0 || p->adam>= a) {
 
806
                  a--;
 
807
                }
 
808
                t->height++;
 
809
          }
 
810
          if(t->height > BOUNCEOVER && t->posn == ' '){
 
811
                t->posn = '_'; /* high defaults can be bounced */
 
812
          } else if(t->height < 3 && t->posn == '_') {
 
813
                t->posn = ' '; /* Can't bounce short throws. */
 
814
          }
 
815
          if(t->height < KICKMIN && t->posn == 'k'){
 
816
                t->posn = ' '; /* Can't kick short throws */
 
817
          }
 
818
          if(t->height > THROWMAX){
 
819
                t->posn = 'k'; /* Use kicks for ridiculously high throws */
 
820
          }
 
821
          t->status = THRATCH;
 
822
        }
 
823
  }
 
824
}
 
825
 
 
826
/* Discover converted heights and update the sequence title */
 
827
static void
 
828
name(jugglestruct *sp)
 
829
{
 
830
  Trajectory *t, *p;
 
831
  char buffer[BUFSIZ];
 
832
  char *b;
 
833
  for(t = sp->head->next; t != sp->head; t = t->next) {
 
834
        if (t->status == THRATCH && t->name != NULL) {
 
835
          b = buffer;
 
836
          for(p = t; p == t || p->name == NULL; p = p->next) {
 
837
                if(p == sp->head || p->height < 0) { /* end of reliable data */
 
838
                  return;
 
839
                }
 
840
                if(p->posn == ' ') {
 
841
                  b += sprintf(b, " %d", p->height);
 
842
                } else {
 
843
                  b += sprintf(b, " %c%d", p->posn, p->height);
 
844
                }
 
845
                if(b - buffer > 500) break; /* otherwise this could eventually
 
846
                                                                           overflow.  It'll be too big to
 
847
                                                                           display anyway. */
 
848
          }
 
849
          if(*t->name != 0) {
 
850
                (void) sprintf(b, ", %s", t->name);
 
851
          }
 
852
          free(t->name); /* Don't need name any more, it's been converted
 
853
                                                to pattern */
 
854
          t->name = NULL;
 
855
          if(t->pattern != NULL) free(t->pattern);
 
856
          t->pattern = strdup(buffer);
 
857
        }
 
858
  }
 
859
}
 
860
 
 
861
/* Split Thratch notation into explicit throws and catches.
 
862
   Usually Catch follows Throw in same hand, but take care of special
 
863
   cases. */
 
864
 
 
865
/* ..n1.. -> .. LTn RT1 LC RC .. */
 
866
/* ..nm.. -> .. LTn LC RTm RC .. */
 
867
 
 
868
static Bool
 
869
part(jugglestruct *sp)
 
870
{
 
871
  Trajectory *t, *nt, *p;
 
872
  Hand hand = (LRAND() & 1) ? RIGHT : LEFT;
 
873
 
 
874
  for (t = sp->head->next; t != sp->head; t = t->next) {
 
875
        if (t->status > THRATCH) {
 
876
          hand = t->hand;
 
877
        } else if (t->status == THRATCH) {
 
878
          char posn = '=';
 
879
 
 
880
          /* plausibility check */
 
881
          if (t->height <= 2 && t->posn == '_') {
 
882
                t->posn = ' '; /* no short bounces */
 
883
          }
 
884
          if (t->height <= 1 && (t->posn == '=' || t->posn == '&')) {
 
885
                t->posn = ' '; /* 1's need close catches */
 
886
          }
 
887
 
 
888
          switch (t->posn) {
 
889
                  /*         throw          catch    */
 
890
          case ' ': posn = '-'; t->posn = '+'; break;
 
891
          case '+': posn = '+'; t->posn = '-'; break;
 
892
          case '=': posn = '='; t->posn = '+'; break;
 
893
          case '&': posn = '+'; t->posn = '='; break;
 
894
          case 'x': posn = '='; t->posn = '='; break;
 
895
          case '_': posn = '_'; t->posn = '-'; break;
 
896
          case 'k': posn = 'k'; t->posn = 'k'; break;
 
897
          default:
 
898
                (void) fprintf(stderr, "juggle: unexpected posn %c\n", t->posn);
 
899
                break;
 
900
          }
 
901
          hand = (Hand) ((hand + 1) % 2);
 
902
          t->status = ACTION;
 
903
          t->hand = hand;
 
904
          p = t->prev;
 
905
 
 
906
          if (t->height == 1 && p != sp->head) {
 
907
                p = p->prev; /* '1's are thrown earlier than usual */
 
908
          }
 
909
 
 
910
 
 
911
 
 
912
          t->action = CATCH;
 
913
          ADD_ELEMENT(Trajectory, nt, p);
 
914
          if(nt == NULL){
 
915
                free_juggle(sp);
 
916
                return False;
 
917
          }
 
918
          nt->object = NULL;
 
919
          nt->status = ACTION;
 
920
          nt->action = THROW;
 
921
          nt->height = t->height;
 
922
          nt->hand = hand;
 
923
          nt->posn = posn;
 
924
 
 
925
        }
 
926
  }
 
927
  return True;
 
928
}
 
929
 
 
930
static ObjType
 
931
choose_object(void) {
 
932
  ObjType o;
 
933
  for (;;) {
 
934
        o = (ObjType)NRAND((ObjType)NUM_OBJECT_TYPES);
 
935
        if(balls && o == BALL) break;
 
936
        if(clubs && o == CLUB) break;
 
937
        if(torches && o == TORCH) break;
 
938
        if(knives && o == KNIFE) break;
 
939
        if(rings && o == RING) break;
 
940
        if(bballs && o == BBALLS) break;
 
941
  }
 
942
  return o;
 
943
}
 
944
 
 
945
/* Connnect up throws and catches to figure out which ball goes where.
 
946
   Do the same with the juggler's hands. */
 
947
 
 
948
static void
 
949
lob(ModeInfo *mi)
 
950
{
 
951
  jugglestruct *sp = &juggles[MI_SCREEN(mi)];
 
952
  Trajectory *t, *p;
 
953
  int h;
 
954
  for (t = sp->head->next; t != sp->head; t = t->next) {
 
955
        if (t->status == ACTION) {
 
956
          if (t->action == THROW) {
 
957
                if (t->type == Empty) {
 
958
                  /* Create new Object */
 
959
                  ADD_ELEMENT(Object, t->object, sp->objects);
 
960
                  t->object->count = 1;
 
961
                  t->object->tracelen = 0;
 
962
                  t->object->active = False;
 
963
                  /* Initialise object's circular trace list */
 
964
                  ADD_ELEMENT(Trace, t->object->trace, t->object->trace);
 
965
 
 
966
                  if (MI_NPIXELS(mi) > 2) {
 
967
                        t->object->color = 1 + NRAND(MI_NPIXELS(mi) - 2);
 
968
                  } else {
 
969
#ifdef STANDALONE
 
970
                        t->object->color = 1;
 
971
#else
 
972
                        t->object->color = 0;
 
973
#endif
 
974
                  }
 
975
 
 
976
                  /* Small chance of picking a random object instead of the
 
977
                         current theme. */
 
978
                  if(NRAND(OBJMIXPROB) == 0) {
 
979
                        t->object->type = choose_object();
 
980
                  } else {
 
981
                        t->object->type = sp->objtypes;
 
982
                  }
 
983
 
 
984
                  /* Check to see if we need trails for this object */
 
985
                  if(tail < ObjectDefs[t->object->type].mintrail) {
 
986
                        t->object->tail = ObjectDefs[t->object->type].mintrail;
 
987
                  } else {
 
988
                        t->object->tail = tail;
 
989
                  }
 
990
                }
 
991
 
 
992
                /* Balls can change divisions at each throw */
 
993
                /* no, that looks stupid. -jwz */
 
994
                if (t->divisions < 1)
 
995
                  t->divisions = 2 * (NRAND(2) + 1);
 
996
 
 
997
                /* search forward for next catch in this hand */
 
998
                for (p = t->next; t->handlink == NULL; p = p->next) {
 
999
                  if(p->status < ACTION || p == sp->head) return;
 
1000
                  if (p->action == CATCH) {
 
1001
                        if (t->handlink == NULL && p->hand == t->hand) {
 
1002
                          t->handlink = p;
 
1003
                        }
 
1004
                  }
 
1005
                }
 
1006
 
 
1007
                if (t->height > 0) {
 
1008
                  h = t->height - 1;
 
1009
 
 
1010
                  /* search forward for next ball catch */
 
1011
                  for (p = t->next; t->balllink == NULL; p = p->next) {
 
1012
                        if(p->status < ACTION || p == sp->head) {
 
1013
                          t->handlink = NULL;
 
1014
                          return;
 
1015
                        }
 
1016
                        if (p->action == CATCH) {
 
1017
                          if (t->balllink == NULL && --h < 1) { /* caught */
 
1018
                                t->balllink = p; /* complete trajectory */
 
1019
# if 0
 
1020
                                if (p->type == Full) {
 
1021
                                  (void) fprintf(stderr, "juggle[%d]: Dropped %d\n",
 
1022
                                                  MI_SCREEN(mi), t->object->color);
 
1023
                                }
 
1024
#endif
 
1025
                                p->type = Full;
 
1026
                                DUP_OBJECT(p, t); /* accept catch */
 
1027
                                p->angle = t->angle;
 
1028
                                p->divisions = t->divisions;
 
1029
                          }
 
1030
                        }
 
1031
                  }
 
1032
                }
 
1033
                t->type = Empty; /* thrown */
 
1034
          } else if (t->action == CATCH) {
 
1035
                /* search forward for next throw from this hand */
 
1036
                for (p = t->next; t->handlink == NULL; p = p->next) {
 
1037
                  if(p->status < ACTION || p == sp->head) return;
 
1038
                  if (p->action == THROW && p->hand == t->hand) {
 
1039
                        p->type = t->type; /* pass ball */
 
1040
                        DUP_OBJECT(p, t); /* pass object */
 
1041
                        p->divisions = t->divisions;
 
1042
                        t->handlink = p;
 
1043
                  }
 
1044
                }
 
1045
          }
 
1046
          t->status = LINKEDACTION;
 
1047
        }
 
1048
  }
 
1049
}
 
1050
 
 
1051
/* Clap when both hands are empty */
 
1052
static void
 
1053
clap(jugglestruct *sp)
 
1054
{
 
1055
  Trajectory *t, *p;
 
1056
  for (t = sp->head->next; t != sp->head; t = t->next) {
 
1057
        if (t->status == LINKEDACTION &&
 
1058
                t->action == CATCH &&
 
1059
                t->type == Empty &&
 
1060
                t->handlink != NULL &&
 
1061
                t->handlink->height == 0) { /* Completely idle hand */
 
1062
 
 
1063
          for (p = t->next; p != sp->head; p = p->next) {
 
1064
                if (p->status == LINKEDACTION &&
 
1065
                        p->action == CATCH &&
 
1066
                        p->hand != t->hand) { /* Next catch other hand */
 
1067
                  if(p->type == Empty &&
 
1068
                         p->handlink != NULL &&
 
1069
                         p->handlink->height == 0) { /* Also completely idle */
 
1070
 
 
1071
                        t->handlink->posn = '^'; /* Move first hand's empty throw */
 
1072
                        p->posn = '^';           /* to meet second hand's empty
 
1073
                                                                                catch */
 
1074
 
 
1075
                  }
 
1076
                  break; /* Only need first catch */
 
1077
                }
 
1078
          }
 
1079
        }
 
1080
  }
 
1081
}
 
1082
 
 
1083
#define CUBIC(s, t) ((((s).a * (t) + (s).b) * (t) + (s).c) * (t) + (s).d)
 
1084
 
 
1085
/* Compute single spline from x0 with velocity dx0 at time t0 to x1
 
1086
   with velocity dx1 at time t1 */
 
1087
static Spline
 
1088
makeSpline(double x0, double dx0, int t0, double x1, double dx1, int t1)
 
1089
{
 
1090
  Spline s;
 
1091
  double a, b, c, d;
 
1092
  double x10;
 
1093
  double t10;
 
1094
 
 
1095
  x10 = x1 - x0;
 
1096
  t10 = t1 - t0;
 
1097
  a = ((dx0 + dx1)*t10 - 2*x10) / (t10*t10*t10);
 
1098
  b = (3*x10 - (2*dx0 + dx1)*t10) / (t10*t10);
 
1099
  c = dx0;
 
1100
  d = x0;
 
1101
  s.a = a;
 
1102
  s.b = -3*a*t0 + b;
 
1103
  s.c = (3*a*t0 - 2*b)*t0 + c;
 
1104
  s.d = ((-a*t0 + b)*t0 - c)*t0 +d;
 
1105
  return s;
 
1106
}
 
1107
 
 
1108
/* Compute a pair of splines.  s1 goes from x0 vith velocity dx0 at
 
1109
   time t0 to x1 at time t1.  s2 goes from x1 at time t1 to x2 with
 
1110
   velocity dx2 at time t2.  The arrival and departure velocities at
 
1111
   x1, t1 must be the same. */
 
1112
static double
 
1113
makeSplinePair(Spline *s1, Spline *s2,
 
1114
                           double x0, double dx0, int t0,
 
1115
                           double x1,             int t1,
 
1116
                           double x2, double dx2, int t2)
 
1117
{
 
1118
  double x10, x21, t21, t10, t20, dx1;
 
1119
  x10 = x1 - x0;
 
1120
  x21 = x2 - x1;
 
1121
  t21 = t2 - t1;
 
1122
  t10 = t1 - t0;
 
1123
  t20 = t2 - t0;
 
1124
  dx1 = (3*x10*t21*t21 + 3*x21*t10*t10 + 3*dx0*t10*t21*t21
 
1125
                 - dx2*t10*t10*t21 - 4*dx0*t10*t21*t21) /
 
1126
        (2*t10*t21*t20);
 
1127
  *s1 = makeSpline(x0, dx0, t0, x1, dx1, t1);
 
1128
  *s2 = makeSpline(x1, dx1, t1, x2, dx2, t2);
 
1129
  return dx1;
 
1130
}
 
1131
 
 
1132
/* Compute a Ballistic path in a pair of degenerate splines.  sx goes
 
1133
   from x at time t at constant velocity dx.  sy goes from y at time t
 
1134
   with velocity dy and constant acceleration g. */
 
1135
static void
 
1136
makeParabola(Trajectory *n,
 
1137
                         double x, double dx, double y, double dy, double g)
 
1138
{
 
1139
  double t = (double)n->start;
 
1140
  n->xp.a = 0;
 
1141
  n->xp.b = 0;
 
1142
  n->xp.c = dx;
 
1143
  n->xp.d = -dx*t + x;
 
1144
  n->yp.a = 0;
 
1145
  n->yp.b = g/2;
 
1146
  n->yp.c = -g*t + dy;
 
1147
  n->yp.d = g/2*t*t - dy*t + y;
 
1148
}
 
1149
 
 
1150
 
 
1151
 
 
1152
 
 
1153
#define SX 25 /* Shoulder Width */
 
1154
 
 
1155
/* Convert hand position symbols into actual time/space coordinates */
 
1156
static void
 
1157
positions(jugglestruct *sp)
 
1158
{
 
1159
  Trajectory *t;
 
1160
  unsigned long now = sp->time; /* Make sure we're not lost in the past */
 
1161
  for (t = sp->head->next; t != sp->head; t = t->next) {
 
1162
        if (t->status >= PTHRATCH) {
 
1163
          now = t->start;
 
1164
        } else if (t->status == ACTION || t->status == LINKEDACTION) {
 
1165
          /* Allow ACTIONs to be annotated, but we won't mark them ready
 
1166
                 for the next stage */
 
1167
 
 
1168
          double xo = 0, yo;
 
1169
          double sx = SX;
 
1170
          double pose = SX/2;
 
1171
 
 
1172
          /* time */
 
1173
          if (t->action == CATCH) { /* Throw-to-catch */
 
1174
                if (t->type == Empty) {
 
1175
                  now += (int) THROW_NULL_INTERVAL; /* failed catch is short */
 
1176
                } else {     /* successful catch */
 
1177
                  now += (int)(THROW_CATCH_INTERVAL);
 
1178
                }
 
1179
          } else { /* Catch-to-throw */
 
1180
                if(t->object != NULL) {
 
1181
                  now += (int) (CATCH_THROW_INTERVAL *
 
1182
                                                ObjectDefs[t->object->type].weight);
 
1183
                } else {
 
1184
                  now += (int) (CATCH_THROW_INTERVAL);
 
1185
                }
 
1186
          }
 
1187
 
 
1188
          if(t->start == 0)
 
1189
                t->start = now;
 
1190
          else /* Concatenated performances may need clock resync */
 
1191
                now = t->start;
 
1192
 
 
1193
          t->cx = 0;
 
1194
 
 
1195
          /* space */
 
1196
          yo = 90;
 
1197
 
 
1198
          /* Add room for the handle */
 
1199
          if(t->action == CATCH && t->object != NULL)
 
1200
                yo -= ObjectDefs[t->object->type].handle;
 
1201
 
 
1202
          switch (t->posn) {
 
1203
          case '-': xo = sx - pose; break;
 
1204
          case '_':
 
1205
          case 'k':
 
1206
          case '+': xo = sx + pose; break;
 
1207
          case '~':
 
1208
          case '=': xo = - sx - pose; yo += pose; break;
 
1209
          case '^': xo = 0; yo += pose*2; break; /* clap */
 
1210
          default:
 
1211
                (void) fprintf(stderr, "juggle: unexpected posn %c\n", t->posn);
 
1212
                break;
 
1213
          }
 
1214
 
 
1215
#ifdef _2DSpinsDontWorkIn3D
 
1216
          t->angle = (((t->hand == LEFT) ^
 
1217
                                   (t->posn == '+' || t->posn == '_' || t->posn == 'k' ))?
 
1218
                                        -1 : 1) * M_PI/2;
 
1219
#else
 
1220
         t->angle = -M_PI/2;
 
1221
#endif
 
1222
 
 
1223
          t->x = t->cx + ((t->hand == LEFT) ? xo : -xo);
 
1224
          t->y = yo;
 
1225
 
 
1226
          /* Only mark complete if it was already linked */
 
1227
          if(t->status == LINKEDACTION) {
 
1228
                t->status = PTHRATCH;
 
1229
          }
 
1230
        }
 
1231
  }
 
1232
}
 
1233
 
 
1234
 
 
1235
/* Private physics functions */
 
1236
 
 
1237
/* Compute the spin-rate for a trajectory.  Different types of throw
 
1238
   (eg, regular thows, bounces, kicks, etc) have different spin
 
1239
   requirements.
 
1240
 
 
1241
   type = type of object
 
1242
   h = trajectory of throwing hand (throws), or next throwing hand (catches)
 
1243
   old = earlier spin to consider
 
1244
   dt = time span of this trajectory
 
1245
   height = height of ball throw or 0 if based on old spin
 
1246
   turns = full club turns required during this operation
 
1247
   togo = partial club turns required to match hands
 
1248
*/
 
1249
static double
 
1250
spinrate(ObjType type, Trajectory *h, double old, double dt,
 
1251
                 int height, int turns, double togo)
 
1252
{
 
1253
#ifdef _2DSpinsDontWorkIn3D
 
1254
  const int dir = (h->hand == LEFT) ^ (h->posn == '+')? -1 : 1;
 
1255
#else
 
1256
  const int dir = 1;
 
1257
#endif
 
1258
 
 
1259
  if(ObjectDefs[type].handle != 0) { /* Clubs */
 
1260
        return (dir * turns * 2 * M_PI + togo) / dt;
 
1261
  } else if(height == 0) { /* Balls already spinning */
 
1262
        return old/2;
 
1263
  } else { /* Balls */
 
1264
        return dir * NRAND(height*10)/20/ObjectDefs[type].weight * 2 * M_PI / dt;
 
1265
  }
 
1266
}
 
1267
 
 
1268
 
 
1269
/* compute the angle at the end of a spinning trajectory */
 
1270
static double
 
1271
end_spin(Trajectory *t)
 
1272
{
 
1273
  return t->angle + t->spin * (t->finish - t->start);
 
1274
}
 
1275
 
 
1276
/* Sets the initial angle of the catch following hand movement t to
 
1277
   the final angle of the throw n.  Also sets the angle of the
 
1278
   subsequent throw to the same angle plus half a turn. */
 
1279
static void
 
1280
match_spins_on_catch(Trajectory *t, Trajectory *n)
 
1281
{
 
1282
  if(ObjectDefs[t->balllink->object->type].handle == 0) {
 
1283
        t->balllink->angle = end_spin(n);
 
1284
        if(t->balllink->handlink != NULL) {
 
1285
#ifdef _2DSpinsDontWorkIn3D
 
1286
          t->balllink->handlink->angle = t->balllink->angle + M_PI;
 
1287
#else
 
1288
         t->balllink->handlink->angle = t->balllink->angle;
 
1289
#endif
 
1290
        }
 
1291
  }
 
1292
}
 
1293
 
 
1294
static double
 
1295
find_bounce(jugglestruct *sp,
 
1296
                        double yo, double yf, double yc, double tc, double cor)
 
1297
{
 
1298
  double tb, i, dy = 0;
 
1299
  const double e = 1; /* permissible error in yc */
 
1300
 
 
1301
  /*
 
1302
        tb = time to bounce
 
1303
        yt = height at catch time after one bounce
 
1304
        one or three roots according to timing
 
1305
        find one by interval bisection
 
1306
  */
 
1307
  tb = tc;
 
1308
  for(i = tc / 2; i > 0.0001; i/=2){
 
1309
        double dt, yt;
 
1310
        if(tb == 0){
 
1311
          (void) fprintf(stderr, "juggle: bounce div by zero!\n");
 
1312
          break;
 
1313
        }
 
1314
        dy = (yf - yo)/tb + sp->Gr/2*tb;
 
1315
        dt = tc - tb;
 
1316
        yt = -cor*dy*dt + sp->Gr/2*dt*dt + yf;
 
1317
        if(yt < yc + e){
 
1318
          tb-=i;
 
1319
        }else if(yt > yc - e){
 
1320
          tb+=i;
 
1321
        }else{
 
1322
          break;
 
1323
        }
 
1324
  }
 
1325
  if(dy*THROW_CATCH_INTERVAL < -200) { /* bounce too hard */
 
1326
        tb = -1;
 
1327
  }
 
1328
  return tb;
 
1329
}
 
1330
 
 
1331
static Trajectory*
 
1332
new_predictor(const Trajectory *t, int start, int finish, double angle)
 
1333
{
 
1334
  Trajectory *n;
 
1335
  ADD_ELEMENT(Trajectory, n, t->prev);
 
1336
  if(n == NULL){
 
1337
        return NULL;
 
1338
  }
 
1339
  DUP_OBJECT(n, t);
 
1340
  n->divisions = t->divisions;
 
1341
  n->type = Ball;
 
1342
  n->status = PREDICTOR;
 
1343
 
 
1344
  n->start = start;
 
1345
  n->finish = finish;
 
1346
  n->angle = angle;
 
1347
  return n;
 
1348
}
 
1349
 
 
1350
/* Turn abstract timings into physically appropriate object trajectories. */
 
1351
static Bool
 
1352
projectile(jugglestruct *sp)
 
1353
{
 
1354
  Trajectory *t;
 
1355
  const int yf = 0; /* Floor height */
 
1356
 
 
1357
  for (t = sp->head->next; t != sp->head; t = t->next) {
 
1358
        if (t->status != PTHRATCH || t->action != THROW) {
 
1359
          continue;
 
1360
        } else if (t->balllink == NULL) { /* Zero Throw */
 
1361
          t->status = BPREDICTOR;
 
1362
        } else if (t->balllink->handlink == NULL) { /* Incomplete */
 
1363
          return True;
 
1364
        } else if(t->balllink == t->handlink) {
 
1365
          /* '2' height - hold on to ball.  Don't need to consider
 
1366
                 flourishes, 'hands' will do that automatically anyway */
 
1367
 
 
1368
          t->type = Full;
 
1369
          /* Zero spin to avoid wrist injuries */
 
1370
          t->spin = 0;
 
1371
          match_spins_on_catch(t, t);
 
1372
          t->dx = t->dy = 0;
 
1373
          t->status = BPREDICTOR;
 
1374
          continue;
 
1375
        } else {
 
1376
          if (t->posn == '_') { /* Bounce once */
 
1377
 
 
1378
                const int tb = t->start +
 
1379
                  find_bounce(sp, t->y, (double) yf, t->balllink->y,
 
1380
                                          (double) (t->balllink->start - t->start),
 
1381
                                          ObjectDefs[t->object->type].cor);
 
1382
 
 
1383
                if(tb < t->start) { /* bounce too hard */
 
1384
                  t->posn = '+'; /* Use regular throw */
 
1385
                } else {
 
1386
                  Trajectory *n; /* First (throw) trajectory. */
 
1387
                  double dt; /* Time span of a trajectory */
 
1388
                  double dy; /* Distance span of a follow-on trajectory.
 
1389
                                                First trajectory uses t->dy */
 
1390
                  /* dx is constant across both trajectories */
 
1391
                  t->dx = (t->balllink->x - t->x) / (t->balllink->start - t->start);
 
1392
 
 
1393
                  { /* ball follows parabola down */
 
1394
                        n = new_predictor(t, t->start, tb, t->angle);
 
1395
                        if(n == NULL) return False;
 
1396
                        dt = n->finish - n->start;
 
1397
                        /* Ball rate 4, no flight or matching club turns */
 
1398
                        n->spin = spinrate(t->object->type, t, 0.0, dt, 4, 0, 0.0);
 
1399
                        t->dy = (yf - t->y)/dt - sp->Gr/2*dt;
 
1400
                        makeParabola(n, t->x, t->dx, t->y, t->dy, sp->Gr);
 
1401
                  }
 
1402
 
 
1403
                  { /* ball follows parabola up */
 
1404
                        Trajectory *m = new_predictor(t, n->finish, t->balllink->start,
 
1405
                                                                                  end_spin(n));
 
1406
                        if(m == NULL) return False;
 
1407
                        dt = m->finish - m->start;
 
1408
                        /* Use previous ball rate, no flight club turns */
 
1409
                        m->spin = spinrate(t->object->type, t, n->spin, dt, 0, 0,
 
1410
                                                           t->balllink->angle - m->angle);
 
1411
                        match_spins_on_catch(t, m);
 
1412
                        dy = (t->balllink->y - yf)/dt - sp->Gr/2 * dt;
 
1413
                        makeParabola(m, t->balllink->x - t->dx * dt,
 
1414
                                                 t->dx, (double) yf, dy, sp->Gr);
 
1415
                  }
 
1416
 
 
1417
                  t->status = BPREDICTOR;
 
1418
                  continue;
 
1419
                }
 
1420
          } else if (t->posn == 'k') { /* Drop & Kick */
 
1421
                Trajectory *n; /* First (drop) trajectory. */
 
1422
                Trajectory *o; /* Second (rest) trajectory */
 
1423
                Trajectory *m; /* Third (kick) trajectory */
 
1424
                const int td = t->start + 2*THROW_CATCH_INTERVAL; /* Drop time */
 
1425
                const int tk = t->balllink->start - 5*THROW_CATCH_INTERVAL; /* Kick */
 
1426
                double dt, dy;
 
1427
 
 
1428
                { /* Fall to ground */
 
1429
                  n = new_predictor(t, t->start, td, t->angle);
 
1430
                  if(n == NULL) return False;
 
1431
                  dt = n->finish - n->start;
 
1432
                  /* Ball spin rate 4, no flight club turns */
 
1433
                  n->spin = spinrate(t->object->type, t, 0.0, dt, 4, 0,
 
1434
                                                         t->balllink->angle - n->angle);
 
1435
                  t->dx = (t->balllink->x - t->x) / dt;
 
1436
                  t->dy = (yf - t->y)/dt - sp->Gr/2*dt;
 
1437
                  makeParabola(n, t->x, t->dx, t->y, t->dy, sp->Gr);
 
1438
                }
 
1439
 
 
1440
                { /* Rest on ground */
 
1441
                  o = new_predictor(t, n->finish, tk, end_spin(n));
 
1442
                  if(o == NULL) return False;
 
1443
                  o->spin = 0;
 
1444
                  makeParabola(o, t->balllink->x, 0.0, (double) yf, 0.0, 0.0);
 
1445
                }
 
1446
 
 
1447
                /* Kick up */
 
1448
                {
 
1449
                  m = new_predictor(t, o->finish, t->balllink->start, end_spin(o));
 
1450
                  if(m == NULL) return False;
 
1451
                  dt = m->finish - m->start;
 
1452
                  /* Match receiving hand, ball rate 4, one flight club turn */
 
1453
                  m->spin = spinrate(t->object->type, t->balllink->handlink, 0.0, dt,
 
1454
                                                         4, 1, t->balllink->angle - m->angle);
 
1455
                  match_spins_on_catch(t, m);
 
1456
                  dy = (t->balllink->y - yf)/dt - sp->Gr/2 * dt;
 
1457
                  makeParabola(m, t->balllink->x, 0.0, (double) yf, dy, sp->Gr);
 
1458
                }
 
1459
 
 
1460
                t->status = BPREDICTOR;
 
1461
                continue;
 
1462
          }
 
1463
 
 
1464
          /* Regular flight, no bounce */
 
1465
          { /* ball follows parabola */
 
1466
                double dt;
 
1467
                Trajectory *n = new_predictor(t, t->start,
 
1468
                                                                          t->balllink->start, t->angle);
 
1469
                if(n == NULL) return False;
 
1470
                dt = t->balllink->start - t->start;
 
1471
                /* Regular spin */
 
1472
                n->spin = spinrate(t->object->type, t, 0.0, dt, t->height, t->height/2,
 
1473
                                                   t->balllink->angle - n->angle);
 
1474
                match_spins_on_catch(t, n);
 
1475
                t->dx = (t->balllink->x - t->x) / dt;
 
1476
                t->dy = (t->balllink->y - t->y) / dt - sp->Gr/2 * dt;
 
1477
                makeParabola(n, t->x, t->dx, t->y, t->dy, sp->Gr);
 
1478
          }
 
1479
 
 
1480
          t->status = BPREDICTOR;
 
1481
        }
 
1482
  }
 
1483
  return True;
 
1484
}
 
1485
 
 
1486
/* Turn abstract hand motions into cubic splines. */
 
1487
static void
 
1488
hands(jugglestruct *sp)
 
1489
{
 
1490
  Trajectory *t, *u, *v;
 
1491
 
 
1492
  for (t = sp->head->next; t != sp->head; t = t->next) {
 
1493
        /* no throw => no velocity */
 
1494
        if (t->status != BPREDICTOR) {
 
1495
          continue;
 
1496
        }
 
1497
 
 
1498
        u = t->handlink;
 
1499
        if (u == NULL) { /* no next catch */
 
1500
          continue;
 
1501
        }
 
1502
        v = u->handlink;
 
1503
        if (v == NULL) { /* no next throw */
 
1504
          continue;
 
1505
        }
 
1506
 
 
1507
        /* double spline takes hand from throw, thru catch, to
 
1508
           next throw */
 
1509
 
 
1510
        t->finish = u->start;
 
1511
        t->status = PREDICTOR;
 
1512
 
 
1513
        u->finish = v->start;
 
1514
        u->status = PREDICTOR;
 
1515
 
 
1516
 
 
1517
        /* FIXME: These adjustments leave a small glitch when alternating
 
1518
           balls and clubs.  Just hope no-one notices.  :-) */
 
1519
 
 
1520
        /* make sure empty hand spin matches the thrown object in case it
 
1521
           had a handle */
 
1522
 
 
1523
        t->spin = ((t->hand == LEFT)? -1 : 1 ) *
 
1524
          fabs((u->angle - t->angle)/(u->start - t->start));
 
1525
 
 
1526
        u->spin = ((v->hand == LEFT) ^ (v->posn == '+')? -1 : 1 ) *
 
1527
          fabs((v->angle - u->angle)/(v->start - u->start));
 
1528
 
 
1529
        (void) makeSplinePair(&t->xp, &u->xp,
 
1530
                                                  t->x, t->dx, t->start,
 
1531
                                                  u->x, u->start,
 
1532
                                                  v->x, v->dx, v->start);
 
1533
        (void) makeSplinePair(&t->yp, &u->yp,
 
1534
                                                  t->y, t->dy, t->start,
 
1535
                                                  u->y, u->start,
 
1536
                                                  v->y, v->dy, v->start);
 
1537
 
 
1538
        t->status = PREDICTOR;
 
1539
  }
 
1540
}
 
1541
 
 
1542
/* Given target x, y find_elbow puts hand at target if possible,
 
1543
 * otherwise makes hand point to the target */
 
1544
static void
 
1545
find_elbow(int armlength, DXPoint *h, DXPoint *e, DXPoint *p, DXPoint *s,
 
1546
                   int z)
 
1547
{
 
1548
  double r, h2, t;
 
1549
  double x = p->x - s->x;
 
1550
  double y = p->y - s->y;
 
1551
  h2 = x*x + y*y + z*z;
 
1552
  if (h2 > 4 * armlength * armlength) {
 
1553
        t = armlength/sqrt(h2);
 
1554
        e->x = t*x + s->x;
 
1555
        e->y = t*y + s->y;
 
1556
        h->x = 2 * t * x + s->x;
 
1557
        h->y = 2 * t * y + s->y;
 
1558
  } else {
 
1559
        r = sqrt((double)(x*x + z*z));
 
1560
        t = sqrt(4 * armlength * armlength / h2 - 1);
 
1561
        e->x = x*(1 + y*t/r)/2 + s->x;
 
1562
        e->y = (y - r*t)/2 + s->y;
 
1563
        h->x = x + s->x;
 
1564
        h->y = y + s->y;
 
1565
  }
 
1566
}
 
1567
 
 
1568
 
 
1569
/* NOTE: returned x, y adjusted for arm reach */
 
1570
static void
 
1571
reach_arm(ModeInfo * mi, Hand side, DXPoint *p)
 
1572
{
 
1573
  jugglestruct *sp = &juggles[MI_SCREEN(mi)];
 
1574
  DXPoint h, e;
 
1575
  find_elbow(40, &h, &e, p, &sp->arm[1][side][SHOULDER], 25);
 
1576
  *p = sp->arm[1][side][HAND] = h;
 
1577
  sp->arm[1][side][ELBOW] = e;
 
1578
}
 
1579
 
 
1580
#if DEBUG
 
1581
/* dumps a human-readable rendition of the current state of the juggle
 
1582
   pipeline to stderr for debugging */
 
1583
static void
 
1584
dump(jugglestruct *sp)
 
1585
{
 
1586
  Trajectory *t;
 
1587
  for (t = sp->head->next; t != sp->head; t = t->next) {
 
1588
        switch (t->status) {
 
1589
        case ATCH:
 
1590
          (void) fprintf(stderr, "%p a %c%d\n", (void*)t, t->posn, t->adam);
 
1591
          break;
 
1592
        case THRATCH:
 
1593
          (void) fprintf(stderr, "%p T %c%d %s\n", (void*)t, t->posn, t->height,
 
1594
                                         t->pattern == NULL?"":t->pattern);
 
1595
          break;
 
1596
        case ACTION:
 
1597
          if (t->action == CATCH)
 
1598
            (void) fprintf(stderr, "%p A %c%cC\n",
 
1599
                                         (void*)t, t->posn,
 
1600
                                         t->hand ? 'R' : 'L');
 
1601
          else
 
1602
            (void) fprintf(stderr, "%p A %c%c%c%d\n",
 
1603
                                         (void*)t, t->posn,
 
1604
                                         t->hand ? 'R' : 'L',
 
1605
                                         (t->action == THROW)?'T':'N',
 
1606
                                         t->height);
 
1607
          break;
 
1608
        case LINKEDACTION:
 
1609
          (void) fprintf(stderr, "%p L %c%c%c%d %d %p %p\n",
 
1610
                                         (void*)t, t->posn,
 
1611
                                         t->hand?'R':'L',
 
1612
                                         (t->action == THROW)?'T':(t->action == CATCH?'C':'N'),
 
1613
                                         t->height, t->object == NULL?0:t->object->color,
 
1614
                                         (void*)t->handlink, (void*)t->balllink);
 
1615
          break;
 
1616
        case PTHRATCH:
 
1617
          (void) fprintf(stderr, "%p O %c%c%c%d %d %2d %6lu %6lu\n",
 
1618
                                         (void*)t, t->posn,
 
1619
                                         t->hand?'R':'L',
 
1620
                                         (t->action == THROW)?'T':(t->action == CATCH?'C':'N'),
 
1621
                                         t->height, t->type, t->object == NULL?0:t->object->color,
 
1622
                                         t->start, t->finish);
 
1623
          break;
 
1624
        case BPREDICTOR:
 
1625
          (void) fprintf(stderr, "%p B %c      %2d %6lu %6lu %g\n",
 
1626
                                         (void*)t, t->type == Ball?'b':t->type == Empty?'e':'f',
 
1627
                                         t->object == NULL?0:t->object->color,
 
1628
                                         t->start, t->finish, t->yp.c);
 
1629
          break;
 
1630
        case PREDICTOR:
 
1631
          (void) fprintf(stderr, "%p P %c      %2d %6lu %6lu %g\n",
 
1632
                                         (void*)t, t->type == Ball?'b':t->type == Empty?'e':'f',
 
1633
                                         t->object == NULL?0:t->object->color,
 
1634
                                         t->start, t->finish, t->yp.c);
 
1635
          break;
 
1636
        default:
 
1637
          (void) fprintf(stderr, "%p: status %d not implemented\n",
 
1638
                                         (void*)t, t->status);
 
1639
          break;
 
1640
        }
 
1641
  }
 
1642
  (void) fprintf(stderr, "---\n");
 
1643
}
 
1644
#endif
 
1645
 
 
1646
static int get_num_balls(const char *j)
 
1647
{
 
1648
  int balls = 0;
 
1649
  const char *p;
 
1650
  int h = 0;
 
1651
  if (!j) abort();
 
1652
  for (p = j; *p; p++) {
 
1653
        if (*p >= '0' && *p <='9') { /* digit */
 
1654
          h = 10*h + (*p - '0');
 
1655
        } else {
 
1656
          if (h > balls) {
 
1657
                balls = h;
 
1658
          }
 
1659
          h = 0;
 
1660
        }
 
1661
  }
 
1662
  return balls;
 
1663
}
 
1664
 
 
1665
static int
 
1666
compare_num_balls(const void *p1, const void *p2)
 
1667
{
 
1668
  int i, j;
 
1669
  i = get_num_balls(((patternstruct*)p1)->pattern);
 
1670
  j = get_num_balls(((patternstruct*)p2)->pattern);
 
1671
  if (i > j) {
 
1672
        return (1);
 
1673
  } else if (i < j) {
 
1674
        return (-1);
 
1675
  } else {
 
1676
        return (0);
 
1677
  }
 
1678
}
 
1679
 
 
1680
 
 
1681
/**************************************************************************
 
1682
 *                        Rendering Functions                             *
 
1683
 *                                                                        *
 
1684
 **************************************************************************/
 
1685
 
 
1686
static int
 
1687
show_arms(ModeInfo * mi)
 
1688
{
 
1689
  int polys = 0;
 
1690
  jugglestruct *sp = &juggles[MI_SCREEN(mi)];
 
1691
  unsigned int i, j;
 
1692
  Hand side;
 
1693
  XPoint a[countof(sp->arm[0][0])];
 
1694
  int slices = 12;
 
1695
  int thickness = 7;
 
1696
  int soffx = 10;
 
1697
  int soffy = 11;
 
1698
 
 
1699
  j = 1;
 
1700
  for(side = LEFT; side <= RIGHT; side = (Hand)((int)side + 1)) {
 
1701
        /* Translate into device coords */
 
1702
        for(i = 0; i < countof(a); i++) {
 
1703
          a[i].x = (short)(SCENE_WIDTH/2 + sp->arm[j][side][i].x*sp->scale);
 
1704
          a[i].y = (short)(SCENE_HEIGHT  - sp->arm[j][side][i].y*sp->scale);
 
1705
          if(j == 1)
 
1706
                sp->arm[0][side][i] = sp->arm[1][side][i];
 
1707
        }
 
1708
 
 
1709
        glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
 
1710
 
 
1711
        /* Upper arm */
 
1712
        polys += tube (a[2].x - (side == LEFT ? soffx : -soffx), a[2].y + soffy, 0,
 
1713
                       a[1].x, a[1].y, ARMLENGTH/2,
 
1714
                       thickness, 0, slices,
 
1715
                       True, True, MI_IS_WIREFRAME(mi));
 
1716
 
 
1717
        /* Lower arm */
 
1718
        polys += tube (a[1].x, a[1].y, ARMLENGTH/2,
 
1719
                       a[0].x, a[0].y, ARMLENGTH,
 
1720
                       thickness * 0.8, 0, slices,
 
1721
                       True, True, MI_IS_WIREFRAME(mi));
 
1722
 
 
1723
        glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
 
1724
 
 
1725
        /* Shoulder */
 
1726
        glPushMatrix();
 
1727
        glTranslatef (a[2].x - (side == LEFT ? soffx : -soffx), 
 
1728
                      a[2].y + soffy, 
 
1729
                      0);
 
1730
        glScalef(9, 9, 9);
 
1731
        polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
 
1732
        glPopMatrix();
 
1733
 
 
1734
        /* Elbow */
 
1735
        glPushMatrix();
 
1736
        glTranslatef (a[1].x, a[1].y, ARMLENGTH/2);
 
1737
        glScalef(4, 4, 4);
 
1738
        polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
 
1739
        glPopMatrix();
 
1740
 
 
1741
        /* Hand */
 
1742
        glPushMatrix();
 
1743
        glTranslatef (a[0].x, a[0].y, ARMLENGTH);
 
1744
        glScalef(8, 8, 8);
 
1745
        polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
 
1746
        glPopMatrix();
 
1747
 
 
1748
  }
 
1749
  return polys;
 
1750
}
 
1751
 
 
1752
static int
 
1753
show_figure(ModeInfo * mi, Bool init)
 
1754
{
 
1755
  int polys = 0;
 
1756
  jugglestruct *sp = &juggles[MI_SCREEN(mi)];
 
1757
  /*XPoint p[7];*/
 
1758
  int i;
 
1759
 
 
1760
  /*      +-----+ 9
 
1761
          |  6  |
 
1762
       10 +--+--+
 
1763
       2 +---+---+ 3
 
1764
          \  5  /
 
1765
           \   /
 
1766
            \ /
 
1767
           1 +
 
1768
            / \
 
1769
           /   \
 
1770
        0 +-----+ 4
 
1771
          |     |
 
1772
          |     |
 
1773
          |     |
 
1774
        7 +     + 8
 
1775
  */
 
1776
 
 
1777
  /* #### most of this is unused now */
 
1778
  static const XPoint figure[] = {
 
1779
        { 15,  70}, /* 0  Left Hip */
 
1780
        {  0,  90}, /* 1  Waist */
 
1781
        { SX, 130}, /* 2  Left Shoulder */
 
1782
        {-SX, 130}, /* 3  Right Shoulder */
 
1783
        {-15,  70}, /* 4  Right Hip */
 
1784
        {  0, 130}, /* 5  Neck */
 
1785
        {  0, 140}, /* 6  Chin */
 
1786
        { SX,   0}, /* 7  Left Foot */
 
1787
        {-SX,   0}, /* 8  Right Foot */
 
1788
        {-17, 174}, /* 9  Head1 */
 
1789
        { 17, 140}, /* 10 Head2 */
 
1790
  };
 
1791
  XPoint a[countof(figure)];
 
1792
  GLfloat gcolor[4] = { 1, 1, 1, 1 };
 
1793
 
 
1794
  /* Translate into device coords */
 
1795
  for(i = 0; i < countof(figure); i++) {
 
1796
        a[i].x = (short)(SCENE_WIDTH/2 + (sp->cx + figure[i].x)*sp->scale);
 
1797
        a[i].y = (short)(SCENE_HEIGHT - figure[i].y*sp->scale);
 
1798
  }
 
1799
 
 
1800
  glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor);
 
1801
 
 
1802
  {
 
1803
    GLfloat scale = ((GLfloat) a[10].x - a[9].x) / 2;
 
1804
    int slices = 12;
 
1805
 
 
1806
    glPushMatrix();
 
1807
    {
 
1808
      glTranslatef(a[6].x, a[6].y - scale, 0);
 
1809
      glScalef(scale, scale, scale);
 
1810
 
 
1811
      /* Head */
 
1812
      glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
 
1813
      glPushMatrix();
 
1814
      scale = 0.75;
 
1815
      glScalef(scale, scale, scale);
 
1816
      glTranslatef(0, 0.3, 0);
 
1817
      glPushMatrix();
 
1818
      glTranslatef(0, 0, 0.35);
 
1819
      polys += tube (0, 0, 0,
 
1820
                     0, 1.1, 0,
 
1821
                     0.64, 0,
 
1822
                     slices, True, True, MI_IS_WIREFRAME(mi));
 
1823
      glPopMatrix();
 
1824
      glScalef(0.9, 0.9, 1);
 
1825
      polys += unit_sphere(2*slices, 2*slices, MI_IS_WIREFRAME(mi));
 
1826
      glPopMatrix();
 
1827
 
 
1828
      /* Neck */
 
1829
      glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
 
1830
      glTranslatef(0, 1.1, 0);
 
1831
      glPushMatrix();
 
1832
      scale = 0.35;
 
1833
      glScalef(scale, scale, scale);
 
1834
      polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
 
1835
      glPopMatrix();
 
1836
 
 
1837
      /* Torso */
 
1838
      glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
 
1839
      glTranslatef(0, 1.1, 0);
 
1840
      glPushMatrix();
 
1841
      glScalef(0.9, 1.0, 0.9);
 
1842
      polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
 
1843
      glPopMatrix();
 
1844
 
 
1845
      /* Belly */
 
1846
      glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
 
1847
      glTranslatef(0, 1.0, 0);
 
1848
      glPushMatrix();
 
1849
      scale = 0.6;
 
1850
      glScalef(scale, scale, scale);
 
1851
      polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
 
1852
      glPopMatrix();
 
1853
 
 
1854
      /* Hips */
 
1855
      glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
 
1856
      glTranslatef(0, 0.8, 0);
 
1857
      glPushMatrix();
 
1858
      scale = 0.85;
 
1859
      glScalef(scale, scale, scale);
 
1860
      polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
 
1861
      glPopMatrix();
 
1862
 
 
1863
 
 
1864
      /* Legs */
 
1865
      glTranslatef(0, 0.7, 0);
 
1866
 
 
1867
      for (i = -1; i <= 1; i += 2) {
 
1868
        glPushMatrix();
 
1869
 
 
1870
        glRotatef (i*10, 0, 0, 1);
 
1871
        glTranslatef(-i*0.65, 0, 0);
 
1872
        
 
1873
        /* Hip socket */
 
1874
        glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
 
1875
        scale = 0.45;
 
1876
        glScalef(scale, scale, scale);
 
1877
        polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
 
1878
 
 
1879
        /* Thigh */
 
1880
        glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
 
1881
        glPushMatrix();
 
1882
        glTranslatef(0, 0.6, 0);
 
1883
        polys += tube (0, 0, 0,
 
1884
                       0, 3.5, 0,
 
1885
                       1, 0,
 
1886
                       slices, True, True, MI_IS_WIREFRAME(mi));
 
1887
        glPopMatrix();
 
1888
 
 
1889
        /* Knee */
 
1890
        glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
 
1891
        glPushMatrix();
 
1892
        glTranslatef(0, 4.4, 0);
 
1893
        scale = 0.7;
 
1894
        glScalef(scale, scale, scale);
 
1895
        polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
 
1896
        glPopMatrix();
 
1897
 
 
1898
        /* Calf */
 
1899
        glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
 
1900
        glPushMatrix();
 
1901
        glTranslatef(0, 4.7, 0);
 
1902
        polys += tube (0, 0, 0,
 
1903
                       0, 4.7, 0,
 
1904
                       0.8, 0,
 
1905
                       slices, True, True, MI_IS_WIREFRAME(mi));
 
1906
        glPopMatrix();
 
1907
 
 
1908
        /* Ankle */
 
1909
        glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_2);
 
1910
        glPushMatrix();
 
1911
        glTranslatef(0, 9.7, 0);
 
1912
        scale = 0.5;
 
1913
        glScalef(scale, scale, scale);
 
1914
        polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
 
1915
        glPopMatrix();
 
1916
 
 
1917
        /* Foot */
 
1918
        glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, body_color_1);
 
1919
        glPushMatrix();
 
1920
        glRotatef (-i*10, 0, 0, 1);
 
1921
        glTranslatef(-i*1.75, 9.7, 0.9);
 
1922
 
 
1923
        glScalef (0.4, 1, 1);
 
1924
        polys += tube (0, 0, 0,
 
1925
                       0, 0.6, 0,
 
1926
                       1.9, 0,
 
1927
                       slices*4, True, True, MI_IS_WIREFRAME(mi));
 
1928
        glPopMatrix();
 
1929
 
 
1930
        glPopMatrix();
 
1931
      }
 
1932
    }
 
1933
    glPopMatrix();
 
1934
  }
 
1935
 
 
1936
  sp->arm[1][LEFT][SHOULDER].x = sp->cx + figure[2].x;
 
1937
  sp->arm[1][RIGHT][SHOULDER].x = sp->cx + figure[3].x;
 
1938
  if(init) {
 
1939
        /* Initialise arms */
 
1940
        unsigned int i;
 
1941
        for(i = 0; i < 2; i++){
 
1942
          sp->arm[i][LEFT][SHOULDER].y = figure[2].y;
 
1943
          sp->arm[i][LEFT][ELBOW].x = figure[2].x;
 
1944
          sp->arm[i][LEFT][ELBOW].y = figure[1].y;
 
1945
          sp->arm[i][LEFT][HAND].x = figure[0].x;
 
1946
          sp->arm[i][LEFT][HAND].y = figure[1].y;
 
1947
          sp->arm[i][RIGHT][SHOULDER].y = figure[3].y;
 
1948
          sp->arm[i][RIGHT][ELBOW].x = figure[3].x;
 
1949
          sp->arm[i][RIGHT][ELBOW].y = figure[1].y;
 
1950
          sp->arm[i][RIGHT][HAND].x = figure[4].x;
 
1951
          sp->arm[i][RIGHT][HAND].y = figure[1].y;
 
1952
        }
 
1953
  }
 
1954
  return polys;
 
1955
}
 
1956
 
 
1957
typedef struct { GLfloat x, y, z; } XYZ;
 
1958
 
 
1959
/* lifted from sphere.c */
 
1960
static int
 
1961
striped_unit_sphere (int stacks, int slices, 
 
1962
                     int stripes, 
 
1963
                     GLfloat *color1, GLfloat *color2,
 
1964
                     int wire_p)
 
1965
{
 
1966
  int polys = 0;
 
1967
  int i,j;
 
1968
  double theta1, theta2, theta3;
 
1969
  XYZ e, p;
 
1970
  XYZ la = { 0, 0, 0 }, lb = { 0, 0, 0 };
 
1971
  XYZ c = {0, 0, 0};  /* center */
 
1972
  double r = 1.0;     /* radius */
 
1973
  int stacks2 = stacks * 2;
 
1974
 
 
1975
  if (r < 0)
 
1976
    r = -r;
 
1977
  if (slices < 0)
 
1978
    slices = -slices;
 
1979
 
 
1980
  if (slices < 4 || stacks < 2 || r <= 0)
 
1981
    {
 
1982
      glBegin (GL_POINTS);
 
1983
      glVertex3f (c.x, c.y, c.z);
 
1984
      glEnd();
 
1985
      return 1;
 
1986
    }
 
1987
 
 
1988
  glFrontFace(GL_CW);
 
1989
 
 
1990
  for (j = 0; j < stacks; j++)
 
1991
    {
 
1992
      theta1 = j       * (M_PI+M_PI) / stacks2 - M_PI_2;
 
1993
      theta2 = (j + 1) * (M_PI+M_PI) / stacks2 - M_PI_2;
 
1994
 
 
1995
      glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
 
1996
                    ((j == 0 || j == stacks-1 ||
 
1997
                     j % (stacks / (stripes+1)))
 
1998
                     ? color1 : color2));
 
1999
 
 
2000
      glBegin (wire_p ? GL_LINE_LOOP : GL_TRIANGLE_STRIP);
 
2001
      for (i = 0; i <= slices; i++)
 
2002
        {
 
2003
          theta3 = i * (M_PI+M_PI) / slices;
 
2004
 
 
2005
          if (wire_p && i != 0)
 
2006
            {
 
2007
              glVertex3f (lb.x, lb.y, lb.z);
 
2008
              glVertex3f (la.x, la.y, la.z);
 
2009
            }
 
2010
 
 
2011
          e.x = cos (theta2) * cos(theta3);
 
2012
          e.y = sin (theta2);
 
2013
          e.z = cos (theta2) * sin(theta3);
 
2014
          p.x = c.x + r * e.x;
 
2015
          p.y = c.y + r * e.y;
 
2016
          p.z = c.z + r * e.z;
 
2017
 
 
2018
          glNormal3f (e.x, e.y, e.z);
 
2019
          glTexCoord2f (i       / (double)slices,
 
2020
                        2*(j+1) / (double)stacks2);
 
2021
          glVertex3f (p.x, p.y, p.z);
 
2022
          if (wire_p) la = p;
 
2023
 
 
2024
          e.x = cos(theta1) * cos(theta3);
 
2025
          e.y = sin(theta1);
 
2026
          e.z = cos(theta1) * sin(theta3);
 
2027
          p.x = c.x + r * e.x;
 
2028
          p.y = c.y + r * e.y;
 
2029
          p.z = c.z + r * e.z;
 
2030
 
 
2031
          glNormal3f (e.x, e.y, e.z);
 
2032
          glTexCoord2f (i   / (double)slices,
 
2033
                        2*j / (double)stacks2);
 
2034
          glVertex3f (p.x, p.y, p.z);
 
2035
          if (wire_p) lb = p;
 
2036
          polys++;
 
2037
        }
 
2038
      glEnd();
 
2039
    }
 
2040
  return polys;
 
2041
}
 
2042
 
 
2043
 
 
2044
 
 
2045
static int
 
2046
show_ball(ModeInfo *mi, unsigned long color, Trace *s)
 
2047
{
 
2048
  int polys = 0;
 
2049
  jugglestruct *sp = &juggles[MI_SCREEN(mi)];
 
2050
  /*int offset = (int)(s->angle*64*180/M_PI);*/
 
2051
  short x = (short)(SCENE_WIDTH/2 + s->x * sp->scale);
 
2052
  short y = (short)(SCENE_HEIGHT - s->y * sp->scale);
 
2053
  GLfloat gcolor1[4] = { 0, 0, 0, 1 };
 
2054
  GLfloat gcolor2[4] = { 0, 0, 0, 1 };
 
2055
  int slices = 24;
 
2056
 
 
2057
  /* Avoid wrapping */
 
2058
  if(s->y*sp->scale >  SCENE_HEIGHT * 2) return 0;
 
2059
 
 
2060
  gcolor1[0] = mi->colors[color].red   / 65536.0;
 
2061
  gcolor1[1] = mi->colors[color].green / 65536.0;
 
2062
  gcolor1[2] = mi->colors[color].blue  / 65536.0;
 
2063
 
 
2064
  gcolor2[0] = gcolor1[0] / 3;
 
2065
  gcolor2[1] = gcolor1[1] / 3;
 
2066
  gcolor2[2] = gcolor1[2] / 3;
 
2067
 
 
2068
  {
 
2069
    GLfloat scale = BALLRADIUS;
 
2070
    glPushMatrix();
 
2071
    glTranslatef(x, y, 0);
 
2072
    glScalef(scale, scale, scale);
 
2073
 
 
2074
    glRotatef (s->angle / M_PI*180, 1, 1, 0);
 
2075
 
 
2076
    polys += striped_unit_sphere (slices, slices, s->divisions, 
 
2077
                                  gcolor1, gcolor2, MI_IS_WIREFRAME(mi));
 
2078
    glPopMatrix();
 
2079
  }
 
2080
  return polys;
 
2081
}
 
2082
 
 
2083
static int
 
2084
show_europeanclub(ModeInfo *mi, unsigned long color, Trace *s)
 
2085
{
 
2086
  int polys = 0;
 
2087
  jugglestruct *sp = &juggles[MI_SCREEN(mi)];
 
2088
  /*int offset = (int)(s->angle*64*180/M_PI);*/
 
2089
  short x = (short)(SCENE_WIDTH/2 + s->x * sp->scale);
 
2090
  short y = (short)(SCENE_HEIGHT - s->y * sp->scale);
 
2091
  double radius = 12 * sp->scale;
 
2092
  GLfloat gcolor1[4] = { 0, 0, 0, 1 };
 
2093
  GLfloat gcolor2[4] = { 1, 1, 1, 1 };
 
2094
  int slices = 16;
 
2095
  int divs = s->divisions;
 
2096
  divs = 4;
 
2097
 
 
2098
  /*    6   6
 
2099
         +-+
 
2100
        /   \
 
2101
     4 +-----+ 7
 
2102
      ////////\
 
2103
   3 +---------+ 8
 
2104
   2 +---------+ 9
 
2105
      |///////|
 
2106
    1 +-------+ 10
 
2107
       |     |
 
2108
       |     |
 
2109
        |   |
 
2110
        |   |
 
2111
         | |
 
2112
         | |
 
2113
         +-+
 
2114
        0  11   */
 
2115
 
 
2116
  /* Avoid wrapping */
 
2117
  if(s->y*sp->scale >  SCENE_HEIGHT * 2) return 0;
 
2118
 
 
2119
  gcolor1[0] = mi->colors[color].red   / 65536.0;
 
2120
  gcolor1[1] = mi->colors[color].green / 65536.0;
 
2121
  gcolor1[2] = mi->colors[color].blue  / 65536.0;
 
2122
 
 
2123
  {
 
2124
    GLfloat scale = radius;
 
2125
    glPushMatrix();
 
2126
    glTranslatef(x, y, 0);
 
2127
    glScalef(scale, scale, scale);
 
2128
 
 
2129
    glTranslatef (0, 0, 2);  /* put end of handle in hand */
 
2130
 
 
2131
    glRotatef (s->angle / M_PI*180, 1, 0, 0);
 
2132
 
 
2133
    glPushMatrix();
 
2134
    glScalef (0.5, 1, 0.5);
 
2135
    polys += striped_unit_sphere (slices, slices, divs, gcolor2, gcolor1, 
 
2136
                                  MI_IS_WIREFRAME(mi));
 
2137
    glPopMatrix();
 
2138
    glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor2);
 
2139
    polys += tube (0, 0, 0,
 
2140
                   0, 2, 0,
 
2141
                   0.2, 0,
 
2142
                   slices, True, True, MI_IS_WIREFRAME(mi));
 
2143
 
 
2144
    glTranslatef (0, 2, 0);
 
2145
    glScalef (0.25, 0.25, 0.25);
 
2146
    polys += unit_sphere(slices, slices, MI_IS_WIREFRAME(mi));
 
2147
 
 
2148
    glPopMatrix();
 
2149
  }
 
2150
  return polys;
 
2151
}
 
2152
 
 
2153
 
 
2154
static int
 
2155
show_torch(ModeInfo *mi, unsigned long color, Trace *s)
 
2156
{
 
2157
  int polys = 0;
 
2158
#if 0
 
2159
        jugglestruct *sp = &juggles[MI_SCREEN(mi)];
 
2160
        XPoint head, tail, last;
 
2161
        DXPoint dhead, dlast;
 
2162
        const double sa = sin(s->angle);
 
2163
        const double ca = cos(s->angle);
 
2164
 
 
2165
        const double TailLen = -24;
 
2166
        const double HeadLen = 16;
 
2167
        const short Width   = (short)(5 * sqrt(sp->scale));
 
2168
 
 
2169
        /*
 
2170
      +///+ head
 
2171
    last  |
 
2172
          |
 
2173
          |
 
2174
          |
 
2175
          |
 
2176
          + tail
 
2177
        */
 
2178
 
 
2179
        dhead.x = s->x + HeadLen * PERSPEC * sa;
 
2180
        dhead.y = s->y - HeadLen * ca;
 
2181
 
 
2182
        if(color == MI_BLACK_PIXEL(mi)) { /* Use 'last' when erasing */
 
2183
          dlast = s->dlast;
 
2184
        } else { /* Store 'last' so we can use it later when s->prev has
 
2185
                                gone */
 
2186
          if(s->prev != s->next) {
 
2187
                dlast.x = s->prev->x + HeadLen * PERSPEC * sin(s->prev->angle);
 
2188
                dlast.y = s->prev->y - HeadLen * cos(s->prev->angle);
 
2189
          } else {
 
2190
                dlast = dhead;
 
2191
          }
 
2192
          s->dlast = dlast;
 
2193
        }
 
2194
 
 
2195
        /* Avoid wrapping (after last is stored) */
 
2196
        if(s->y*sp->scale >  SCENE_HEIGHT * 2) return 0;
 
2197
 
 
2198
        head.x = (short)(SCENE_WIDTH/2 + dhead.x*sp->scale);
 
2199
        head.y = (short)(SCENE_HEIGHT - dhead.y*sp->scale);
 
2200
 
 
2201
        last.x = (short)(SCENE_WIDTH/2 + dlast.x*sp->scale);
 
2202
        last.y = (short)(SCENE_HEIGHT - dlast.y*sp->scale);
 
2203
 
 
2204
        tail.x = (short)(SCENE_WIDTH/2 +
 
2205
                                         (s->x + TailLen * PERSPEC * sa)*sp->scale );
 
2206
        tail.y = (short)(SCENE_HEIGHT - (s->y - TailLen * ca)*sp->scale );
 
2207
 
 
2208
        if(color != MI_BLACK_PIXEL(mi)) {
 
2209
          XSetForeground(MI_DISPLAY(mi), MI_GC(mi), MI_BLACK_PIXEL(mi));
 
2210
          XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi),
 
2211
                                                 Width, LineSolid, CapRound, JoinRound);
 
2212
          draw_line(mi, head.x, head.y, tail.x, tail.y);
 
2213
        }
 
2214
        XSetForeground(MI_DISPLAY(mi), MI_GC(mi), color);
 
2215
        XSetLineAttributes(MI_DISPLAY(mi), MI_GC(mi),
 
2216
                                           Width * 2, LineSolid, CapRound, JoinRound);
 
2217
 
 
2218
        draw_line(mi, head.x, head.y, last.x, last.y);
 
2219
 
 
2220
#endif /* 0 */
 
2221
   return polys;
 
2222
}
 
2223
 
 
2224
 
 
2225
static int
 
2226
show_knife(ModeInfo *mi, unsigned long color, Trace *s)
 
2227
{
 
2228
  int polys = 0;
 
2229
  jugglestruct *sp = &juggles[MI_SCREEN(mi)];
 
2230
  /*int offset = (int)(s->angle*64*180/M_PI);*/
 
2231
  short x = (short)(SCENE_WIDTH/2 + s->x * sp->scale);
 
2232
  short y = (short)(SCENE_HEIGHT - s->y * sp->scale);
 
2233
  GLfloat gcolor1[4] = { 0, 0, 0, 1 };
 
2234
  GLfloat gcolor2[4] = { 1, 1, 1, 1 };
 
2235
  int slices = 8;
 
2236
 
 
2237
  /* Avoid wrapping */
 
2238
  if(s->y*sp->scale >  SCENE_HEIGHT * 2) return 0;
 
2239
 
 
2240
  gcolor1[0] = mi->colors[color].red   / 65536.0;
 
2241
  gcolor1[1] = mi->colors[color].green / 65536.0;
 
2242
  gcolor1[2] = mi->colors[color].blue  / 65536.0;
 
2243
 
 
2244
  glPushMatrix();
 
2245
  glTranslatef(x, y, 0);
 
2246
  glScalef (2, 2, 2);
 
2247
 
 
2248
  glTranslatef (0, 0, 2);  /* put end of handle in hand */
 
2249
  glRotatef (s->angle / M_PI*180, 1, 0, 0);
 
2250
 
 
2251
  glScalef (0.3, 1, 1);  /* flatten blade */
 
2252
 
 
2253
  glTranslatef(0, 6, 0);
 
2254
  glRotatef (180, 1, 0, 0);
 
2255
 
 
2256
  glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor1);
 
2257
  polys += tube (0, 0, 0,
 
2258
                 0, 10, 0,
 
2259
                 1, 0,
 
2260
                 slices, True, True, MI_IS_WIREFRAME(mi));
 
2261
 
 
2262
  glTranslatef (0, 12, 0);
 
2263
  glScalef (0.7, 10, 0.7);
 
2264
  glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor2);
 
2265
  polys += unit_sphere (slices, slices, MI_IS_WIREFRAME(mi));
 
2266
 
 
2267
  glPopMatrix();
 
2268
  return polys;
 
2269
}
 
2270
 
 
2271
 
 
2272
static int
 
2273
show_ring(ModeInfo *mi, unsigned long color, Trace *s)
 
2274
{
 
2275
  int polys = 0;
 
2276
  jugglestruct *sp = &juggles[MI_SCREEN(mi)];
 
2277
  /*int offset = (int)(s->angle*64*180/M_PI);*/
 
2278
  short x = (short)(SCENE_WIDTH/2 + s->x * sp->scale);
 
2279
  short y = (short)(SCENE_HEIGHT - s->y * sp->scale);
 
2280
  double radius = 12 * sp->scale;
 
2281
  GLfloat gcolor1[4] = { 0, 0, 0, 1 };
 
2282
  GLfloat gcolor2[4] = { 0, 0, 0, 1 };
 
2283
  int slices = 24;
 
2284
  int i, j;
 
2285
  int wire_p = MI_IS_WIREFRAME(mi);
 
2286
  GLfloat width = M_PI * 2 / slices;
 
2287
  GLfloat ra = 1.0;
 
2288
  GLfloat rb = 0.7;
 
2289
  GLfloat thickness = 0.15;
 
2290
 
 
2291
  /* Avoid wrapping */
 
2292
  if(s->y*sp->scale >  SCENE_HEIGHT * 2) return 0;
 
2293
 
 
2294
  gcolor1[0] = mi->colors[color].red   / 65536.0;
 
2295
  gcolor1[1] = mi->colors[color].green / 65536.0;
 
2296
  gcolor1[2] = mi->colors[color].blue  / 65536.0;
 
2297
 
 
2298
  gcolor2[0] = gcolor1[0] / 3;
 
2299
  gcolor2[1] = gcolor1[1] / 3;
 
2300
  gcolor2[2] = gcolor1[2] / 3;
 
2301
 
 
2302
  glPushMatrix();
 
2303
  glTranslatef(0, 0, 12);  /* back of ring in hand */
 
2304
 
 
2305
  glTranslatef(x, y, 0);
 
2306
  glScalef(radius, radius, radius);
 
2307
 
 
2308
  glRotatef (90, 0, 1, 0);
 
2309
  glRotatef (s->angle / M_PI*180, 0, 0, 1);
 
2310
 
 
2311
  glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor1);
 
2312
 
 
2313
  /* discs */
 
2314
  for (j = -1; j <= 1; j += 2)
 
2315
    {
 
2316
      GLfloat z = j * thickness/2;
 
2317
      glFrontFace (j < 0 ? GL_CCW : GL_CW);
 
2318
      glNormal3f (0, 0, j*1);
 
2319
      glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
 
2320
      for (i = 0; i < slices + (wire_p ? 0 : 1); i++) {
 
2321
        GLfloat th, cth, sth;
 
2322
        glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
 
2323
                      (i % (slices/3) ? gcolor1 : gcolor2));
 
2324
        th = i * width;
 
2325
        cth = cos(th);
 
2326
        sth = sin(th);
 
2327
        glVertex3f (cth * ra, sth * ra, z);
 
2328
        glVertex3f (cth * rb, sth * rb, z);
 
2329
        polys++;
 
2330
      }
 
2331
      glEnd();
 
2332
    }
 
2333
 
 
2334
  /* outer ring */
 
2335
  glFrontFace (GL_CCW);
 
2336
  glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
 
2337
  for (i = 0; i < slices + (wire_p ? 0 : 1); i++)
 
2338
    {
 
2339
      GLfloat th = i * width;
 
2340
      GLfloat cth = cos(th);
 
2341
      GLfloat sth = sin(th);
 
2342
      glNormal3f (cth, sth, 0);
 
2343
      glVertex3f (cth * ra, sth * ra, thickness/2);
 
2344
      glVertex3f (cth * ra, sth * ra, -thickness/2);
 
2345
      polys++;
 
2346
    }
 
2347
  glEnd();
 
2348
 
 
2349
  /* inner ring */
 
2350
  glFrontFace (GL_CW);
 
2351
  glBegin (wire_p ? GL_LINES : GL_QUAD_STRIP);
 
2352
  for (i = 0; i < slices + (wire_p ? 0 : 1); i++)
 
2353
    {
 
2354
      GLfloat th = i * width;
 
2355
      GLfloat cth = cos(th);
 
2356
      GLfloat sth = sin(th);
 
2357
      glNormal3f (-cth, -sth, 0);
 
2358
      glVertex3f (cth * rb, sth * ra, thickness/2);
 
2359
      glVertex3f (cth * rb, sth * ra, -thickness/2);
 
2360
      polys++;
 
2361
    }
 
2362
  glEnd();
 
2363
 
 
2364
  glFrontFace (GL_CCW);
 
2365
  glPopMatrix();
 
2366
  return polys;
 
2367
}
 
2368
 
 
2369
 
 
2370
static int
 
2371
show_bball(ModeInfo *mi, unsigned long color, Trace *s)
 
2372
{
 
2373
  int polys = 0;
 
2374
  jugglestruct *sp = &juggles[MI_SCREEN(mi)];
 
2375
  /*int offset = (int)(s->angle*64*180/M_PI);*/
 
2376
  short x = (short)(SCENE_WIDTH/2 + s->x * sp->scale);
 
2377
  short y = (short)(SCENE_HEIGHT - s->y * sp->scale);
 
2378
  double radius = 12 * sp->scale;
 
2379
  GLfloat gcolor1[4] = { 0, 0, 0, 1 };
 
2380
  GLfloat gcolor2[4] = { 0, 0, 0, 1 };
 
2381
  int slices = 16;
 
2382
  int i, j;
 
2383
 
 
2384
  /* Avoid wrapping */
 
2385
  if(s->y*sp->scale >  SCENE_HEIGHT * 2) return 0;
 
2386
 
 
2387
  gcolor1[0] = mi->colors[color].red   / 65536.0;
 
2388
  gcolor1[1] = mi->colors[color].green / 65536.0;
 
2389
  gcolor1[2] = mi->colors[color].blue  / 65536.0;
 
2390
 
 
2391
  {
 
2392
    GLfloat scale = radius;
 
2393
    glPushMatrix();
 
2394
 
 
2395
    glTranslatef(0, -6, 5);  /* position on top of hand */
 
2396
 
 
2397
    glTranslatef(x, y, 0);
 
2398
    glScalef(scale, scale, scale);
 
2399
    glRotatef (s->angle / M_PI*180, 1, 0, 1);
 
2400
 
 
2401
    glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor1);
 
2402
    polys += unit_sphere (slices, slices, MI_IS_WIREFRAME(mi));
 
2403
 
 
2404
    glRotatef (90, 0, 0, 1);
 
2405
    glTranslatef (0, 0, 0.81);
 
2406
    glScalef(0.15, 0.15, 0.15);
 
2407
    glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gcolor2);
 
2408
    for (i = 0; i < 3; i++) {
 
2409
      glPushMatrix();
 
2410
      glTranslatef (0, 0, 1);
 
2411
      glRotatef (360 * i / 3, 0, 0, 1);
 
2412
      glTranslatef (2, 0, 0);
 
2413
      glRotatef (18, 0, 1, 0);
 
2414
      glBegin (MI_IS_WIREFRAME(mi) ? GL_LINE_LOOP : GL_TRIANGLE_FAN);
 
2415
      glVertex3f (0, 0, 0);
 
2416
      for (j = slices; j >= 0; j--) {
 
2417
        GLfloat th = j * M_PI*2 / slices;
 
2418
        glVertex3f (cos(th), sin(th), 0);
 
2419
        polys++;
 
2420
      }
 
2421
      glEnd();
 
2422
      glPopMatrix();
 
2423
    }
 
2424
 
 
2425
    glPopMatrix();
 
2426
  }
 
2427
  return polys;
 
2428
}
 
2429
 
 
2430
 
 
2431
/**************************************************************************
 
2432
 *                    Public Functions                                    *
 
2433
 *                                                                        *
 
2434
 **************************************************************************/
 
2435
 
 
2436
 
 
2437
ENTRYPOINT void
 
2438
release_juggle (ModeInfo * mi)
 
2439
{
 
2440
  if (juggles != NULL) {
 
2441
        int screen;
 
2442
 
 
2443
        for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++)
 
2444
          free_juggle(&juggles[screen]);
 
2445
        free(juggles);
 
2446
        juggles = (jugglestruct *) NULL;
 
2447
  }
 
2448
}
 
2449
 
 
2450
/* FIXME: refill_juggle currently just appends new throws to the
 
2451
 * programme.  This is fine if the programme is empty, but if there
 
2452
 * are still some trajectories left then it really should take these
 
2453
 * into account */
 
2454
 
 
2455
static void
 
2456
refill_juggle(ModeInfo * mi)
 
2457
{
 
2458
  jugglestruct *sp = NULL;
 
2459
  int i;
 
2460
 
 
2461
  if (juggles == NULL)
 
2462
        return;
 
2463
  sp = &juggles[MI_SCREEN(mi)];
 
2464
 
 
2465
  /* generate pattern */
 
2466
 
 
2467
  if (pattern == NULL) {
 
2468
 
 
2469
#define MAXPAT 10
 
2470
#define MAXREPEAT 300
 
2471
#define CHANGE_BIAS 8 /* larger makes num_ball changes less likely */
 
2472
#define POSITION_BIAS 20 /* larger makes hand movements less likely */
 
2473
 
 
2474
        int count = 0;
 
2475
        while (count < MI_CYCLES(mi)) {
 
2476
          char buf[MAXPAT * 3 + 3], *b = buf;
 
2477
          int maxseen = 0;
 
2478
          int l = NRAND(MAXPAT) + 1;
 
2479
          int t = NRAND(MIN(MAXREPEAT, (MI_CYCLES(mi) - count))) + 1;
 
2480
 
 
2481
          { /* vary number of balls */
 
2482
                int new_balls = sp->num_balls;
 
2483
                int change;
 
2484
 
 
2485
                if (new_balls == 2) /* Do not juggle 2 that often */
 
2486
                  change = NRAND(2 + CHANGE_BIAS / 4);
 
2487
                else
 
2488
                  change = NRAND(2 + CHANGE_BIAS);
 
2489
                switch (change) {
 
2490
                case 0:
 
2491
                  new_balls++;
 
2492
                  break;
 
2493
                case 1:
 
2494
                  new_balls--;
 
2495
                  break;
 
2496
                default:
 
2497
                  break; /* NO-OP */
 
2498
                }
 
2499
                if (new_balls < sp->patternindex.minballs) {
 
2500
                  new_balls += 2;
 
2501
                }
 
2502
                if (new_balls > sp->patternindex.maxballs) {
 
2503
                  new_balls -= 2;
 
2504
                }
 
2505
                if (new_balls < sp->num_balls) {
 
2506
                  if (!program(mi, "[*]", NULL, 1)) /* lose ball */
 
2507
                        return;
 
2508
                }
 
2509
                sp->num_balls = new_balls;
 
2510
          }
 
2511
 
 
2512
          count += t;
 
2513
          if (NRAND(2) && sp->patternindex.index[sp->num_balls].number) {
 
2514
                /* Pick from PortFolio */
 
2515
                int p = sp->patternindex.index[sp->num_balls].start +
 
2516
                  NRAND(sp->patternindex.index[sp->num_balls].number);
 
2517
                if (!program(mi, portfolio[p].pattern, portfolio[p].name, t))
 
2518
                  return;
 
2519
          } else {
 
2520
                /* Invent a new pattern */
 
2521
                *b++='[';
 
2522
                for(i = 0; i < l; i++){
 
2523
                  int n, m;
 
2524
                  do { /* Triangular Distribution => high values more likely */
 
2525
                        m = NRAND(sp->num_balls + 1);
 
2526
                        n = NRAND(sp->num_balls + 1);
 
2527
                  } while(m >= n);
 
2528
                  if (n == sp->num_balls) {
 
2529
                        maxseen = 1;
 
2530
                  }
 
2531
                  switch(NRAND(5 + POSITION_BIAS)){
 
2532
                  case 0:            /* Outside throw */
 
2533
                        *b++ = '+'; break;
 
2534
                  case 1:            /* Cross throw */
 
2535
                        *b++ = '='; break;
 
2536
                  case 2:            /* Cross catch */
 
2537
                        *b++ = '&'; break;
 
2538
                  case 3:            /* Cross throw and catch */
 
2539
                        *b++ = 'x'; break;
 
2540
                  case 4:            /* Bounce */
 
2541
                        *b++ = '_'; break;
 
2542
                  default:
 
2543
                        break;             /* Inside throw (default) */
 
2544
                  }
 
2545
 
 
2546
                  *b++ = n + '0';
 
2547
                  *b++ = ' ';
 
2548
                }
 
2549
                *b++ = ']';
 
2550
                *b = '\0';
 
2551
                if (maxseen) {
 
2552
                  if (!program(mi, buf, NULL, t))
 
2553
                        return;
 
2554
                }
 
2555
          }
 
2556
        }
 
2557
  } else { /* pattern supplied in height or 'a' notation */
 
2558
        if (!program(mi, pattern, NULL, MI_CYCLES(mi)))
 
2559
          return;
 
2560
  }
 
2561
 
 
2562
  adam(sp);
 
2563
 
 
2564
  name(sp);
 
2565
 
 
2566
  if (!part(sp))
 
2567
        return;
 
2568
 
 
2569
  lob(mi);
 
2570
 
 
2571
  clap(sp);
 
2572
 
 
2573
  positions(sp);
 
2574
 
 
2575
  if (!projectile(sp)) {
 
2576
        free_juggle(sp);
 
2577
        return;
 
2578
  }
 
2579
 
 
2580
  hands(sp);
 
2581
#ifdef DEBUG
 
2582
  if(MI_IS_DEBUG(mi)) dump(sp);
 
2583
#endif
 
2584
}
 
2585
 
 
2586
static void
 
2587
change_juggle(ModeInfo * mi)
 
2588
{
 
2589
  jugglestruct *sp = NULL;
 
2590
  Trajectory *t;
 
2591
 
 
2592
  if (juggles == NULL)
 
2593
        return;
 
2594
  sp = &juggles[MI_SCREEN(mi)];
 
2595
 
 
2596
  /* Strip pending trajectories */
 
2597
  for (t = sp->head->next; t != sp->head; t = t->next) {
 
2598
        if(t->start > sp->time || t->finish < sp->time) {
 
2599
          Trajectory *n = t;
 
2600
          t=t->prev;
 
2601
          trajectory_destroy(n);
 
2602
        }
 
2603
  }
 
2604
 
 
2605
  /* Pick the current object theme */
 
2606
  sp->objtypes = choose_object();
 
2607
 
 
2608
  refill_juggle(mi);
 
2609
 
 
2610
  mi->polygon_count += show_figure(mi, True);
 
2611
}
 
2612
 
 
2613
 
 
2614
ENTRYPOINT void
 
2615
reshape_juggle (ModeInfo *mi, int width, int height)
 
2616
{
 
2617
  GLfloat h = (GLfloat) height / (GLfloat) width;
 
2618
 
 
2619
  glViewport (0, 0, (GLint) width, (GLint) height);
 
2620
 
 
2621
  glMatrixMode(GL_PROJECTION);
 
2622
  glLoadIdentity();
 
2623
  gluPerspective (30.0, 1/h, 1.0, 100.0);
 
2624
 
 
2625
  glMatrixMode(GL_MODELVIEW);
 
2626
  glLoadIdentity();
 
2627
  gluLookAt( 0.0, 0.0, 30.0,
 
2628
             0.0, 0.0, 0.0,
 
2629
             0.0, 1.0, 0.0);
 
2630
 
 
2631
  glClear(GL_COLOR_BUFFER_BIT);
 
2632
}
 
2633
 
 
2634
 
 
2635
ENTRYPOINT void
 
2636
init_juggle (ModeInfo * mi)
 
2637
{
 
2638
  jugglestruct *sp = 0;
 
2639
  int wire = MI_IS_WIREFRAME(mi);
 
2640
 
 
2641
  if (!juggles) {
 
2642
    juggles = (jugglestruct *)
 
2643
      calloc (MI_NUM_SCREENS(mi), sizeof (jugglestruct));
 
2644
    if (!juggles) {
 
2645
      fprintf(stderr, "%s: out of memory\n", progname);
 
2646
      exit(1);
 
2647
    }
 
2648
  }
 
2649
 
 
2650
  sp = &juggles[MI_SCREEN(mi)];
 
2651
 
 
2652
  sp->glx_context = init_GL(mi);
 
2653
 
 
2654
  load_font (mi->dpy, "titleFont",  &sp->mode_font, &sp->font_dlist);
 
2655
 
 
2656
  reshape_juggle (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
 
2657
 
 
2658
  if (!wire)
 
2659
    {
 
2660
      GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
 
2661
      GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
 
2662
      GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
 
2663
      GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
 
2664
 
 
2665
      glEnable(GL_LIGHTING);
 
2666
      glEnable(GL_LIGHT0);
 
2667
      glEnable(GL_DEPTH_TEST);
 
2668
      glEnable(GL_CULL_FACE);
 
2669
 
 
2670
      glLightfv(GL_LIGHT0, GL_POSITION, pos);
 
2671
      glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
 
2672
      glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
 
2673
      glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
 
2674
    }
 
2675
 
 
2676
  make_random_colormap (0, 0, 0,
 
2677
                        mi->colors, &MI_NPIXELS(mi),
 
2678
                        True, False, 0, False);
 
2679
 
 
2680
  {
 
2681
    double spin_speed   = 0.05;
 
2682
    double wander_speed = 0.001;
 
2683
    double spin_accel   = 0.05;
 
2684
    sp->rot = make_rotator (0, spin_speed, 0, 
 
2685
                            spin_accel, wander_speed, False);
 
2686
    sp->trackball = gltrackball_init ();
 
2687
  }
 
2688
 
 
2689
  if (only && *only && strcmp(only, " ")) {
 
2690
    balls = clubs = torches = knives = rings = bballs = False;
 
2691
    if (!strcasecmp (only, "balls"))   balls   = True;
 
2692
    else if (!strcasecmp (only, "clubs"))   clubs   = True;
 
2693
    else if (!strcasecmp (only, "torches")) torches = True;
 
2694
    else if (!strcasecmp (only, "knives"))  knives  = True;
 
2695
    else if (!strcasecmp (only, "rings"))   rings   = True;
 
2696
    else if (!strcasecmp (only, "bballs"))  bballs  = True;
 
2697
    else {
 
2698
      (void) fprintf (stderr,
 
2699
               "Juggle: -only must be one of: balls, clubs, torches, knives,\n"
 
2700
               "\t rings, or bballs (not \"%s\")\n", only);
 
2701
#ifdef STANDALONE /* xlock mustn't exit merely because of a bad argument */
 
2702
      exit (1);
 
2703
#endif
 
2704
    }
 
2705
  }
 
2706
 
 
2707
  /* #### hard to make this look good in OpenGL... */
 
2708
  torches = False;
 
2709
 
 
2710
 
 
2711
  if (sp->head == 0) {  /* first time initializing this juggler */
 
2712
 
 
2713
        sp->count = ABS(MI_COUNT(mi));
 
2714
        if (sp->count == 0)
 
2715
          sp->count = 200;
 
2716
 
 
2717
        /* record start time */
 
2718
        sp->begintime = time(NULL);
 
2719
        if(sp->patternindex.maxballs > 0) {
 
2720
          sp->num_balls = sp->patternindex.minballs +
 
2721
                NRAND(sp->patternindex.maxballs - sp->patternindex.minballs);
 
2722
        }
 
2723
 
 
2724
        mi->polygon_count +=
 
2725
          show_figure(mi, True); /* Draw figure.  Also discovers
 
2726
                                    information about the juggler's
 
2727
                                    proportions */
 
2728
 
 
2729
        /* "7" should be about three times the height of the juggler's
 
2730
           shoulders */
 
2731
        sp->Gr = -GRAVITY(3 * sp->arm[0][RIGHT][SHOULDER].y,
 
2732
                                          7 * THROW_CATCH_INTERVAL);
 
2733
 
 
2734
        if(!balls && !clubs && !torches && !knives && !rings && !bballs)
 
2735
          balls = True; /* Have to juggle something! */
 
2736
 
 
2737
        /* create circular trajectory list */
 
2738
        ADD_ELEMENT(Trajectory, sp->head, sp->head);
 
2739
        if(sp->head == NULL){
 
2740
          free_juggle(sp);
 
2741
          return;
 
2742
        }
 
2743
 
 
2744
        /* create circular object list */
 
2745
        ADD_ELEMENT(Object, sp->objects, sp->objects);
 
2746
        if(sp->objects == NULL){
 
2747
          free_juggle(sp);
 
2748
          return;
 
2749
        }
 
2750
 
 
2751
        sp->pattern =  strdup(""); /* Initialise saved pattern with 
 
2752
                                      free-able memory */
 
2753
  }
 
2754
 
 
2755
  sp = &juggles[MI_SCREEN(mi)];
 
2756
 
 
2757
  if (pattern &&
 
2758
      (!*pattern ||
 
2759
       !strcasecmp (pattern, ".") ||
 
2760
       !strcasecmp (pattern, "random")))
 
2761
        pattern = NULL;
 
2762
 
 
2763
  if (pattern == NULL && sp->patternindex.maxballs == 0) {
 
2764
        /* pattern list needs indexing */
 
2765
        int nelements = countof(portfolio);
 
2766
        int numpat = 0;
 
2767
        int i;
 
2768
 
 
2769
        /* sort according to number of balls */
 
2770
        qsort((void*)portfolio, nelements,
 
2771
                  sizeof(portfolio[1]), compare_num_balls);
 
2772
 
 
2773
        /* last pattern has most balls */
 
2774
        sp->patternindex.maxballs = get_num_balls(portfolio[nelements - 1].pattern);
 
2775
        /* run through sorted list, indexing start of each group
 
2776
           and number in group */
 
2777
        sp->patternindex.maxballs = 1;
 
2778
        for (i = 0; i < nelements; i++) {
 
2779
          int b = get_num_balls(portfolio[i].pattern);
 
2780
          if (b > sp->patternindex.maxballs) {
 
2781
                sp->patternindex.index[sp->patternindex.maxballs].number = numpat;
 
2782
                if(numpat == 0) sp->patternindex.minballs = b;
 
2783
                sp->patternindex.maxballs = b;
 
2784
                numpat = 1;
 
2785
                sp->patternindex.index[sp->patternindex.maxballs].start = i;
 
2786
          } else {
 
2787
                numpat++;
 
2788
          }
 
2789
        }
 
2790
        sp->patternindex.index[sp->patternindex.maxballs].number = numpat;
 
2791
  }
 
2792
 
 
2793
  /* Set up programme */
 
2794
  change_juggle(mi);
 
2795
 
 
2796
  /* Only put things here that won't interrupt the programme during
 
2797
         a window resize */
 
2798
 
 
2799
  /* Use MIN so that users can resize in interesting ways, eg
 
2800
         narrow windows for tall patterns, etc */
 
2801
  sp->scale = MIN(SCENE_HEIGHT/480.0, SCENE_WIDTH/160.0);
 
2802
 
 
2803
}
 
2804
 
 
2805
ENTRYPOINT Bool
 
2806
juggle_handle_event (ModeInfo *mi, XEvent *event)
 
2807
{
 
2808
  jugglestruct *sp = &juggles[MI_SCREEN(mi)];
 
2809
 
 
2810
  if (event->xany.type == ButtonPress &&
 
2811
      event->xbutton.button == Button1)
 
2812
    {
 
2813
      sp->button_down_p = True;
 
2814
      gltrackball_start (sp->trackball,
 
2815
                         event->xbutton.x, event->xbutton.y,
 
2816
                         MI_WIDTH (mi), MI_HEIGHT (mi));
 
2817
      return True;
 
2818
    }
 
2819
  else if (event->xany.type == ButtonRelease &&
 
2820
           event->xbutton.button == Button1)
 
2821
    {
 
2822
      sp->button_down_p = False;
 
2823
      return True;
 
2824
    }
 
2825
  else if (event->xany.type == ButtonPress &&
 
2826
           (event->xbutton.button == Button4 ||
 
2827
            event->xbutton.button == Button5 ||
 
2828
            event->xbutton.button == Button6 ||
 
2829
            event->xbutton.button == Button7))
 
2830
    {
 
2831
      gltrackball_mousewheel (sp->trackball, event->xbutton.button, 10,
 
2832
                              !!event->xbutton.state);
 
2833
      return True;
 
2834
    }
 
2835
  else if (event->xany.type == MotionNotify &&
 
2836
           sp->button_down_p)
 
2837
    {
 
2838
      gltrackball_track (sp->trackball,
 
2839
                         event->xmotion.x, event->xmotion.y,
 
2840
                         MI_WIDTH (mi), MI_HEIGHT (mi));
 
2841
      return True;
 
2842
    }
 
2843
  else if (event->xany.type == KeyPress)
 
2844
    {
 
2845
      KeySym keysym;
 
2846
      char c = 0;
 
2847
      XLookupString (&event->xkey, &c, 1, &keysym, 0);
 
2848
      if (c == ' ' || c == '\n' || c == '\r' || c == '\t')
 
2849
        {
 
2850
          change_juggle (mi);
 
2851
          return True;
 
2852
        }
 
2853
    }
 
2854
 
 
2855
 
 
2856
  return False;
 
2857
}
 
2858
 
 
2859
 
 
2860
ENTRYPOINT void
 
2861
draw_juggle (ModeInfo *mi)
 
2862
{
 
2863
  jugglestruct *sp = &juggles[MI_SCREEN(mi)];
 
2864
  Display *dpy = MI_DISPLAY(mi);
 
2865
  Window window = MI_WINDOW(mi);
 
2866
 
 
2867
  Trajectory *traj = NULL;
 
2868
  Object *o = NULL;
 
2869
  unsigned long future = 0;
 
2870
  char *pattern = NULL;
 
2871
 
 
2872
  if (!sp->glx_context)
 
2873
    return;
 
2874
 
 
2875
  glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(sp->glx_context));
 
2876
 
 
2877
  glShadeModel(GL_SMOOTH);
 
2878
 
 
2879
  glEnable(GL_DEPTH_TEST);
 
2880
  glEnable(GL_NORMALIZE);
 
2881
  glEnable(GL_CULL_FACE);
 
2882
 
 
2883
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
2884
 
 
2885
  glPushMatrix ();
 
2886
 
 
2887
  glTranslatef(0,-3,0);
 
2888
 
 
2889
  {
 
2890
    double x, y, z;
 
2891
    get_position (sp->rot, &x, &y, &z, !sp->button_down_p);
 
2892
    glTranslatef((x - 0.5) * 8,
 
2893
                 (y - 0.5) * 3,
 
2894
                 (z - 0.5) * 15);
 
2895
 
 
2896
    gltrackball_rotate (sp->trackball);
 
2897
 
 
2898
    get_rotation (sp->rot, &x, &y, &z, !sp->button_down_p);
 
2899
 
 
2900
    if (y < 0.8) y = 0.8 - (y - 0.8);   /* always face forward */
 
2901
    if (y > 1.2) y = 1.2 - (y - 1.2);
 
2902
 
 
2903
    glRotatef (x * 360, 1.0, 0.0, 0.0);
 
2904
    glRotatef (y * 360, 0.0, 1.0, 0.0);
 
2905
    glRotatef (z * 360, 0.0, 0.0, 1.0);
 
2906
  }
 
2907
 
 
2908
  {
 
2909
    GLfloat scale = 20.0 / SCENE_HEIGHT;
 
2910
    glScalef(scale, scale, scale);
 
2911
  }
 
2912
 
 
2913
  glRotatef (180, 0, 0, 1);
 
2914
  glTranslatef(-SCENE_WIDTH/2, -SCENE_HEIGHT/2, 0);
 
2915
  glTranslatef(0, -150, 0);
 
2916
 
 
2917
  mi->polygon_count = 0;
 
2918
 
 
2919
  /* Update timer */
 
2920
  if (real) {
 
2921
        struct timeval tv;
 
2922
        (void)gettimeofday(&tv, NULL);
 
2923
        sp->time = (int) ((tv.tv_sec - sp->begintime)*1000 + tv.tv_usec/1000);
 
2924
  } else {
 
2925
        sp->time += MI_DELAY(mi) / 1000;
 
2926
  }
 
2927
 
 
2928
  /* First pass: Move arms and strip out expired elements */
 
2929
  for (traj = sp->head->next; traj != sp->head; traj = traj->next) {
 
2930
        if (traj->status != PREDICTOR) {
 
2931
          /* Skip any elements that need further processing */
 
2932
          /* We could remove them, but there shoudn't be many and they
 
2933
                 would be needed if we ever got the pattern refiller
 
2934
                 working */
 
2935
          continue;
 
2936
        }
 
2937
        if (traj->start > future) { /* Lookahead to the end of the show */
 
2938
          future = traj->start;
 
2939
        }
 
2940
        if (sp->time < traj->start) { /* early */
 
2941
          continue;
 
2942
        } else if (sp->time < traj->finish) { /* working */
 
2943
 
 
2944
          /* Look for pattern name */
 
2945
          if(traj->pattern != NULL) {
 
2946
                pattern=traj->pattern;
 
2947
          }
 
2948
 
 
2949
          if (traj->type == Empty || traj->type == Full) {
 
2950
                /* Only interested in hands on this pass */
 
2951
/*              double angle = traj->angle + traj->spin * (sp->time - traj->start);*/
 
2952
                double xd = 0, yd = 0;
 
2953
                DXPoint p;
 
2954
 
 
2955
                /* Find the catching offset */
 
2956
                if(traj->object != NULL) {
 
2957
#if 0
 
2958
                  /* #### not sure what this is doing, but I'm guessing
 
2959
                     that the use of PERSPEC means this isn't needed
 
2960
                     in the OpenGL version? -jwz
 
2961
                   */
 
2962
                  if(ObjectDefs[traj->object->type].handle > 0) {
 
2963
                        /* Handles Need to be oriented */
 
2964
                        xd = ObjectDefs[traj->object->type].handle *
 
2965
                          PERSPEC * sin(angle);
 
2966
                        yd = ObjectDefs[traj->object->type].handle *
 
2967
                          cos(angle);
 
2968
                  } else
 
2969
#endif
 
2970
                    {
 
2971
                        /* Balls are always caught at the bottom */
 
2972
                        xd = 0;
 
2973
                        yd = -4;
 
2974
                  }
 
2975
                }
 
2976
                p.x = (CUBIC(traj->xp, sp->time) - xd);
 
2977
                p.y = (CUBIC(traj->yp, sp->time) + yd);
 
2978
                reach_arm(mi, traj->hand, &p);
 
2979
 
 
2980
                /* Store updated hand position */
 
2981
                traj->x = p.x + xd;
 
2982
                traj->y = p.y - yd;
 
2983
          }
 
2984
          if (traj->type == Ball || traj->type == Full) {
 
2985
                /* Only interested in objects on this pass */
 
2986
                double x, y;
 
2987
                Trace *s;
 
2988
 
 
2989
                if(traj->type == Full) {
 
2990
                  /* Adjusted these in the first pass */
 
2991
                  x = traj->x;
 
2992
                  y = traj->y;
 
2993
                } else {
 
2994
                  x = CUBIC(traj->xp, sp->time);
 
2995
                  y = CUBIC(traj->yp, sp->time);
 
2996
                }
 
2997
 
 
2998
                ADD_ELEMENT(Trace, s, traj->object->trace->prev);
 
2999
                s->x = x;
 
3000
                s->y = y;
 
3001
                s->angle = traj->angle + traj->spin * (sp->time - traj->start);
 
3002
                s->divisions = traj->divisions;
 
3003
                traj->object->tracelen++;
 
3004
                traj->object->active = True;
 
3005
          }
 
3006
        } else { /* expired */
 
3007
          Trajectory *n = traj;
 
3008
          traj=traj->prev;
 
3009
          trajectory_destroy(n);
 
3010
        }
 
3011
  }
 
3012
 
 
3013
 
 
3014
  mi->polygon_count += show_figure(mi, False);
 
3015
  mi->polygon_count += show_arms(mi);
 
3016
 
 
3017
  /* Draw Objects */
 
3018
  glTranslatef(0, 0, ARMLENGTH);
 
3019
  for (o = sp->objects->next; o != sp->objects; o = o->next) {
 
3020
        if(o->active) {
 
3021
          mi->polygon_count += ObjectDefs[o->type].draw(mi, o->color, 
 
3022
                                                        o->trace->prev);
 
3023
          o->active = False;
 
3024
        }
 
3025
  }
 
3026
 
 
3027
 
 
3028
  /* Save pattern name so we can erase it when it changes */
 
3029
  if(pattern != NULL && strcmp(sp->pattern, pattern) != 0 ) {
 
3030
        free(sp->pattern);
 
3031
        sp->pattern = strdup(pattern);
 
3032
 
 
3033
        if (MI_IS_VERBOSE(mi)) {
 
3034
          (void) fprintf(stderr, "Juggle[%d]: Running: %s\n",
 
3035
                                         MI_SCREEN(mi), sp->pattern);
 
3036
        }
 
3037
  }
 
3038
 
 
3039
  if(sp->mode_font != None) {
 
3040
    print_gl_string (mi->dpy, sp->mode_font, sp->font_dlist,
 
3041
                     mi->xgwa.width, mi->xgwa.height,
 
3042
                     10, mi->xgwa.height - 10,
 
3043
                     sp->pattern, False);
 
3044
  }
 
3045
 
 
3046
#ifdef MEMTEST
 
3047
  if((int)(sp->time/10) % 1000 == 0)
 
3048
        (void) fprintf(stderr, "sbrk: %d\n", (int)sbrk(0));
 
3049
#endif
 
3050
 
 
3051
  if (future < sp->time + 100 * THROW_CATCH_INTERVAL) {
 
3052
        refill_juggle(mi);
 
3053
  } else if (sp->time > 1<<30) { /* Hard Reset before the clock wraps */
 
3054
        release_juggle(mi);
 
3055
        init_juggle(mi);
 
3056
  }
 
3057
 
 
3058
  glPopMatrix ();
 
3059
 
 
3060
  if (mi->fps_p) do_fps (mi);
 
3061
  glFinish();
 
3062
 
 
3063
  glXSwapBuffers(dpy, window);
 
3064
}
 
3065
 
 
3066
XSCREENSAVER_MODULE_2 ("Juggler3D", juggler3d, juggle)
 
3067
 
 
3068
#endif /* USE_GL */