collide.java

Download source: collide.zip

classes


collide.java

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.awt.geom.*;
import javax.swing.Timer;
import javax.swing.*;


class TestPanel extends JPanel
{
    Timer myTimer;
    ArrayList<Ball>  balls;
    ArrayList<Collision> collisions;
    int width, height;
    Ball ball1, ball2;
    int wait, mode;
    
    TestPanel(int mode)
    {
	this.mode = mode;
	balls = new ArrayList<Ball>();
	collisions = new ArrayList<Collision>();
	if (mode>0) {
	    ball1 = new Ball(20,70,6,-1,Color.RED);
	    ball2 = new Ball(300,80,-8,1,Color.BLUE);
	}
	else {
	    ball1 = new Ball();
	    ball2 = new Ball(300,800,-5,8,Color.BLUE);
	    balls.add(new Ball(30,200,-9,7,Color.GREEN));
	}
	balls.add(ball1);
	balls.add(ball2);
	//for (Ball ball: balls) System.out.println(ball);
    }

    public void update_collision_list(double tstep)
    {
	Collision c;
	collisions.clear();
	for (Ball ball: balls)	{
	    c = intersect_window(ball,tstep);
	    if (c!=null) collisions.add(c);
	}
	int nballs = balls.size();
	for (int i=0; i<nballs-1; i++) {
	    for (int j=i+1; j<nballs; j++) {
		Ball b1 = balls.get(i);
		Ball b2 = balls.get(j);
		c = intersect_balls(b1,b2,tstep);
		if (c!=null) collisions.add(c);
	    }
	}
    }	

    public void update()
    {
	width = getWidth();
	height = getHeight();
	double tstep, tmore;
	tmore = 1.0;
	while (tmore>0.0) {
	    update_collision_list(tmore);
	    if (collisions.size()>0) {
		java.util.List<Collision> list = collisions; 
		Collections.<Collision>sort(list);
		Collision c = collisions.get(0);
		tstep = c.timestep;
		tmore = tmore - tstep;
		for (Ball ball: balls) ball.move(tstep);
		c.update_velocity();
	    }
	    else {
		tstep = tmore;
		tmore = 0.0;
		for (Ball ball: balls) ball.move(tstep);
	    }
	}
	repaint();
    }

   Collision intersect_window(Ball ball, double tstep)
    {
	double [] t = new double[4];
	double tmax = 1000;
	if (ball.vx<0) {
	    t[0] = (ball.px-ball.radius)/(-ball.vx);
	}
	else t[0] = tmax;
	if (ball.vx>0) {
	    t[1] = (width - ball.px - ball.radius)/(ball.vx);
	}
	else t[1] = tmax;
	if (ball.vy<0) {
	    t[2] = (ball.py-ball.radius)/(-ball.vy);
	}
	else t[2] =  tmax;
	if (ball.vy>0) {
	    t[3] = (height - ball.py - ball.radius)/ball.vy;
	}
	else t[3] = tmax;
	int idx = 0;
	double tx = t[0];
	for (int i=1; i<4; i++) {
	    if (t[i]>tx) continue;
	    tx = t[i];
	    idx = i;
	}
	if (tx>tstep) {
	    return null; // does not intersect
	}
	return new Collision(tx,ball,idx);
    }

    Collision intersect_balls(Ball ball1, Ball ball2, double tstep)
    {
	// relative velocity
	double rx = ball2.vx - ball1.vx;
	double ry = ball2.vy - ball1.vy;
	// relative position
	double px = ball2.px - ball1.px;
	double py = ball2.py - ball1.py;
	// test radius
	double r = ball1.radius + ball2.radius - 1;
	double C =  px*px + py*py - r*r;
	if (C<0) return null; // already intersecting
	double B = rx*px+ry*py;
	double A = rx*rx+ry*ry;
	if (A<=0.0) return null; // no relative velocity
	// quadratic At^2 + 2Bt + C = 0
	double radical = B*B-A*C;
	if (radical<=0.0) return null; // no intersection (balls miss)
	double R = Math.sqrt(radical);
	double t;
	if (B>0) t = (-B + R)/A;
	else t = -(B+R)/A;
	if (t<=0.0 || t>tstep) return null; // no intersection within time limit
	return new Collision(t,ball1,ball2);
    }

    

    public void paintComponent(Graphics g)
    {
	super.paintComponent(g);
	for (Ball ball: balls) 	ball.draw(g);
    }

    
    public void startAnimation()
    {
	if (myTimer == null)
	{
	    myTimer = new Timer(50,new TimerHandler() );
	    myTimer.start();
	}
	else if (!myTimer.isRunning()) myTimer.restart();
    }

    public void stopAnimation()
    {
	myTimer.stop();
    }

    private class TimerHandler implements ActionListener
    {
	public void actionPerformed(ActionEvent actionevent)
	{
	    update();
	}
    }
}

public class collide {

    public static void main(String[] args)
   {
	int n = args.length;
	if (n>0) {
	    n = Integer.parseInt(args[0]);
	}
	final int mode = n;
	EventQueue.invokeLater(new Runnable() {
	   public void run()
	   {
	       TestPanel panel = new TestPanel(mode);
	       JFrame frame = new JFrame("Moving Objects");
	       frame.add(panel);
	       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	       if (panel.mode>0) frame.setSize(400,120);
	       else frame.setSize(400,400);
	       frame.setVisible(true);
	       panel.startAnimation();
	   }
       });
   }
}


Maintained by John Loomis, updated Fri Feb 22 20:09:09 2019