/** Simulator.java   David Fourer
  * 
  * an applet with multiple threads each painting a bouncing
  * ball on an applet's off-screen buffer.  The threads are
  * independent and random elements make the balls move in
  * unrelated paths.
  * 	A seperate thread called PaintThread( ) sends the off-screen
  * buffer to the screen 20 times per second.
  *	A seperate thread called LaunchThread( ) instantiates another
  * copy of the bouncing ball (applet) thread every 2 seconds.
  * 	Balls simulate natural motion and gradually come to rest.
  */

import java.applet.Applet;
import java.awt.*;

public class Simulator extends Applet implements Runnable {
	Image buf;						//off-screen image.
	Graphics gc;					
	ThreadGroup tGroup;			//threads created in init( ): LaunchThread, PaintThread
	int w, h;						//Applet width and height.
	boolean untilStop = true;	//loop in Applet thread, for stop( ) to kill threads.
	int aThreadAlive = 0;		//kills PaintThread when all Applet threads expired
	
	public void init( ) {
		w = getSize( ).width;
		h = getSize( ).height;

		buf = createImage( w, h );
		gc = buf.getGraphics( );
		gc.setColor( Color.pink );
		gc.fillRect( 0, 0, w, h );

		tGroup = new ThreadGroup( "tg" );
		new Thread( tGroup, new LaunchThread( this ) ).start( );
		new Thread( tGroup, new PaintThread( this ) ).start( );
	}
	public void run( ) {
		int x = 0;													//coordinate of ball
		int y = 10000;												//next coordinate
		int x2 = 0;
		int y2 = 10000;
		int d =  (int) ( Math.random( ) * 15 + 30 );		//diameter of balls
		int dx = (int) ( Math.random( ) * 2400 + 100 );	//x component velocity
		int dy = (int) ( Math.random( ) * 10000 - 5000 );//y component velocity
		int wEnd = (w-d)*100;								//boundary for balls
		int hEnd = (h-d)*100;
		int drag = 100;										//loss velocity per bounce
		int accelerate = 300 - d*3;
		int count = 0;											//end loop, kill thread

		Color randomColor = new Color(					//color of ball
									(int) ( Math.random( ) * 200 ),
									(int) ( Math.random( ) * 255 ),
									(int) ( Math.random( ) * 200 + 55 ) );
		
		while( untilStop ) {
			x = x2;									//move ball
			y = y2;
			x2 += dx;
			y2 += dy;

			if( x2 > wEnd ) {						//right boundary,
				dx = (-dx);							//reverse x component velocity
				x2 = wEnd;
			}
			if( x2 < 0 ) {							//left boundary
				dx = (-dx);
				x2 = 0;
			}
			if( y2 > hEnd ) {						//floor boundary
				dy = (-dy) + drag; 
				y2 = hEnd;
				if( dy <= accelerate )			//rolling on floor
					if( count++ > 50 ) {
						gc.setColor( Color.pink );
						gc.fillOval( x/100, y/100, d, d );	//paint out ball
						return;										//end loop, kill thread
					}
			}
			if( y2 < 0 ) {					//correct for integer calculations
				dy = (-dy) - drag;		//reduce velocity with each bounce
				y2 = 0;
			}
			dy += accelerate;				//gravity effect
			
			drawBall( randomColor, gc, x, y, x2, y2, d );	//synchronized method
		
			try { Thread.sleep( 50 ); }
			catch( InterruptedException e ) {}
			aThreadAlive = 0;									//for killing PaintThread
		}
	}//______________________________change off screen buffer_____________drawBall( )
	public synchronized void drawBall(
					Color randomColor, Graphics gc, int x, int y, int x2, int y2, int d ) {
		gc.setColor( Color.pink );
		gc.fillOval( x/100, y/100, d, d );
		gc.setColor( randomColor );
		gc.fillOval( x2/100, y2/100, d, d );
	}//______________________________________________________________PaintSim( )
	public void paintSim( Graphics g ) {
		g.drawImage( buf, 0, 0, this );
		aThreadAlive++;		//Applet thread re-sets to 0 if any threads running
		if( aThreadAlive > 5 )
			tGroup.stop( );			
	}//________________________________________________________________update( )
	public void update( Graphics g ) {
		paintSim( g );
	}//__________________________________________________________________stop( )
	public void stop( ) {
		untilStop = false;
		tGroup.stop( );		
	}//_________________________________________________________________start( )
	public void start( ) {
		gc.setColor( Color.pink );
		gc.fillRect( 0, 0, w, h );
		untilStop = true;
		repaint( );
	}

}

****************************************
****************************************

/** LaunchThread.java     David Fourer
  * for Simulator.java
  * instantiates new threads that creat animation on offscreen 
  * buffer in Applet.  New Threads created continuously at 2 second
  * intervals.  This thread automatically dies after 40 seconds.
  */
import java.applet.Applet;

public class LaunchThread extends Thread {
	Simulator sim;
	public LaunchThread( Simulator simulate ) {
		sim = simulate;
		
	}
	public void run( ) {
		for( int i=0; i<20; i++ ) {
			new Thread( sim ).start( );			//creates and starts multiple threads
			try { Thread.sleep( 2000 ); }
			catch( InterruptedException e ) {} 
		}
	}
}

****************************************
****************************************

/** PaintThread.java     David Fourer
  * for Simulator.java
  * calls paint in an applet at 1/20 sec intervals for animation.
  */
import java.applet.Applet;

public class PaintThread extends Thread {
	Applet a;
	public PaintThread( Applet aa ) {
		a = aa;
	}
	public void run( ) {
		while( true ) {
			try { Thread.sleep( 50 ); }
			catch( InterruptedException e ) {}
			a.repaint( );								//the calling applet paints
		}
	}
}