//Nbody Applet vers.1, Last updated 5/09/99 //M.Dubson, April/May 1999, ENVD 3252 Final Project import java.awt.*; import java.applet.Applet; import java.awt.event.*; import java.lang.Thread; public class Nbody extends Applet implements Runnable, MouseListener, MouseMotionListener, ActionListener, ItemListener { int w, h; //screen width, height int x,y; //present coordinates of mouse int xPrevious, yPrevious; //most recent previous mouse coordinates. int nbrBodies; // number of bodies int bodyClicked; int movieLength; //number of time steps (dtFixed) in movie int movieTime; //time index in movie double dt; //the (variable)time step used in calculating trajectories double dtFixed; //the (fixed) time step used when playing back movie int moviePause; //delay is msec between movie frames double dtTot; // time since last screen paint double sizA; //size of accelerations - used to set dt Body bod[]; boolean calculating; //true if trajectories are being calculated boolean setPosState; boolean setVelState; boolean playMovieState; boolean removeCMState; Thread theThread; Image offscreen; static Color colorArray[]= {Color.red, Color.orange, Color.green, Color.blue, Color.yellow}; private Panel buttonPanel, massPanel; private Button buttons[]; private Checkbox checkCM; private Label massLabel[]; private TextField massField[]; public void init() { addMouseMotionListener(this); addMouseListener(this); w = getSize().width; h = getSize().height; nbrBodies = 3; dt = 0.1; //initial time step dtFixed = 2; //time step between screen paints moviePause = 8; movieLength = 10000; dtTot = 0.0; bod = new Body[nbrBodies]; for (int i = 0; i < nbrBodies; i++) { bod[i] = new Body(i); } setPosState = true; setVelState = false; playMovieState = false; calculating = false; removeCMState = true; //GUI controls buttonPanel = new Panel(); buttons = new Button[8]; checkCM = new Checkbox("C.M.frame"); massPanel = new Panel(); massField = new TextField[5]; massLabel = new Label[5]; buttonPanel.setLayout( new GridLayout(2, buttons.length+2)); massPanel.setLayout ( new GridLayout(1,5)); buttons[0] = new Button("Position"); buttons[1] = new Button("Velocity"); buttons[2] = new Button("Start"); buttons[3] = new Button("Stop"); buttons[4] = new Button("Reset"); buttons[5] = new Button("Play Movie"); buttons[6] = new Button("Slower"); buttons[7] = new Button("Faster"); for (int i = 0; i < nbrBodies; i++) { String str1 = String.valueOf(bod[i].mass); massField[i] = new TextField(str1,5); massField[i].setBackground(colorArray[i]); } for (int i = 0; i < buttons.length; i++) { buttonPanel.add(buttons[i]); } buttonPanel.add(checkCM); massLabel[0]= new Label("Masses"); massPanel.add(massLabel[0]); for (int i = 0; i < nbrBodies; i++) { //massPanel.add(massLabel[i]); massPanel.add(massField[i]); } for (int i = 0; i < nbrBodies; i++) { massField[i].addActionListener(this); } for (int i = 0; i < buttons.length; i++) { buttons[i].addActionListener(this); } checkCM.addItemListener(this); setLayout(new BorderLayout()); add(buttonPanel, BorderLayout.SOUTH); add(massPanel, BorderLayout.NORTH); //End of GUI controls }// end of init method public void actionPerformed( ActionEvent e ) { if( e.getSource() == buttons[0] ){buttonPos_action(e);} if( e.getSource() == buttons[1] ){buttonVel_action(e);} if( e.getSource() == buttons[2] ){buttonStart(e);} if( e.getSource() == buttons[3] ){buttonStop(e);} if( e.getSource() == buttons[4] ){buttonReset(e);} if( e.getSource() == buttons[5] ){buttonPlayMovie(e);} if( e.getSource() == buttons[6] ){buttonSlower(e);} if( e.getSource() == buttons[7] ){buttonFaster(e);} for (int i = 0; i < nbrBodies; i++) { if( e.getSource() == massField[i] ) { bod[i].mass = Double.valueOf(massField[i].getText()).doubleValue(); bod[i].radius = 1 + (int) (1.5*Math.pow(bod[i].mass, 0.333)); Graphics g = getGraphics(); this.paint(g); String str = String.valueOf(i+1); System.out.println("Mass"+ str + " is "+ bod[i].mass); } } } public void itemStateChanged( ItemEvent ie) { if (checkCM.getState()) { removeCMState = true;} else { removeCMState = false;} System.out.println("removeCMState is " + removeCMState); } public final void paint(Graphics g) { g.setColor(Color.black); g.fillRect(0, 0, w, h); for (int i = 0; i < nbrBodies; i++) //Color bars for mass Textfields { bod[i].paint(g); //System.out.println("Painting " + i + "."); } }//end of paint method public final void paintPaths(Graphics g) { for (int i = 0; i < nbrBodies; i++) { bod[i].paintTrajectory(g); bod[i].xLastPaint = bod[i].xPos; bod[i].yLastPaint = bod[i].yPos; //System.out.println("Painting " + i + "."); } }//end of paintPaths method //Double-buffered graphics code /* public void update (Graphics g) { if ( offscreen == null || offscreen.getWidth(this) != w || offscreen.getHeight(this) != h ) { offscreen = createImage ( w, h ); } Graphics g2 = offscreen.getGraphics(); paint (g2); g.drawImage ( offscreen, 0, 0, this); } */ public final void step() { //System.out.println("Calculating is " + calculating + "."); if (calculating) { update_as(); update_rs(); //4rd order Verlet update_vs(); //update_rs2(); //6th order Verlet dtTot += dt; if ( dtTot > dtFixed) { Graphics g = getGraphics(); paintPaths(g); dtTot = 0.0; //System.out.println("dt is " + dt + "."); for (int i = 0; i < nbrBodies; i++) { bod[i].xMovie[movieTime] = bod[i].xPos; bod[i].yMovie[movieTime] = bod[i].yPos; //bod[i].vXMovie[movieTime] = bod[i].vX; //bod[i].vYMovie[movieTime] = bod[i].vY; } movieTime ++; if (movieTime == 1000){System.out.println("movieTime is "+movieTime+".");} } }//end of if (calculating) loop if (playMovieState && movieTime < 10000) { Graphics g = getGraphics(); g.setColor(Color.black); g.fillRect(0, 0, w, h); for (int b = 0; b < nbrBodies; b++) { int xTemp = (int) bod[b].xMovie[movieTime]; int yTemp = (int) bod[b].yMovie[movieTime]; bod[b].posPaintIn(g, xTemp, yTemp); } movieTime ++; }//end of playMovie Loop //repaint(); }//end of step method public void start() { if ( theThread == null ){ theThread = new Thread (this);} theThread.start(); } public void run() { while(true) { //System.out.println("Hit run method..."); step(); if (playMovieState) { try {theThread.sleep(moviePause);} catch (Exception e){;} } } } public void stop() { if (theThread != null && theThread.isAlive()) {theThread.stop();} theThread = null; } public void mousePressed (MouseEvent e) { x = e.getX(); y = e.getY(); xPrevious = x; yPrevious = y; bodyClicked = whoIsClicked(x,y); }//end of mousePressed method public void mouseReleased (MouseEvent e) { Graphics g = getGraphics(); update_as(); paint(g); if (setPosState && bodyClicked != 100) { bod[bodyClicked].setR(x,y); //bod[bodyClicked].xInit = bod[bodyClicked].xPos; //bod[bodyClicked].yInit = bod[bodyClicked].yPos; System.out.println("X is " + bod[bodyClicked].xPos + ". Y is "+ bod[bodyClicked].yPos + ". "); System.out.println("XLastPnt is " + bod[bodyClicked].xLastPaint + ". YLastPnt is "+ bod[bodyClicked].yLastPaint + ". "); /*System.out.println("aX=" + bod[bodyClicked].aX + " aY=" + bod[bodyClicked].aY + "."); System.out.println("sizA is " + sizA + ". dt is" + dt + "."); */ } if (setVelState && bodyClicked != 100) { bod[bodyClicked].setV(x,y); bod[bodyClicked].vXInit = bod[bodyClicked].vX; bod[bodyClicked].vYInit = bod[bodyClicked].vY; System.out.println("vCMX is " + vCMX()+" . vCMY is " + vCMY() + "."); System.out.println("vXInit is " + bod[bodyClicked].vXInit + ". vYInit is "+ bod[bodyClicked].vYInit + ". "); } //System.out.println("vXInitCM is " + bod[bodyClicked].vXInitCM + // ". vYInitCM is "+ bod[bodyClicked].vYInitCM + ". "); /* System.out.println("X is " + bod[bodyClicked].xPos + ". Y is "+ bod[bodyClicked].yPos + ". "); System.out.println("XLastPnt is " + bod[bodyClicked].xLastPaint + ". YLastPnt is "+ bod[bodyClicked].yLastPaint + ". "); */ }//end of mouseReleased method public void mouseDragged (MouseEvent e) { x = e.getX(); y = e.getY(); Graphics g = getGraphics(); if (setPosState && bodyClicked != 100) { bod[bodyClicked].paintOut(g); bod[bodyClicked].posPaintIn(g, x, y); bod[bodyClicked].setR(x,y); //System.out.println("xPos=" + bod[bodyClicked].xPos //+ " yPos=" + bod[bodyClicked].yPos + "."); } if (setVelState && bodyClicked != 100) { bod[bodyClicked].paintOut(g); bod[bodyClicked].velPaintIn(g, x, y); bod[bodyClicked].setV(x,y); //System.out.println("vX=" + bod[bodyClicked].vX //+ " vY=" + bod[bodyClicked].vY + "."); } } public void mouseClicked( MouseEvent e){;} void buttonPos_action(ActionEvent e) { setPosState = true; setVelState = false; System.out.println("Setting positions."); } void buttonVel_action(ActionEvent e) { setPosState = false; setVelState = true; System.out.println("Setting velocities."); } void buttonStart(ActionEvent e) { if (removeCMState) { for (int i=0; i < nbrBodies; i++ ) { bod[i].vXInitCM = bod[i].vX - vCMX(); bod[i].vYInitCM = bod[i].vY - vCMY(); bod[i].vX = bod[i].vXInitCM; bod[i].vY = bod[i].vYInitCM; } } //Paint out bodies to see trajectories clearly Graphics g = getGraphics(); g.setColor(Color.black); g.fillRect(0, 0, w, h); System.out.println("Starting calculation."); double pXtot = 0.0; double pYtot = 0.0; for (int i = 0; i < nbrBodies; i++) { pXtot += bod[i].mass * bod[i].vX; pYtot += bod[i].mass * bod[i].vY; } System.out.println("pXtot = " + pXtot + ". pYtot = " + pYtot + "."); double p_tot = magnitude_Totp(); System.out.println("P_tot is " + p_tot + "."); calculating = true; }//end of buttonStart method void buttonStop(ActionEvent e) { calculating = false; playMovieState = false; double p_tot = magnitude_Totp(); System.out.println("movieTime is "+movieTime); //System.out.println("P_tot is " + p_tot + "."); } void buttonReset(ActionEvent e) { for (int i = 0; i < nbrBodies; i++) { bod[i].xPos = bod[i].xInit; bod[i].yPos = bod[i].yInit; bod[i].vX = bod[i].vXInit; bod[i].vY = bod[i].vYInit; bod[i].xLastPaint = bod[i].xInit; bod[i].yLastPaint = bod[i].yInit; } movieTime = 0; setPosState = true; setVelState = false; calculating = false; playMovieState = false; repaint(); System.out.println("Stopping calculation."); } void buttonPlayMovie(ActionEvent e) { calculating = false; playMovieState = true; moviePause = 16; movieTime = 0; } void buttonSlower(ActionEvent e) { moviePause += 2; System.out.println("moviePause = "+ moviePause+ "."); } void buttonFaster(ActionEvent e) { if (moviePause > 6){moviePause -= 2 ;} System.out.println("moviePause = " + moviePause + "."); } public int whoIsClicked(int xPress, int yPress) { for ( int i = 0; i < nbrBodies; i++) { if (setPosState && bod[i].bodClicked(xPress, yPress)) { System.out.println("Body "+i+" is clicked."); return i; } if (setVelState && bod[i].velClicked(xPress, yPress)) { System.out.println("Velocity "+i+" is clicked."); return i; } } System.out.println("No body is clicked."); return 100; }//end of whoIsClicked()method //method update_as() computes present accelerations of all bodies //*************************************************************** public final void update_as() { dt = 0.1; for(int i = 0; i < nbrBodies; i++) { bod[i].aXBeforeLast = bod[i].aXLast; bod[i].aYBeforeLast = bod[i].aYLast; bod[i].aXLast = bod[i].aX; bod[i].aYLast = bod[i].aY; bod[i].aX = 0.0; bod[i].aY = 0.0; for(int j = 0; j < nbrBodies; j++) { if (j != i) { double rQ =Math.pow((Math.pow((bod[i].xPos - bod[j].xPos),2)+ Math.pow((bod[i].yPos - bod[j].yPos),2)) , -1.5); bod[i].aX += bod[j].mass*(bod[j].xPos - bod[i].xPos)*rQ; bod[i].aY += bod[j].mass*(bod[j].yPos - bod[i].yPos)*rQ; double dtTemp = 0.0005*Math.pow(rQ*bod[j].mass,-0.5); if (dtTemp < dt) {dt = dtTemp;} } }//end of j-loop }//end of i-loop }// end of update_as method //Update positions of all bodies //**************************************************************** public final void update_rs() { for(int i = 0; i < nbrBodies; i++) { bod[i].xLast = bod[i].xPos; bod[i].yLast = bod[i].yPos; bod[i].xPos = bod[i].xPos + dt*bod[i].vX + dt*dt*0.5*bod[i].aX; bod[i].yPos = bod[i].yPos + dt*bod[i].vY + dt*dt*0.5*bod[i].aY; } }//end of update_rs //Update velocities of all bodies //***************************************************************** public final void update_vs() { for(int i = 0; i < nbrBodies; i++) { bod[i].vX = bod[i].vX + dt*0.5*(bod[i].aX + bod[i].aXLast); bod[i].vY = bod[i].vY + dt*0.5*(bod[i].aY + bod[i].aYLast); } }//end of update_vx //Compute x- and y-component of center-of-mass velocity. //****************************************************************** public double vCMX() { double totMass = 0.0; double totxMom = 0.0; for (int i = 0; i < nbrBodies; i++) { totMass += bod[i].mass; totxMom += bod[i].mass * bod[i].vXInit; } return totxMom/totMass; } public double vCMY() { double totMass = 0.0; double totyMom = 0.0; for (int i = 0; i < nbrBodies; i++) { totMass += bod[i].mass; totyMom += bod[i].mass * bod[i].vYInit; } return totyMom/totMass; } public double totpX() { double totxMom = 0.0; for (int i = 0; i < nbrBodies; i++) { totxMom += bod[i].mass * bod[i].vX; } return totxMom; } public double totpY() { double totyMom = 0.0; for (int i = 0; i < nbrBodies; i++) { totyMom += bod[i].mass * bod[i].vY; } return totyMom; } public double magnitude_Totp() { double totp = 0.0; for (int i = 0; i < nbrBodies; i++) { totp += Math.pow((Math.pow(totpX(),2) + Math.pow(totpY(),2)),0.5); } return totp; } public void mouseMoved( MouseEvent e){;} public void mouseEntered( MouseEvent e){;} public void mouseExited( MouseEvent e){;} }//end of class Nbody