/***** Anharmonic oscillator & Runge-Kutta-Integration ***** ***** @author: Christoph Federrath *****/ import java.awt.*; import java.util.*; import java.applet.*; import java.awt.event.*; public class OsciAnharm extends Applet implements Runnable, ActionListener { private static final int W = 400, H = 200; //graphic window private int px, py; //pixelcoordinates private double x, xp, y, yp; //coordinates and velocities private double m = 1., D = 1.; //mass, spring private static final double l = 0.2; //spring balance length private int reib; //friction private static final double dt = 0.002; //timestep private double k1[] = new double[4]; //Runge-Kutta private double k2[] = new double[4]; //Runge-Kutta private double l1[] = new double[4]; //Runge-Kutta private double l2[] = new double[4]; //Runge-Kutta private int xPoints1_int[] = new int[11]; //pixelcoordinates for spring1 private int yPoints1_int[] = new int[11]; //pixelcoordinates for spring1 private int xPoints2_int[] = new int[11]; //pixelcoordinates for spring2 private int yPoints2_int[] = new int[11]; //pixelcoordinates for spring2 private double startwert[] = new double[5]; //save initial values private int startwert_reib; //save initial friction private boolean stop = false; //go/pause private int step; Canvas can, header; Button but_new, but_go; Button but_D_plus, but_D_min; Button but_reib_plus, but_reib_min; Thread animator; Graphics g, offGraphics, ghead; Image offImage; public void init() { setLayout(null); header = new Canvas(); header.setBounds(1,1,W,40); header.setBackground(Color.white); can = new Canvas(); can.setBounds(1,41,W,H+50); can.setBackground(Color.white); can.addMouseListener(new ML()); can.addMouseMotionListener(new ML()); Panel pan = new Panel(); pan.setLayout(null); pan.setBounds(W+1,1,100,H+90); pan.setBackground(new Color(0,200,100)); pan.setFont(new Font("Verdana",Font.PLAIN,10)); but_new = new Button("New"); but_go = new Button("Go/Pause"); but_D_plus = new Button("Spring +"); but_D_min = new Button("Spring -"); but_reib_plus = new Button("Friction ++"); but_reib_min = new Button("Friction --"); but_new.setBounds(10,H/2-78,80,27); but_D_plus.setBounds(10,H/2-28,80,27); but_D_min.setBounds(10,H/2+7,80,27); but_reib_plus.setBounds(10,H/2+47,80,27); but_reib_min.setBounds(10,H/2+82,80,27); but_go.setBounds(10,H/2+132,80,27); pan.add(but_go); pan.add(but_new); pan.add(but_D_plus); pan.add(but_D_min); pan.add(but_reib_plus); pan.add(but_reib_min); add(pan); add(can); add(header); but_new.addActionListener(this); but_go.addActionListener(this); but_D_plus.addActionListener(this); but_D_min.addActionListener(this); but_reib_plus.addActionListener(this); but_reib_min.addActionListener(this); g = can.getGraphics(); g.setFont(new Font("Verdana",Font.BOLD,10)); ghead = header.getGraphics(); ghead.setColor(Color.blue); ghead.setFont(new Font("Verdana",Font.BOLD,14)); startwert[0] = 0.4; //x startwert[1] = 0.0; //xp startwert[2] = 0.7; //y startwert[3] = 0.0; //yp startwert[4] = D; //D startwert_reib = 0; //reib }//init() public void startwerte() //method for setting initial values { x = startwert[0]; xp = startwert[1]; y = startwert[2]; yp = startwert[3]; D = startwert[4]; reib = startwert_reib; step = 0; }//startwerte() public void run() { try{Thread.sleep(100);} catch(InterruptedException e){} startwerte(); while(Thread.currentThread() == animator) { while(stop){try{Thread.sleep(40);} catch(InterruptedException e){}} runge_step_x(); //Runge-Kutta runge_step_y(); //Runge-Kutta x = x + (k1[0]+2*k1[1]+2*k1[2]+k1[3])/6; //Runge-Kutta xp = xp + (l1[0]+2*l1[1]+2*l1[2]+l1[3])/6; //Runge-Kutta y = y + (k2[0]+2*k2[1]+2*k2[2]+k2[3])/6; //Runge-Kutta yp = yp + (l2[0]+2*l2[1]+2*l2[2]+l2[3])/6; //Runge-Kutta if(step % 15 == 0) //paint frame not so often { try{Thread.sleep(10);} catch(InterruptedException e){} pixels(); //calculate pixelcoordinates repaint(); //paint new frame } step++; }//while currentThread()==animator }//run() public void paint(Graphics gr) //double buffer { if(offImage != null) { gr = can.getGraphics(); //initialize graphic window gr.drawImage(offImage, 0, 0, null); } } //paint(gr) public void update(Graphics gr) //double buffer { gr = can.getGraphics(); //initialize graphic window if(offGraphics == null) { offImage = createImage(W,H+50); offGraphics = offImage.getGraphics(); } offGraphics.setFont(new Font("Verdana",Font.BOLD,10)); offGraphics.setColor(can.getBackground()); offGraphics.fillRect(0, 0, W, H+50); paintFrame(offGraphics); gr.drawImage(offImage, 0, 0, null); } //update(gr) public void paintFrame(Graphics gr) { print(gr); //print values gr.drawLine(0,H/2,W,H/2); gr.drawLine(W/2,0,W/2,H); gr.setColor(Color.black); gr.drawPolyline(xPoints1_int,yPoints1_int,11); gr.drawPolyline(xPoints2_int,yPoints2_int,11); gr.fillOval(px-12,py-12,25,25); } //paintFrame(gr) public void print(Graphics gr) { ghead.drawString("Drag and drop mass ...",W/2-90,28); gr.setColor(Color.blue); gr.drawString("Energy:",W/2-150,H+20); gr.drawString("" + (double)(Math.round(100*energy()))/100,W/2-150,H+40); gr.drawString("Spring force:",W/2-40,H+20); gr.drawString("" + D,W/2-40,H+40); gr.drawString("Friction:",W/2+90,H+20); gr.drawString("" + reib,W/2+90,H+40); }//print(gr) public void pixels() //method for calculating pixelcoords { px = (int)Math.round(W*x); py = (int)Math.round(H*y); double currentLength1, currentLength2; //spring lengths double d1, d2; //pixel points for springs double alpha1, alpha2; //angle to rotate double xPoints1[] = new double[11]; //x spring1 double yPoints1[] = new double[11]; //y spring1 double xPoints2[] = new double[11]; //x spring2 double yPoints2[] = new double[11]; //y spring2 currentLength1 = Math.sqrt(x*x + (y-0.5)*(y-0.5))-0.05; currentLength2 = Math.sqrt((1.-x)*(1.-x) + (y-0.5)*(y-0.5))-0.05; d1 = currentLength1/16; d2 = currentLength2/16; alpha1 = Math.atan((y-0.5)/x); alpha2 = Math.atan((y-0.5)/(1.-x)); if(px < 0) { alpha1 = alpha1 + Math.PI; } if(px == 0) { if(py > H/2) alpha1 = Math.PI/2.; else alpha1 = -Math.PI/2.;} if(px > W) { alpha2 = alpha2 + Math.PI; } if(px == W) { if(py > H/2) alpha2 = Math.PI/2.; else alpha2 = -Math.PI/2.;} xPoints1[0] = 0.; yPoints1[0] = 0.; xPoints1[1] = d1; yPoints1[1] = 0.02; xPoints1[2] = 3*d1; yPoints1[2] = -0.02; xPoints1[3] = 5*d1; yPoints1[3] = 0.02; xPoints1[4] = 7*d1; yPoints1[4] = -0.02; xPoints1[5] = 9*d1; yPoints1[5] = 0.02; xPoints1[6] = 11*d1; yPoints1[6] = -0.02; xPoints1[7] = 13*d1; yPoints1[7] = 0.02; xPoints1[8] = 15*d1; yPoints1[8] = -0.02; xPoints1[9] = 16*d1; yPoints1[9] = 0.; xPoints2[0] = 0.; yPoints2[0] = 0.; xPoints2[1] = -d2; yPoints2[1] = -0.02; xPoints2[2] = -3*d2; yPoints2[2] = 0.02; xPoints2[3] = -5*d2; yPoints2[3] = -0.02; xPoints2[4] = -7*d2; yPoints2[4] = 0.02; xPoints2[5] = -9*d2; yPoints2[5] = -0.02; xPoints2[6] = -11*d2; yPoints2[6] = 0.02; xPoints2[7] = -13*d2; yPoints2[7] = -0.02; xPoints2[8] = -15*d2; yPoints2[8] = 0.02; xPoints2[9] = -16*d2; yPoints2[9] = 0.; double xnew, ynew; for(int i=0; i<10; i++) //rotate coordinates for springs { xnew = xPoints1[i]*Math.cos(alpha1) - yPoints1[i]*Math.sin(alpha1); ynew = xPoints1[i]*Math.sin(alpha1) + yPoints1[i]*Math.cos(alpha1); xPoints1[i] = xnew; yPoints1[i] = ynew; xPoints1_int[i] = (int)Math.round(W*xPoints1[i]); yPoints1_int[i] = (int)Math.round(H*yPoints1[i]) + H/2; xnew = xPoints2[i]*Math.cos(alpha2) + yPoints2[i]*Math.sin(alpha2); ynew = - xPoints2[i]*Math.sin(alpha2) + yPoints2[i]*Math.cos(alpha2); xPoints2[i] = xnew; yPoints2[i] = ynew; xPoints2_int[i] = (int)Math.round(W*xPoints2[i]) + W; yPoints2_int[i] = (int)Math.round(H*yPoints2[i]) + H/2; } xPoints1_int[10] = px-12; yPoints1_int[10] = py; xPoints2_int[10] = px+12; yPoints2_int[10] = py; }//pixels() /* force in x-direction */ public double forcex(double x) { return (D*(1. - (2*l)/Math.sqrt(5. - 8*x + 4*x*x - 4*y + 4*y*y) + x*(-2. + 2*l*(1./Math.sqrt(4*x*x + (1. - 2*y)*(1. - 2*y)) + 1./Math.sqrt(5. - 8*x + 4*x*x - 4*y + 4*y*y)))))/m - 0.1*reib*xp; }//forcex() /* force in y-direction */ public double forcey(double y) { return (D*(1. - l/Math.sqrt(4*x*x + (1. - 2*y)*(1. - 2*y)) - l/Math.sqrt(5. - 8*x + 4*x*x - 4*y + 4*y*y) + y*(-2. + 2*l*(1./Math.sqrt(4*x*x + (1. - 2*y)*(1. - 2*y)) + 1./Math.sqrt(5. - 8*x + 4*x*x - 4*y + 4*y*y)))))/m - 0.1*reib*yp; }//forcey() public double energy() { return (3*D)/4 + D*l*l - D*x + D*x*x + (m*xp*xp)/2 - D*l*Math.sqrt((-1. + x)*(-1. + x) + (-0.5 + y)*(-0.5 + y)) - D*l*Math.sqrt(x*x + (-0.5 + y)*(-0.5 + y)) - D*y + D*y*y + (m*yp*yp)/2; }//energy() public void runge_step_x() { k1[0] = dt*xp; l1[0] = dt*forcex(x); k1[1] = dt*(xp+l1[0]/2); l1[1] = dt*forcex(x+k1[0]/2); k1[2] = dt*(xp+l1[1]/2); l1[2] = dt*forcex(x+k1[1]/2); k1[3] = dt*(xp+l1[2]); l1[3] = dt*forcex(x+k1[2]); }//runge_step_x() public void runge_step_y() { k2[0] = dt*yp; l2[0] = dt*forcey(y); k2[1] = dt*(yp+l2[0]/2); l2[1] = dt*forcey(y+k2[0]/2); k2[2] = dt*(yp+l2[1]/2); l2[2] = dt*forcey(y+k2[1]/2); k2[3] = dt*(yp+l2[2]); l2[3] = dt*forcey(y+k2[2]); }//runge_step_y() public void actionPerformed(ActionEvent evt) { if(evt.getActionCommand() == but_new.getActionCommand()) { stop(); startwerte(); //set initial values pixels(); //calculate pixelcoords update(g); //paint initial state start(); } if(evt.getActionCommand()==but_go.getActionCommand()) { stop = !stop; } if(evt.getActionCommand()==but_D_plus.getActionCommand()) { startwert[4] = 2*startwert[4]; D = startwert[4]; if(stop == true) { update(g); } } if(evt.getActionCommand()==but_D_min.getActionCommand()) { startwert[4] = startwert[4]/2; D = startwert[4]; if(stop == true) { update(g); } } if(evt.getActionCommand() == but_reib_plus.getActionCommand()) { startwert_reib++; reib = startwert_reib; if(stop == true) { update(g); } } if(evt.getActionCommand() == but_reib_min.getActionCommand()) { if(reib > 0) { startwert_reib--; reib = startwert_reib; if(stop == true) { update(g); } } } }//actionPerformed(evt) public void start() { animator = new Thread(this); animator.start(); }//start() public void stop() { animator = null; offImage = null; offGraphics = null; }//stop() class ML extends MouseAdapter implements MouseMotionListener //overwrite Mouse { public void mousePressed (MouseEvent evt) { animator = null; stop = false; step = 0; if(evt.getX() < 40) { startwert[0] = 40./W; } else if(evt.getX() > W-40) { startwert[0] = (double)(W-40)/W; } else { startwert[0] = (double)(evt.getX())/W; } startwert[2] = (double)(evt.getY())/H; //new x startwerte(); pixels(); update(g); }//mousePressed public void mouseDragged (MouseEvent evt) { if(evt.getX() < 40) { startwert[0] = 40./W; } else if(evt.getX() > W-40) { startwert[0] = (double)(W-40)/W; } else { startwert[0] = (double)(evt.getX())/W; } startwert[2] = (double)(evt.getY())/H; //new x startwerte(); pixels(); update(g); }//mouseDragged public void mouseReleased (MouseEvent evt) { start(); }//mouseReleased public void mouseMoved (MouseEvent evt) { }//mouseMoved (simply implemented for Interface MouseMotionListener) }//ML }