import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Toolkit; import javax.swing.JFrame; import javax.swing.JPanel; /** * Dynamics of a collision between 2 hard spheres * * Based on equations from http://introcs.cs.princeton.edu/java/assignments/collisions.html * (I kept the same names for clarity) * * @author Jan Lemeire * */ public class Collision { double rxi, ryi, vxi, vyi; double rxj, ryj, vxj, vyj; double ri, rj, rtot; // radius of both spheres double mi, mj; // masses // exact time and place of collision double deltat; double rxiC, ryiC; // position sphere i double rxjC, ryjC; // position sphere j // velocities right after collision double vxiC, vyiC; // velocity sphere i double vxjC, vyjC; // velocity sphere j /** * Calculates time and position of collision and velocities right after the collision. * If spheres will not collide, an error is thrown. * Properties sphere i: position (rxi, ryi), velocity (vxi, vyi), radius ri, mass mi * Properties sphere j: position (rxj, ryj), velocity (vxj, vyj), radius rj, mass mj */ public Collision(double rxi, double ryi, double vxi, double vyi, double rxj, double ryj, double vxj, double vyj, double ri, double rj, double mi, double mj){ this.rxi=rxi; this.ryi=ryi; this.vxi=vxi; this.vyi=vyi; this.rxj=rxj; this.ryj= ryj; this.vxj= vxj; this.vyj= vyj; this.ri=ri; this.rj=rj; rtot = ri+rj; this.mi=mi; this.mj= mj; // (1) calculate exact time of collision double deltarx = rxj - rxi; double deltary = ryj - ryi; double deltavx = vxj - vxi; double deltavy = vyj - vyi; double deltav_dot_deltar = deltarx * deltavx + deltary * deltavy; if (deltav_dot_deltar >= 0) throw new RuntimeException("Spheres will not collide."); double deltav_dot_deltav = deltavx*deltavx + deltavy*deltavy; double discriminant = Math.pow(deltav_dot_deltar, 2) - (deltav_dot_deltav) * (deltarx * deltarx + deltary * deltary - rtot * rtot); if (discriminant<0) throw new RuntimeException("Spheres will not collide."); deltat = - (deltav_dot_deltar + Math.sqrt(discriminant)) / deltav_dot_deltav; // (2) calculate place of collision rxiC = rxi + deltat * vxi; ryiC = ryi + deltat * vyi; rxjC = rxj + deltat * vxj; ryjC = ryj + deltat * vyj; // (3) calculate velocities after collision double deltarxC = rxjC - rxiC; double deltaryC = ryjC - ryiC; double deltavxC = vxj - vxi; double deltavyC = vyj - vyi; double deltav_dot_deltarC = deltarxC * deltavxC + deltaryC * deltavyC; // the equations are based on the preservation of impulse ("moment" in nederlands) // impulse = mass * velocity double J = 2 * mi * mj * deltav_dot_deltarC / (rtot * (mi + mj)); double Jx = J * deltarxC / rtot; double Jy = J * deltaryC / rtot; vxiC = vxi + Jx/mi; vyiC = vyi + Jy/mi; vxjC = vxj - Jx/mj; vyjC = vyj - Jy/mj; } /** * DEMO PROGRAM WITH EXAMPLE COLLISION */ public static void main(String[] args) { final int SCREEN_WIDTH = 800, SCREEN_HEIGHT = 800; JPanel panel = new JPanel(){ public void paintComponent(Graphics g) { // * START CONDITION double rxi = 100, ryi = 100, vxi = 20, vyi= 21; double rxj = 500, ryj=500, vxj=-20, vyj=-25; double ri=40, rj=40; // radius of both spheres double mi=1, mj=1; // masses Graphics2D g2D = (Graphics2D)g; // draw a grid on the panel where gap between the lines is 50 StandardGraphics.drawGrid(g2D, 0, 0, getWidth(), getHeight(), 50); // SITUATION BEFORE COLLISION StandardGraphics.drawCircle(g, rxi, ryi, ri); StandardGraphics.DrawArrow(g2D, (int)rxi, (int)ryi, (int)(rxi+vxi), (int)(ryi+vyi), 15); g.setColor(Color.RED); StandardGraphics.drawCircle(g, rxj, ryj, rj); StandardGraphics.DrawArrow(g2D, (int)rxj, (int)ryj, (int)(rxj+vxj), (int)(ryj+vyj), 15); try{ // CALCULATION OF COLLISION Collision col = new Collision(rxi, ryi, vxi, vyi, rxj, ryj, vxj, vyj, ri, rj, mi, mj); System.out.println("Collision after "+col.deltat+" time units"); g.setFont(new Font("Times", Font.BOLD, 24)); // change font g.drawString("Collision after "+col.deltat+" time units", 250, 100); // SITUATION JUST AFTER COLLISION g.setColor(Color.BLACK); StandardGraphics.drawCircle(g, col.rxiC, col.ryiC, ri); StandardGraphics.DrawArrow(g2D, (int)col.rxiC, (int)col.ryiC, (int)(col.rxiC+col.vxiC), (int)(col.ryiC+col.vyiC), 15); g.setColor(Color.RED); StandardGraphics.drawCircle(g, col.rxjC, col.ryjC, rj); StandardGraphics.DrawArrow(g2D, (int)col.rxjC, (int)col.ryjC, (int)(col.rxjC+col.vxjC), (int)(col.ryjC+col.vyjC), 15); } catch (RuntimeException e){ g.setFont(new Font("Times", Font.BOLD, 24)); // change font g.drawString("Spheres will not collide", 300, 100); } } }; panel.setPreferredSize(new Dimension(SCREEN_WIDTH, SCREEN_HEIGHT)); JFrame frame = new JFrame("Demo of StandardGraphics Functions"); frame.add(panel); frame.pack(); // calculate frame size frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // show centered Dimension frameSize = frame.getSize(); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); frame.setLocation(screenSize.width / 2 - frameSize.width / 2, screenSize.height / 2 - frameSize.height / 2); frame.setVisible(true); } }