/* file Pendulum.java ___________________Bob Terrell 4/16/97_____________ */ import java.applet.Applet; import java.awt.*; import java.util.*; import java.lang.*; // ---------- public class Pendulum ---------- public class Pendulum extends java.applet.Applet { graphr animator, plotter; double x00,y00, tmax, g,q,w, av, max = 0, min = 0; double[] x,y; final double delt = 0.1; int n, time = 1; // "time" runs from 1, representing 0, to n-1, representing tmax public void init() { setLayout(new BorderLayout(5,5)); add("East",new bpanel(this)); animator= new graphr(this); plotter = new graphr(this); add("West",plotter); add("Center",animator); q=10;g=2;w=1;tmax=25; } public void info() { System.out.println("\n Pendulum solves x''+(1/q)x'+sin(x) = g cos(wt)"); System.out.println("by the fourth order Runge-Kutta method. "); System.out.println("The animation shows the pendulum weight together "); System.out.println("with an arrow representing the direction of the "); System.out.println("applied force. The plot shows x versus t, and the"); System.out.println("effect of a small change in initial conditions or"); System.out.println("other parameters can be seen if you do not clear "); System.out.println("the previous plot, though a large change will "); System.out.println("affect the scale. Mouse clicks within the "); System.out.println("animator stop and restart it. "); // System.out.println("The Poincare' sections show (x,x') at succesive "); // System.out.println("maxima of the applied force."); System.out.println("Inspiration provided by Baker and Gollub's book. "); System.out.println("-This applet is unfinished. Please send comments "); System.out.println("-to bterrell@math.cornell.edu "); } public void go() // called when the Go button is pressed { int j; n = 2 + (int)(tmax/delt); x = null; x = new double[n]; y = null; y = new double[n]; x[1] = x00; y[1] = y00; for(time = 1; time < n-1; ++time) { rkstep(time); } max = min = x[1]; for(time = 2; time < n-1; ++time) { if(x[time] > max) max = x[time]; if(x[time] < min) min = x[time]; } plotter.paint(plotter.getGraphics()); animator.whereweare = 1; animator.start(); } private void rkstep(int j) { double k1x,k1y,k2x,k2y,k3x,k3y,k4x,k4y, xj = x[j], yj = y[j], tj = delt*j, xx,yy,tt; k1x = yj; k1y = -(1/q)*yj - Math.sin(xj) + g*Math.cos(w*tj); tt = 0.5*delt; xx = xj+tt*k1x; yy = yj+tt*k1y; k2x = yy; k2y = -(1/q)*yy - Math.sin(xx) + g*Math.cos(w*(tj+tt)); xx = xj+tt*k2x; yy = yj+tt*k2y; k3x = yy; k3y = -(1/q)*yy - Math.sin(xx) + g*Math.cos(w*(tj+tt)); tt = delt; xx = xj+tt*k3x; yy = yj+tt*k3y; k4x = yy; k4y = -(1/q)*yy - Math.sin(xx) + g*Math.cos(w*(tj+tt)); x[j+1] = xj + tt*(k1x+2*k2x+2*k3x+k4x)/6; y[j+1] = yj + tt*(k1y+2*k2y+2*k3y+k4y)/6; return; } } // ----------- class graphr ----------- class graphr extends Canvas implements Runnable { private Pendulum p; Thread thr; Graphics g; double x1, x2, y1, y2, F1, F2; int whereweare; boolean pleasestop; public void start() { if(this == p.animator) { x1 = Math.sin(0); y1 = -Math.cos(0); F1 = -.8*(p.g)*Math.cos((p.delt)*(p.w)*1); g = getGraphics(); pleasestop = false; if(thr == null) { thr = new Thread(this); thr.setPriority(Thread.MAX_PRIORITY); } thr.start(); } } public void run() { if(this == p.animator) { Image offscreen = this.createImage(this.size().width,this.size().height); Graphics gw = offscreen.getGraphics(); if(thr != null) { if(whereweare == 1) { g.drawOval(tox(x1)-4,toy(y1)-4,8,8); g.drawLine(tox(x1),toy(y1),tox(0),toy(0)); } if(F1 > 0) g.drawString("<--",tox(0),290); else g.drawString("-->",tox(0),290); } while(thr != null) { if(pleasestop) { g.drawString("t = " +(new Double(p.delt*whereweare)).toString(),tox(0),275); thr = null; return; } x2 = Math.sin(p.x[whereweare+1]); y2 = -Math.cos(p.x[whereweare+1]); F2 = -.8*(p.g)*Math.cos((p.delt)*(p.w)*whereweare); gw.clearRect(0,0,size().width,size().height); gw.drawOval(tox(x2)-4,toy(y2)-4,8,8); gw.drawLine(tox(x2),toy(y2),tox(0),toy(0)); if(F2 > 0) gw.drawString("<--",tox(0),290); else gw.drawString("-->",tox(0),290); //g.drawImage(offscreen,0,0,this.getBackground(),this); g.drawImage(offscreen,0,0,getBackground(),this); x1 = x2; y1 = y2; F1 = F2; ++whereweare; try { thr.sleep(60);} catch(InterruptedException ir) {} if(whereweare == p.n-1) { whereweare = 1; thr.stop(); thr = null; } } } } public boolean mouseDown(Event e,int x, int y) { if( this != p.animator) return true; if( thr != null) pleasestop = true; else { pleasestop = false; start(); } return true; } public int tox(double x) { double sc = 1.2; int hscale = size().width - 5; return (int)(hscale*((x + sc)/(2*sc))); } public int toy(double y) { double sc = 1.5; int vscale = size().height - 1; return (int)(vscale - vscale*((y + sc)/(2*sc))); } public int tov(double y) { double sc = p.max - p.min; int vscale = 200; ////////plotter.size().height - 1; return (int)(vscale - vscale*((y + sc)/(2*sc)))+60; } public graphr(Pendulum p) { super(); this.p = p; } public Dimension minimumSize() { return new Dimension(330,330); } public Dimension preferredSize() { return new Dimension(330,330); } public void paint(Graphics g) { if(this == p.animator) { /*g.setColor(Color.blue); g.drawString("x'' + (1/q)x' + sin(x) = g cos(wt)",15,20); g.setColor(Color.black); */ } if(this == p.plotter && p.time == p.n-1) { double t1 = 1, x1 = 0, // depends on IC t2,x2, pmx=p.max,pmn=p.min; g.drawLine(5,tov(pmn-1.2*Math.abs(pmn)),5,tov(pmx+1.2*Math.abs(pmx))); g.drawString("x",10,tov(pmx)-20); g.drawString((new Integer(-1+(int)pmn)).toString(),10,tov(-1+pmn)); g.drawString((new Integer( 1+(int)pmx)).toString(),10,tov( 1+pmx)); g.drawLine(2,tov(0),260,tov(0)); g.drawString("t",260,tov(0)-20); g.drawString((new Integer((int)p.tmax)).toString(),260,tov(0)+20); for(int tim = 1; tim < p.n-1; ++tim) { t2 = tim + 1; x2 = p.x[tim+1]; g.drawLine((int)((260*t1)/p.n)+5,tov(x1), (int)((260*t2)/p.n)+5,tov(x2)); t1 = t2; x1 = x2; } } } } // ----------- class bpanel -------------- class bpanel extends Panel { Pendulum p; TextField qF, gF, wF, tF, x0F, v0F; public bpanel(Pendulum p) { super(); this.p = p; qF= new TextField("10"); gF= new TextField("2"); wF= new TextField("1"); tF= new TextField("25"); x0F = new TextField("0"); v0F = new TextField("0"); setLayout(new GridLayout(12,2)); Label l1 = new Label("x''+ (1/q)x'"), l2 = new Label("+ sin(x)"), l3 = new Label("= g cos(wt)"); l1.setForeground(Color.blue); l2.setForeground(Color.blue); l3.setForeground(Color.blue); add(l1); add(l2); add(new Label("")); add(l3); add(new Button("Info")); add(new Label("")); add(new Button("Clear")); add(new Label("")); add(new Button("Go")); add(new Label("")); add(new Button("Stop")); add(new Label("")); add(new Label(" q = ")); add(qF); add(new Label(" g = ")); add(gF); add(new Label(" w = ")); add(wF); add(new Label(" 0 < t < ")); add(tF); add(new Label(" x(0) = ")); add(x0F); add(new Label(" x'(0) = ")); add(v0F); } public synchronized Dimension minimumSize() { return new Dimension(170,240);} public synchronized Dimension preferredSize() { return new Dimension(170,240);} public boolean action(Event e,Object arg) { if(e.target instanceof Button) { if(((String)arg).equals("Info")) p.info(); else if(((String)arg).equals("Clear")) { p.animator.getGraphics().clearRect(0,30, p.animator.size().width,p.animator.size().height); p.plotter.getGraphics().clearRect(0,0, p.plotter.size().width,p.plotter.size().height); } else if(((String)arg).equals("Go")) { try { p.q = (new Double(qF.getText())).doubleValue(); p.g = (new Double(gF.getText())).doubleValue(); p.w = (new Double(wF.getText())).doubleValue(); p.tmax = (new Double(tF.getText())).doubleValue(); p.x00 = (new Double(x0F.getText())).doubleValue(); p.y00 = (new Double(v0F.getText())).doubleValue(); } catch(NumberFormatException er) { System.err.println( " There is an error in q, g, w, or tmax."); return true; } p.go(); } else if(((String)arg).equals("Stop")) { p.animator.thr = null; } return true; } return false; } } // end Pendulum.java