package pendulk;

import java.util.*;
import java.text.*;
import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import java.lang.*;

public class pendula extends Applet
  implements Runnable,ActionListener{

final int Nx=16,Ny=6;
double mm,k,k2,len,ang;
double Mtheta,Mangv;
double Emax,scale,p10,ML2,kdt,k2dt;
int ct,tct,gpos,ysz;
double mas[][]=new double [Nx+1][Ny+1];
double theta[][]=new double [Nx+1][Ny+1];
double angv[][]=new double [Nx+1][Ny+1];
int xp[]=new int[240];
int yp1[]=new int[240];
int yp2[]=new int[240];
int yp3[]=new int[240];
int yp4[]=new int[240];
int yp5[]=new int[240];
int yp6[]=new int[240];
final int g=10,sp=40,pl=sp*4/5,gap=10;
final double dt=0.001;
Color palered=new Color(255,216,216);
Color palegrey=new Color(216,216,216);
Color paleblue=new Color(216,216,255);
boolean logscale;

MainPanel MP=new MainPanel();
GPanel GraP=new GPanel();
Panel CP2=new Panel();
keyPanel key=new keyPanel();
Thread timer = null;
Button but1=new Button("Restart");
TextField ktxt,k2txt,matxt,mmtxt,angtxt,lentxt;
Label klab,k2lab,malab,mmlab,anglab,lenlab;
CheckboxGroup loglin = new CheckboxGroup();
Checkbox logcb=new Checkbox("log graph scale",loglin,true);
Checkbox lincb=new Checkbox("linear graph scale",loglin,false);
Font sf;

public pendula() {}

double sqr(double x){return x*x;}
double strToD(String s){return Double.valueOf(s).doubleValue();}

public void init() {
int i,j,x;
for (x=0;x<240;x++)xp[x]=x;
sf=new Font("Arial",Font.PLAIN,11);
tct=0;
k=1;k2=1;mm=50;ang=0.5;Mangv=1;len=5;
setLayout(null);
setBackground(new Color(230,240,240));
ktxt=new TextField(11);klab=new Label("Coupling between small masses");
k2txt=new TextField(11);k2lab=new Label("Coupling to large mass");
mmtxt=new TextField(11);mmlab=new Label("Large mass");
angtxt=new TextField(11);anglab=new Label("Average angular velocity.");
matxt=new TextField(11);malab=new Label("Ang. Vel. of large mass");
lentxt=new TextField(11);lenlab=new Label("Length of large pendulum");
but1.addActionListener(this);
add(MP);
int xsz=(Nx+1)*sp+50; //730
ysz=(Ny+1)*sp+20; //300
gpos=xsz-240;
MP.setBounds(25,gap,xsz,ysz);
MP.setBackground(Color.white);
add(GraP);
GraP.setBounds(gpos,ysz+2*gap,242,242);
GraP.setBackground(Color.white);
add(key);
key.setBounds(gpos-190,ysz+2*gap,140,100);
key.setBackground(Color.white);
add(CP2);
CP2.setBounds(25,ysz+2*gap,240,240);
CP2.setBackground(new Color(225,225,225));
CP2.setLayout(null);
CP2.add(angtxt);angtxt.setBounds(170,5,60,20);
CP2.add(matxt);matxt.setBounds(170,35,60,20);
CP2.add(lentxt);lentxt.setBounds(170,65,60,20);
CP2.add(ktxt);ktxt.setBounds(170,95,60,20);
CP2.add(k2txt);k2txt.setBounds(170,125,60,20);
CP2.add(mmtxt);mmtxt.setBounds(170,155,60,20);
CP2.add(anglab);anglab.setBounds(5,5,150,20);anglab.setFont(sf);
CP2.add(malab);malab.setBounds(5,35,160,20);malab.setFont(sf);
CP2.add(lenlab);lenlab.setBounds(5,65,160,20);lenlab.setFont(sf);
CP2.add(klab);klab.setBounds(5,95,160,20);klab.setFont(sf);
CP2.add(k2lab);k2lab.setBounds(5,125,160,20);k2lab.setFont(sf);
CP2.add(mmlab);mmlab.setBounds(5,155,160,20);mmlab.setFont(sf);
CP2.add(but1);but1.setBounds(85,210,70,25);
CP2.add(logcb);logcb.setBounds(125,180,110,20);lincb.setFont(sf);
CP2.add(lincb);lincb.setBounds(5,180,110,20);logcb.setFont(sf);
initpos();
setTs();
}

void initpos(){
int i,j;
double Elow;
kdt=k*dt;k2dt=k2*dt;Emax=0;
logscale=logcb.getState();
for (i=0;i<=Nx;i++) for (j=0;j<=Ny;j++){
  mas[i][j]=1+0.1*Math.random();
  angv[i][j]=ang*(Math.random()-0.5);
  theta[i][j]=Math.PI;
  Emax+=sqr(angv[i][j])/2;}
Elow=Emax/(Nx*Ny);
Emax+=mm*sqr(Mangv*len)/2;
if (logscale){
  if (Elow<1e-6) Elow=1e-6;
  Emax*=2;Elow/=2;
  p10=Math.floor(Math.log(Elow)/Math.log(10.0));
  scale=240/Math.log(Emax/Elow);}
else {
  Emax*=1.1;scale=240/Emax;
  p10=Math.floor(Math.log(Emax)/Math.log(10.0));}
Mtheta=Math.PI;
}

String threadInfo(){
int act=Thread.activeCount();
String result=Integer.toString(act)+"\n";
Thread[] tarr=new Thread[act];
Thread.enumerate(tarr);
for (int i=0;i<act;i++) result=result+tarr[i].toString()+"\n";
return result;
}

void setTs(){
matxt.setText(Double.toString(Mangv));
lentxt.setText(Double.toString(len));
angtxt.setText(Double.toString(ang));
mmtxt.setText(Double.toString(mm));
ktxt.setText(Double.toString(k));
k2txt.setText(Double.toString(k2));

}

void getValues(){
Mangv=strToD(matxt.getText());
len=strToD(lentxt.getText());
ang=strToD(angtxt.getText());
mm=strToD(mmtxt.getText());
k=strToD(ktxt.getText());
k2=strToD(k2txt.getText());
initpos();
}

public void step(){
int i,j;
double cor;
for (i=0;i<Nx;i++) for (j=0;j<=Ny;j++){
   angv[i][j]=angv[i][j]-kdt*Math.sin(theta[i][j]-theta[i+1][j])/mas[i][j];
   angv[i+1][j]=angv[i+1][j]-kdt*Math.sin(theta[i+1][j]-theta[i][j])/mas[i+1][j];}
for (i=0;i<=Nx;i++) for (j=0;j<Ny;j++){
   angv[i][j]=angv[i][j]-kdt*Math.sin(theta[i][j]-theta[i][j+1])/mas[i][j];
   angv[i][j+1]=angv[i][j+1]-kdt*Math.sin(theta[i][j+1]-theta[i][j])/mas[i][j+1];}
for (i=0;i<=Nx;i++) for (j=0;j<=Ny;j++){
  angv[i][j]=angv[i][j]+g*dt*Math.sin(theta[i][j])-k2dt*Math.sin(theta[i][j]-Mtheta)/mas[i][j];
  Mangv=Mangv-k2dt*Math.sin(Mtheta-theta[i][j])/(mm*len*len);
  theta[i][j]=theta[i][j]+angv[i][j]*dt;}
Mangv=Mangv+g*dt*Math.sin(Mtheta)/len;
Mtheta=Mtheta+Mangv*dt;
}

public void start(){
int act,i;
requestFocus();
for (ct=0;ct<240;ct++){yp1[ct]=0;yp2[ct]=0;yp3[ct]=0;yp4[ct]=0;yp5[ct]=0;yp6[ct]=0;}
ct=0;
if (timer == null) {
tct++;
timer = new Thread(this,Integer.toString(tct));timer.start();
repaint();
}}

public void stop() {
if (timer!=null){timer.stop();timer = null;}}

public void paint(Graphics g){
double t,ts;
int y;
g.setFont(sf);
t=Math.pow(10,p10);
if (logscale) t*=10;
else if (Emax/t<2) t/=4;
ts=t;
while (t<Emax){
  if (logscale)y=(int)(scale*Math.log(Emax/t));
  else y=240-(int)(scale*t);
  y+=ysz+2*gap;
  g.drawString(Double.toString(t),gpos-38,y+5);
  g.drawLine(gpos-5,y,gpos,y);
  if (logscale)t*=10;else t+=ts;}
}

public void actionPerformed(ActionEvent e){
String act=e.getActionCommand();
if (act.equals("Restart")){stop();getValues();initpos();start();}
}

public void run() {
int j,i,ms;
double energy,Etot,Eav;
ms=20;
Thread myThread = Thread.currentThread();
while (timer!=null) {
  try {Thread.sleep(ms);} catch (InterruptedException e){}
  for (j=0;j<ms;j++) step();//1 step per millisecond
  Etot=0;
  energy=mm*sqr(Mangv*len)/2+mm*g*(1+Math.cos(Mtheta));
  for (i=0;i<=Nx;i++) for (j=0;j<=Ny;j++)
    Etot=Etot+sqr(angv[i][j])/2+g*(1+Math.cos(theta[i][j]));
  Eav=Etot/(Nx*Ny);
  Etot=Etot+energy;
  if (logscale){
  yp1[ct]=(int)(scale*Math.log(Emax/energy));
  yp3[ct]=(int)(scale*Math.log(Emax/Etot));
  yp5[ct]=(int)(scale*Math.log(Emax/Eav));}
  else{
  yp1[ct]=240-(int)(scale*energy);
  yp3[ct]=240-(int)(scale*Etot);
  yp5[ct]=240-(int)(scale*Eav);}
  ct++;
  if (ct==240) {ct=0;
   System.arraycopy(yp1,0,yp2,0,240);
   System.arraycopy(yp3,0,yp4,0,240);
   System.arraycopy(yp5,0,yp6,0,240);}
  MP.repaint();GraP.repaint();}
timer = null;}

public String getAppletInfo() {return "Coupled Pendulums";}

public String[][] getParameterInfo() {return null;}

public class MainPanel  extends Canvas{
public void paint(Graphics g){
int x,y,i,j;
for (i=0;i<=Nx;i++) for (j=0;j<=Ny;j++){
  x=150+sp/2+sp*i*5/6;y=sp/2+sp*j;
  g.drawLine(x,y,(int)Math.round(x+pl*Math.sin(theta[i][j])),(int)Math.round(y-pl*Math.cos(theta[i][j])));}
x=(int)Math.round(75+len*pl*Math.sin(Mtheta));
y=(int)Math.round(sp/2-len*pl*Math.cos(Mtheta));
g.setColor(new Color(255,128,128));
g.drawLine(75,sp/2,x,y);
g.setColor(new Color(255,192,192));
g.drawLine(76,sp/2,x+1,y);
g.drawLine(74,sp/2,x-1,y);
}
}

public class keyPanel extends Canvas{
public void paint(Graphics g){
g.drawString("Kinetic Energy",10,17);
g.setFont(sf);
g.setColor(Color.blue);
g.drawLine(5,35,30,35);
g.drawString("Total",35,40);
g.setColor(Color.red);
g.drawLine(5,55,30,55);
g.drawString("Large mass",35,60);
g.setColor(Color.black);
g.drawLine(5,75,30,75);
g.drawString("Av. for small masses",35,80);
}
}

public class GPanel  extends Canvas{
public void paint(Graphics g){
g.setColor(palegrey);
g.drawPolyline(xp,yp6,240);
g.setColor(paleblue);
g.drawPolyline(xp,yp4,240);
g.setColor(palered);
g.drawPolyline(xp,yp2,240);
g.setColor(Color.red);//new Color(255,0,0));
g.drawPolyline(xp,yp1,ct);
g.setColor(Color.blue);
g.drawPolyline(xp,yp3,ct);
g.setColor(Color.black);
g.drawPolyline(xp,yp5,ct);
}
}

}

