All pastes #912500 Raw Edit

grom358

public java v1 · immutable
·published 2008-02-21 12:46 UTC
rendered paste body
import java.awt.Color;import java.awt.Dimension;import java.awt.Graphics;import java.awt.Graphics2D;import java.awt.Image;import java.awt.RenderingHints;import java.awt.Toolkit;import java.awt.event.KeyAdapter;import java.awt.event.KeyEvent;import javax.swing.JComponent;import java.text.DecimalFormat;import java.util.List;import java.util.ArrayList;import java.util.LinkedList;/** * @author grom */public class Animation extends JComponent implements Runnable {	private static final long serialVersionUID = 9214955127889918259L;	/**	 * Number of frames with a delay of 0 ms before the animation thread yields	 * to other running threads.	 */	private static final int NO_DELAYS_PER_YIELD = 16;	private Ball[] balls;	private long period; // The amount of time in nanoseconds allocated to each frame	public Animation(int width, int height, int fps) {		setOpaque(true);		setBackground(Color.WHITE);		setSize(width, height);		setPreferredSize(new Dimension(width, height));		this.period = (long) 1000.0 / fps * 1000000L;		List<Ball> ballList = new ArrayList<Ball>();		ballList.add(new Ball(this, 10, 10, 0.5, 0.2, Color.BLUE));		ballList.add(new Ball(this, 30, 30, 0.3, 0.1, Color.GREEN));		ballList.add(new Ball(this, 150, 150, -0.2, -0.3, Color.RED));		ballList.add(new Ball(this, 300, 440, -0.3, -0.4, Color.CYAN));		ballList.add(new Ball(this, 600, 10, 0.3, 0.3, Color.GRAY));		ballList.add(new Ball(this, 10, 460, 0.1, 0.2, Color.MAGENTA));		ballList.add(new Ball(this, 50, 50, 0.3, 0.2, Color.ORANGE));		ballList.add(new Ball(this, 620, 30, -0.4, -0.2, Color.YELLOW));		ballList.add(new Ball(this, 10, 330, 0.2, 0.3, Color.PINK));		balls = (Ball[]) ballList.toArray(new Ball[0]);		initStats();		setFocusable(true);		requestFocus();		readyForTermination();	}		 private void readyForTermination() {		addKeyListener(new KeyAdapter() {			// listen for esc, q, end, ctrl-c on the canvas to			// allow a convenient exit from the full screen configuration			public void keyPressed(KeyEvent e) {				int keyCode = e.getKeyCode();				if ((keyCode == KeyEvent.VK_ESCAPE)						|| (keyCode == KeyEvent.VK_Q)						|| (keyCode == KeyEvent.VK_END)						|| ((keyCode == KeyEvent.VK_C) && e.isControlDown())) {					isRunning = false;				}			}		});	}	 	private Thread animator; // Thread for performing the animation	 	public void start() {		// initialise and start the thread		if (animator == null || !isRunning) {			animator = new Thread(this);			animator.start();		}	}	private volatile boolean isPaused = false;	private volatile boolean isRunning = false;	public void pause() {		isPaused = true;	}	public void resume() {		isPaused = false;	}	public void stop() {		isRunning = false;	}	public void run() {		final long dt = (long) (1000.0 / 50) * 1000000L; // Physics - 50 FPS		final long dtLimit = 1000000000L; // Limit number of updates to 1 second				Thread.currentThread().setPriority(Thread.MAX_PRIORITY);		isRunning = true;		prevStatsTime = System.nanoTime();				long accumulator = 0;				long newTime, deltaTime, sleepTime;		long overSleepTime = 0;		long noDelays = 0;		long sleepRemaining, beforeSleep, afterSleep;		long currentTime = System.nanoTime();		while (isRunning) {			newTime = System.nanoTime();			deltaTime = newTime - currentTime;			currentTime = newTime;			sleepTime = (period - deltaTime) - overSleepTime;						if (deltaTime > dtLimit)				deltaTime = dtLimit; // Limit the amount of physic updates			/*			if (sleepTime > 0) {				sleepRemaining = sleepTime;								while(sleepRemaining > 0) {					beforeSleep = System.nanoTime();					try {						Thread.sleep(sleepRemaining / 1000000L);					} catch (InterruptedException ex) {						// do nothing					}					afterSleep = System.nanoTime();					sleepRemaining -= (afterSleep - beforeSleep);				}				overSleepTime = (System.nanoTime() - currentTime) - sleepTime;			} else {				overSleepTime = 0L;				if (++noDelays >= NO_DELAYS_PER_YIELD) {					Thread.yield();   // give another thread a chance to run					noDelays = 0;				}							}*/			try {				Thread.sleep(16);			} catch (InterruptedException ex) {				// do nothing			}						accumulator += deltaTime;						while (accumulator >= dt) {				nextFrame(dt/1000000L);				accumulator -= dt;				physicUpdates++;			}										updateDisplay();			updateStats();		}		cleanup();	}		public void cleanup() {		isRunning = false;		System.exit(0);	}	/**	 * Creates the next frame	 */	public void nextFrame(double t) {		if (isPaused) return;		double rt = t; // remaining frame time		do {			// t is the time of the earliest collision.			// if t == rt then there are either no collisions			// or collision occured at time rt			// Since only the earliest collision needs to be found,			// the collision test are performed for the current			// time of the earliest collision (t).			// Test for wall collisions			for (int i = 0; i < balls.length; i++) {				t = Math.min(t, balls[i].testWallCollision(t));			}			// Test for ball collisions			// collision are added to the head of this list			List<Collision> collisions = new LinkedList<Collision>();			Ball a, b;			for (int i = 0; i < balls.length; i++) {				a = balls[i];				for (int j = i+1; j < balls.length; j++) {					b = balls[j];					t = Math.min(t, a.testBallCollision(b, t, collisions));				}			}			// Move the balls			for (int i = 0; i < balls.length; i++) {				balls[i].move(t);			}			// Handle the collisions that occured			// NOTE: It is possible that two collision occurred at the			// same time. Therefore the collision list is iterated			// until we get to a collision that occured after the			// time of the first collision			for (Collision c : collisions) {				if (c.t == t) {					c.handle();				} else {					break;				}			}			// calculate frame time renaming			t = rt - t;			rt = t;		// continue updating frame if there was a collision and the frame period has not expired		} while(rt > 0);	}	private void renderFrame(Graphics2D g2d) {		// clear screen in background		g2d.setColor(getBackground());		g2d.fillRect(0, 0, this.getWidth(), this.getHeight());		g2d.setRenderingHint(			RenderingHints.KEY_ANTIALIASING,			RenderingHints.VALUE_ANTIALIAS_ON);		for (int i = 0; i < balls.length; i++) {			balls[i].paint(g2d);		}		drawStats(g2d);	}	// off screen rendering	private Graphics dbg;	private Image dbImage = null;	private void updateDisplay() {	    if (dbImage == null) {	    	// Create buffer image			dbImage = createImage(getWidth(), getHeight());			if (dbImage == null) {				System.out.println("dbImage is null");				return;			} else {				dbg = dbImage.getGraphics();			}		}	    // Render frame to off-screen buffer	    renderFrame((Graphics2D) dbg);		// Use active rendering	    Graphics g;		try {			g = this.getGraphics();			if ((g != null) && (dbImage != null)) {				g.drawImage(dbImage, 0, 0, null);				Toolkit.getDefaultToolkit().sync();  // sync the display on some systems				g.dispose();			}		} catch (Exception e) {			System.out.println("Graphics context error: " + e);		}				frameCount++;	}	// used for gathering statistics	private static final long MAX_STATS_INTERVAL = 1000000000L; // record (roughly) every 1 second	private static final int NUM_FPS = 10; // number of FPS values stored to get an average	private long prevStatsTime;	private long frameCount = 0;	private double fpsStore[];	private long statsCount = 0;	private double averageFPS = 0.0;	private long physicUpdates = 0L;	private double upsStore[];	private double averageUPS = 0.0;	private void initStats() {		fpsStore = new double[NUM_FPS];		upsStore = new double[NUM_FPS];		for (int i = 0; i < NUM_FPS; i++) {			fpsStore[i] = 0.0;			upsStore[i] = 0.0;		}	}	private void updateStats() {		long elapsedTime = System.nanoTime() - prevStatsTime; // time since last		if (elapsedTime >= MAX_STATS_INTERVAL) { // record stats every MAX_STATS_INTERVAL			// stats collection			double actualFPS = (((double) frameCount / elapsedTime) * 1000000000L);			double actualUPS = (((double) physicUpdates / elapsedTime) * 1000000000L);			// store the latest FPS and UPS			fpsStore[(int) statsCount % NUM_FPS] = actualFPS;			upsStore[(int) statsCount % NUM_FPS] = actualUPS;			statsCount = statsCount + 1;			double totalFPS = 0.0; // total the stored FPSs and UPSs			double totalUPS = 0.0;			for (int i = 0; i < NUM_FPS; i++) {				totalFPS += fpsStore[i];				totalUPS += upsStore[i];			}			if (statsCount < NUM_FPS) { // obtain the average FPS and UPS				averageFPS = totalFPS / statsCount;				averageUPS = totalUPS / statsCount;			} else {				averageFPS = totalFPS / NUM_FPS;				averageUPS = totalUPS / NUM_FPS;			}			physicUpdates = 0;			frameCount = 0;			prevStatsTime = System.nanoTime();		}	}	private DecimalFormat df = new DecimalFormat("0.##");	private void drawStats(Graphics g) {	    // report frame count & average FPS and UPS at top left		g.setColor(Color.GRAY);		g.drawString("Average FPS/UPS: " + df.format(averageFPS) + ", "				+ df.format(averageUPS), 20, 25);	}	// Overrides for performance	public boolean isOpaque() {		return true;	}	public void paint(Graphics g) {	}	public void paintComponent(Graphics g) {	}	public void paintImmediately(int x, int y, int w, int h) {	}	public void paintImmediately(java.awt.Rectangle r) {	}	public void validate() {	}	public void revalidate() {	}	public void repaint(long tm, int x, int y, int width, int height) {	}	public void repaint(java.awt.Rectangle r) {	}	protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {	}	public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {	}	public void updateUI() {	}}