//
// Pendulum.java
// (c) Neil Gershenfeld  3/2/03
// integrates a parametrically-driven pendulum
//

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;

public class Pendulum extends JApplet implements Runnable,
   ActionListener {
   JTextField amplitude_field,frequency_field;
   final static int SIZE=500, NPTS=2000;
   double theta0=0.01, theta1=0, t=0, omega=1.5, amplitude=1, z;
   float x[] = new float[NPTS];
   float y[] = new float[NPTS];
   Thread thread;

   public void init() {
      int i;
      amplitude_field = new JTextField("1",5);
      amplitude_field.addActionListener(this);
      frequency_field = new JTextField("1.5",5);
      frequency_field.addActionListener(this);
      JLabel amplitude_label = new JLabel("Amplitude: ");
      JLabel frequency_label = new JLabel("  Frequency: ");
      ImagePanel image_panel = new ImagePanel();
      image_panel.setSize(SIZE,SIZE+SIZE/2);
      image_panel.setBackground(Color.white);
      JPanel control_panel = new JPanel();
      control_panel.add(amplitude_label);
      control_panel.add(amplitude_field);
      control_panel.add(frequency_label);
      control_panel.add(frequency_field);
      control_panel.setBackground(Color.white);
      Container contentPane = getContentPane();
      contentPane.setLayout(new BorderLayout());
      contentPane.add(image_panel,BorderLayout.CENTER);
      contentPane.add(control_panel,BorderLayout.SOUTH);
      for (i = 0; i < NPTS ; ++i) {
	      x[i] = SIZE*i/((float) NPTS);
	      y[i] = (float) (1.25*SIZE+0.2*SIZE*theta0/25);
         }      
      }

   public void start() {
      if (thread == null) {
         Thread thread = new Thread(this);
         thread.start();
         }
      }

   public void stop() {
      if (thread != null) {
         thread = null;
         }
      }
   
   public void run() {
      double l,g,A,dt;
      double k1_0,k2_0,k3_0,k4_0,k1_1,k2_1,k3_1,k4_1;
      int i;
      dt = 0.01;
      g = 1;
      l = 1;
      while (true) {
         repaint();
	 for (i = 0; i < (NPTS-1) ; ++i) {
		 y[i] = y[i+1];
	 }
	 y[NPTS-1] = (float) (1.25*SIZE+0.2*SIZE*theta0/25);
         t = t + dt;
         k1_0 = dt * theta1;
         k1_1 = dt * (-(g-amplitude*omega*omega*Math.sin(omega*t))
                   * Math.sin(theta0)/l);
         k2_0 = dt * (theta1+k1_1/2);
         k2_1 = dt * (-(g-amplitude*omega*omega*Math.sin(omega*(t+dt/2)))
                   * Math.sin(theta0+k1_0/2)/l);
         k3_0 = dt * (theta1+k2_1/2);
         k3_1 = dt * (-(g-amplitude*omega*omega*Math.sin(omega*(t+dt/2)))
                   * Math.sin(theta0+k2_0/2)/l);
         k4_0 = dt * (theta1+k3_1);
         k4_1 = dt * (-(g-amplitude*omega*omega*Math.sin(omega*(t+dt)))
                   * Math.sin(theta0+k3_0)/l);
         theta0 = theta0 + k1_0/6 + k2_0/3 + k3_0/3 + k4_0/6;
         theta1 = theta1 + k1_1/6 + k2_1/3 + k3_1/3 + k4_1/6;
	 z = amplitude*Math.sin(omega*t);
         try {Thread.sleep(10);}
         catch (InterruptedException e) { }
         }
      }
      
   class ImagePanel extends JPanel {
      public void paintComponent(Graphics g) {
         Graphics2D g2 = (Graphics2D) g;
         double x0,y0,x1,y1;
	 int i;
         x0 = SIZE/2;
         y0 = SIZE/2-z*SIZE/10;
         x1 = x0 + (SIZE/3)*Math.sin(theta0);
         y1 = y0 + (SIZE/3)*Math.cos(theta0);
         super.paintComponent(g2);
         g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
         g2.setStroke(new BasicStroke(3));
         g2.draw(new Line2D.Double(x0,y0,x1,y1));
         g2.fill(new Ellipse2D.Double(x1-15,y1-15,30,30));
	 GeneralPath path = new GeneralPath(GeneralPath.WIND_EVEN_ODD,NPTS);
         path.moveTo(x[0],y[0]);
         for (i = 1; i < (NPTS-1); ++i) {
            path.lineTo(x[i],y[i]);
         }
         g2.draw(path);  
         }
      }

   public void actionPerformed(ActionEvent event) {
      String amplitude_text = amplitude_field.getText();
      amplitude = Double.valueOf(amplitude_text).doubleValue();
      String frequency_text = frequency_field.getText();
      omega = Double.valueOf(frequency_text).doubleValue();
      }
   }
