import java.awt.*; import java.applet.*; import java.awt.event.*; public class DoublePendulum extends Applet implements Runnable { static final double g=9.8; static final double dt=0.05; static final double scale=(72/0.0254)*0.05; static final double rho=7874; boolean running; Image image; double length1; double length2; double mass1; double mass2; double mu; int radius1; int radius2; class Pendulum extends RungeKutta { double theta1; double theta2; double dtheta1; double dtheta2; protected void copy(RungeKutta q) { Pendulum p=(Pendulum)q; theta1=p.theta1; theta2=p.theta2; dtheta1=p.dtheta1; dtheta2=p.dtheta2; } protected void add(RungeKutta q) { Pendulum p=(Pendulum)q; theta1+=p.theta1; theta2+=p.theta2; dtheta1+=p.dtheta1; dtheta2+=p.dtheta2; } protected void multiply(double c) { theta1*=c; theta2*=c; dtheta1*=c; dtheta2*=c; } protected void differentiate(double t) { double delta=theta1-theta2; double new_dtheta1= ( g*(Math.sin(theta2)*Math.cos(delta)-mu*Math.sin(theta1)) -( length2*Math.pow(dtheta2,2) +length1*Math.pow(dtheta1,2)*Math.cos(delta) )*Math.sin(delta) )/(length1*(mu-Math.pow(Math.cos(delta),2))); double new_dtheta2= ( g*mu*(Math.sin(theta1)*Math.cos(delta)-Math.sin(theta2)) +( mu*length1*Math.pow(dtheta1,2) +length2*Math.pow(dtheta2,2)*Math.cos(delta) )*Math.sin(delta) )/(length2*(mu-Math.pow(Math.cos(delta),2))); theta1=dtheta1; theta2=dtheta2; dtheta1=new_dtheta1; dtheta2=new_dtheta2; } } Pendulum pendulum=new Pendulum(); public void init() { setBackground(Color.white); addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { start(); } }); } public synchronized void start() { length1=Math.random(); length2=0.1+Math.random(); mass1=Math.random(); mass2=Math.random(); mu=1+(mass1/mass2); radius1=(int)(Math.pow((3*mass1)/(4*Math.PI*rho),1/3.)*scale); radius2=(int)(Math.pow((3*mass2)/(4*Math.PI*rho),1/3.)*scale); pendulum.theta1=Math.PI*Math.random()*2; pendulum.theta2=Math.PI*Math.random()*2; pendulum.dtheta1=0; pendulum.dtheta2=0; if (!running) { running=true; new Thread(this,"Physics").start(); } } public void stop() { running=false; } public void run() { while (running) { synchronized (this) { pendulum.step(0,dt); } repaint(); try { Thread.sleep((long)(dt*1000)); } catch (InterruptedException e) { e.printStackTrace(); } } } public void update(Graphics g) { paint(g); } public synchronized void paint (Graphics g) { int width=getSize().width,height=getSize().height; if (image==null) image=createImage(width,height); Graphics og=image.getGraphics(); og.clearRect(0,0,width,height); og.translate(width/2,height/3); int x1=(int)(Math.sin(pendulum.theta1)*length1*scale); int y1=(int)(Math.cos(pendulum.theta1)*length1*scale); int x2=(int)(x1+Math.sin(pendulum.theta2)*length2*scale); int y2=(int)(y1+Math.cos(pendulum.theta2)*length2*scale); og.drawLine(0,0,x1,y1); og.drawLine(x1,y1,x2,y2); og.fillOval(x1-radius1,y1-radius1,radius1*2,radius1*2); og.fillOval(x2-radius2,y2-radius2,radius2*2,radius2*2); og.dispose(); g.drawImage(image,0,0,this); } } abstract class RungeKutta implements Cloneable { private RungeKutta back; private RungeKutta diff; abstract protected void copy(RungeKutta q); abstract protected void add(RungeKutta q); abstract protected void multiply(double c); abstract protected void differentiate(double t); public void step(double time,double h) { if (back==null) try { back=(RungeKutta)clone(); diff=(RungeKutta)clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } back.copy(this); // k1 diff.copy(back); diff.differentiate(time); diff.multiply(h/6); add(diff); // k2 diff.multiply(0.5*6); diff.add(back); diff.differentiate(time+h/2); diff.multiply(h/3); add(diff); // k3 diff.multiply(0.5*3); diff.add(back); diff.differentiate(time+h/2); diff.multiply(h/3); add(diff); // k4 diff.multiply(3); diff.add(back); diff.differentiate(time+h); diff.multiply(h/6); add(diff); } }