/* Author: Ross McNab, 1996 * Vanilla Site Swap Juggler, v1.5 * 16/1/96 - First translation into Java from C * 9/2/96 - Added smooth catching and improved behaviour of "1" throws */ import java.awt.*; import java.applet.*; class Pattern { int thrown[]; int length = 0; float balls; boolean islower(char c) { return((c >= 'a') && (c <= 'z')); } boolean isdigit(char c) { return((c >= '0') && (c <= '9')); } public void parse_string(String strng) { int i, total; char c[]; c = new char[strng.length()]; length = strng.length(); thrown = new int[length]; total = 0; strng.getChars(0, length, c, 0); for(i = 0; i < length; i++) { if(islower(c[i])) thrown[i] = (c[i] - 'a') + 9; else { if(isdigit(c[i])) thrown[i] = (c[i] - '0'); else { System.err.println("Error: unrecognised char in pattern string"); } } total += thrown[i]; } balls = total / i; } public void check_valid() { int i, j, land_time; /* check number of balls is a whole number */ if (balls != (float)((int)balls)) { System.err.println("Error: can't have " + balls + " balls!"); } /* check no two balls land at same time */ for(i = 0; i < length; i++) { land_time = i + thrown[i]; for(j = i + 1; j < length; j++) { if(land_time == (j + thrown[j])) { System.err.println("Error: two balls land at same time"); } } } } public int balls() { return((int)balls); } public int length() { return(length); } public int get_throw(int time_step) { return(thrown[time_step]); } } class Ball { float x; float re_throw_x; float y; float vx; float vy; float gravity; int throw_next; Color col; // Our colour table common to all balls static Color colours[] = { new java.awt.Color(255,0,0), new java.awt.Color(0,255,0), new java.awt.Color(0,0,255), new java.awt.Color(255,255,0), new java.awt.Color(0,255,255), new java.awt.Color(255,0,255) }; public Ball(int i) { throw_next = -1; gravity = 0; col = colours[i % 6]; x = -100; } // Draw a filled circle at (x,y) in colour c public void draw_ball(Graphics g, Color c) { int i, r=5, ny; int px; ny = (int)(4*y+190); for(i = 0; i <= r; i++) { px = (int)Math.sqrt(r*r - i*i); g.setColor(c); g.drawLine((int)(x+px), (ny +i), (int)(x-px), (ny +i)); g.drawLine((int)(x+px), (ny -i), (int)(x-px), (ny -i)); } } public void move_ball(float frames, int frame, Graphics g) { draw_ball(g, new java.awt.Color(192,192,192)); if(throw_next != -1) { x += vx / frames; y += vy / frames; vy += (gravity / frames); } draw_ball(g, col); } public void caught_ball(Graphics g) { if((throw_next == 0) && (re_throw_x != -1)) { draw_ball(g, new java.awt.Color(192,192,192)); y = 0; vx = (float)((re_throw_x - x)/0.5); gravity = -4*gravity; vy = (float)-(0.25 * gravity); draw_ball(g, col); } } public void reset_ball(float tx, float cx, float rtx, int throw_height, float g) { float throw_time; if(rtx == -1) throw_time = (float)throw_height; else throw_time = (float)(throw_height - 0.5); x = tx; y = 0; vx = (cx - tx)/throw_time; vy = (float)-(0.5 * g * throw_time); throw_next = throw_height; re_throw_x = rtx; gravity = g; } public int throw_next() { return(throw_next); } public void next_time_step() { throw_next--; } } public class Juggle extends java.applet.Applet implements Runnable { Thread artist=null; // The animation thread boolean allow_suspend = true; boolean threadSuspended = true; // Used so we can stop anim by clicking Pattern p = new Pattern(); // The pattern we are juggling Ball balls[]; // Array string the balls float frames; // How many frames are there in a "1" handoff? int frame = 0; // The frame we are on int time_step = 0; // Our current position in the ss-pattern boolean left = false; // Next ball thrown with left hand? /* Initialise graphics area, and get parameters */ public void init() { int i; String s = null, s2 = null, s3 = null; resize(320,200); // Set juggle window size /* read in the pattern */ s = getParameter("pattern"); if(s == null) { System.err.println("Error: no pattern given, default: 3"); s = "3"; } p.parse_string(s); p.check_valid(); /* read in the number of frames */ s2 = getParameter("frames"); if(s2 == null) { System.err.println("Error: no frames given, default: 20"); s2 = "20"; } frames = Float.valueOf(s2).floatValue(); /* Do we want to allow starting and stopping? */ s3 = getParameter("allow_suspend"); if(s3 == null) { s3 = "true"; } if(s3.equals("false")) { threadSuspended = false; allow_suspend = false; } balls = new Ball[p.balls()]; for(i = 0; i < p.balls(); i++) { balls[i] = new Ball(i); } } /* This does all the work */ public void paint(Graphics g) { int throw_height; // The height to throw the next ball int throw_ball; // Index to next ball to be thrown float tx, cx, rtx; // Throw, catch and re-throw X co-ords float gravity; // The gravity the ball should experience int i; if(frame != 0) { frame = (frame + 1) % (int)frames; for(i = 0; i < p.balls(); i++) balls[i].move_ball(frames, frame, g); if(frame == (int)(frames/2)) for(i = 0; i < p.balls(); i++) balls[i].caught_ball(g); } else { throw_height = p.get_throw(time_step); if(throw_height != 0) { // We don't want to throw a ball for 0 // Find the ball due to be thrown for(throw_ball = 0; throw_ball < p.balls(); throw_ball++) { if(balls[throw_ball].throw_next() <=0) break; } if(left) { /* which hands are we throwing from/to? */ tx = 160 - (4 * 4); // TLX cx = 160 + (13 * 4); // CRX rtx = 160 + (4*4); // TRX if((throw_height % 2) == 0){ /* even throw, catch in same hand */ cx = 160 - (13 * 4); // CLX rtx = 160- (4 * 4); // TLX } } else { tx = 160 + (4 * 4); // TRX cx = 160 - (13 * 4); // CLX rtx = 160 - (4 * 4); // TLX if((throw_height % 2) == 0){ /* even throw, catch in same hand */ cx = 160 + (13 * 4); // CRX rtx = 160 + (4 * 4); // TRX } } if(throw_height == 1) { cx = rtx; rtx = -1; } if(throw_height == 2) { gravity = 0; cx = tx; } else gravity = (float) 9.81; balls[throw_ball].draw_ball(g, new java.awt.Color(192,192,192)); /* set up the ball's new position and speeds */ balls[throw_ball].reset_ball(tx, cx, rtx, throw_height, gravity); } /* move on to next time step */ for(i = 0; i < p.balls(); i++) { balls[i].next_time_step(); } time_step = (time_step + 1) % p.length(); left = !(left); frame = (frame + 1) % (int)frames; } } public void update(Graphics g) { paint(g); } public void start() { if (artist == null) { artist = new Thread(this); artist.start(); if(threadSuspended) artist.suspend(); } } public void stop() { artist = null; } public void run() { Thread.currentThread().setPriority(Thread.MIN_PRIORITY); while (artist != null) { try {Thread.sleep(10);} catch (InterruptedException e){} repaint(); } artist = null; } /* Handy function to start and stop the animation by clicking on it */ public boolean mouseDown(java.awt.Event evt, int x, int y) { if (allow_suspend) { if (threadSuspended) { artist.resume(); } else { artist.suspend(); } threadSuspended = !threadSuspended; } return true; } }