import java.awt.*;
import java.awt.event.*;
import java.applet.Applet; 
import java.awt.image.*; 


/**
 * Class Duffing_Orbit - oscilloscope-like display of the duffing-oscillator's orbit y(t)
 * created with BlueJ
 * 
 * @author (Martin Lieberherr) 
 */
public class Duffing_Orbit extends Applet implements ActionListener, Runnable
{  
    // instance variables and attributes  (all private)
    int nx = 800;       // number of pixels in x-direction for picture
    int ny = 400;        //  number of pixels in y-direction for picture
    // d2y/dt^2 + delta*dy/dt + alpha*y + beta*y^3 = gamma*cos(omega*t) 
    double delta = 0.1;     // coefficient of damping (prop. velocity)
    double alpha=1.0;       // coefficient of linear force-term
    double beta=0.0;    // coefficient of cubic force term
    double gamma = 0.1;     // coefficient of forcing
    double omega = 0.333;           // angular frequency of forcing
    
    double yMax = 1.0;                // vertical window display -yMax..+yMax
    double tMin = 0.0;          // horizontal window display tMin..tMax
    double tMax = 100;          //  horizontal window display tMin..tMax
    double yStart = 0;              // start position for orbit y(t)
    int stepsPerCycle = 1000;     // steps per cycle for Runge-Kutta integrator 
    
    boolean mouseFlag = false;      // true if mouse is clicked in graphical display
    int mouse_i = 1;                 // position relative to graphical display where clicking occurred
    
    
    Label titleLabel;           // displays "Orbit y(t) of forced Duffing-oscillator"
    Label authorLabel;           // displays author-name and date
    Label alphaLabel;           // displays "alpha:"
    TextField alphaField;       // display/input coefficient alpha.
    Label betaLabel;            // displays "beta:"
    TextField betaField;        // display/input coefficient beta
    Label gammaLabel;            // displays "gamma:"
    TextField gammaField;        // display/input coefficient gamma
    Label deltaLabel;            // displays "delta:"
    TextField deltaField;        // display/input coefficient delta
    Label omegaLabel;            // displays "omega:"
    TextField omegaField;        // diplay/input omega
    Label yNullLabel;              // displays "y=0"
    TextField yMaxField;          // display/input yMax
    Label yMinLabel;              // displays yMin (= - yMax)
    Label timeLabel;          // displays "time"
    TextField tMinField;      // dispay/input tMin
    TextField tMaxField;        // display/input tMax
    Label stepsLabel;           // display "steps:"
    TextField stepsField;       // display/input stepsPerCycle
    Label yStartLabel;                // displays "y(0):"
    TextField yStartField;                // display/input y(0)
   
    Thread thread;              // loop to numerically integrate the movement
    
    // buffered image for display of positions
    BufferedImage  BuffImage1 = new BufferedImage(nx, ny, BufferedImage.TYPE_INT_RGB);
    Graphics g1 = BuffImage1.createGraphics(); 
    
    
 
     /**
     * Called by the browser or applet viewer to inform this Applet that it
     * has been loaded into the system. It is always called before the first 
     * time that the start method is called.
     */
    public void init()
    {   
        setLayout(null);  // for "hard" layout in pixels
        
        int xText = 10;  // position of next element on window
        int yText = 10;  // position of next element on window
        
        yMaxField = new TextField(""+yMax);
        yMaxField.setBounds(xText, yText, 80, 20);
        add(yMaxField);
        yMaxField.addActionListener(this);
        
        yText = ny/2;
        yNullLabel = new Label("y = 0");
        yNullLabel.setBounds(xText, yText, 40, 20);
        add(yNullLabel);
                                                      
        yText = ny-10;
        yMinLabel = new Label("-"+yMax);
        yMinLabel.setBounds(xText,yText,80,20);
        add(yMinLabel);
        
        xText = 100;
        yText = 20+ny;
        tMinField = new TextField(""+tMin);
        tMinField.setBounds(xText, yText, 100, 20);
        add(tMinField);
        tMinField.addActionListener(this);
        
        xText = xText+10+nx/2-20;
        timeLabel = new Label("time");
        timeLabel.setBounds(xText, yText, 40, 20);
        add(timeLabel);
        
        xText = 100+nx-100;
        tMaxField = new TextField(""+tMax);
        tMaxField.setBounds(xText, yText, 100, 20);
        add(tMaxField);
        tMaxField.addActionListener(this);
        
        xText = 100;
        yText = yText + 40;
        titleLabel = new Label("Orbit of forced Duffing-oscillator");
        titleLabel.setBounds(xText, yText, 300, 20);// setBounds(x_left, y_top, width, height) for display
        add(titleLabel);  //added to ContentPane to the right of picture 
        
        xText = 100+nx-300;
        authorLabel = new Label("Martin Lieberherr, September 9th, 2011");
        authorLabel.setBounds(xText, yText, 300, 20);
        add(authorLabel);
       
        xText = 100; 
        yText = yText+30;
        alphaLabel = new Label("alpha:");
        alphaLabel.setBounds(xText, yText, 40, 20);  // setBounds(x_left, y_top, width, height) for display
        add(alphaLabel);             //added to ContentPane to the right of picture
        xText=xText+10+40;
        alphaField = new TextField(""+alpha);    
        alphaField.setBounds(xText, yText, 150, 20);
        add(alphaField);
        alphaField.addActionListener(this); // ActionListener is called if "enter" is pressed in TextField
        
        xText = xText+200;
        betaLabel = new Label("beta:");
        betaLabel.setBounds(xText, yText, 40, 20);
        add(betaLabel);
        xText = xText+10+40;
        betaField = new TextField(""+beta);
        betaField.setBounds(xText, yText, 150, 20);
        add(betaField);
        betaField.addActionListener(this);
        
        xText = xText+200;
        gammaLabel = new Label("gamma:");
        gammaLabel.setBounds(xText, yText, 55, 20);
        add(gammaLabel);
        xText = xText+10+60;
        gammaField = new TextField(""+gamma);
        gammaField.setBounds(xText, yText, 150, 20);
        add(gammaField);
        gammaField.addActionListener(this);
        
        xText = 100;
        yText = yText+30;
        deltaLabel = new Label("delta:");
        deltaLabel.setBounds(xText, yText, 40, 20);
        add(deltaLabel);
        xText = xText+10+40;
        deltaField = new TextField(""+delta);
        deltaField.setBounds(xText, yText, 150, 20);
        add(deltaField);
        deltaField.addActionListener(this);
        
        xText = xText+190;
        omegaLabel = new Label("omega:");
        omegaLabel.setBounds(xText, yText, 50, 20);
        add(omegaLabel);
        xText = xText+10+50;
        omegaField = new TextField(""+omega);
        omegaField.setBounds(xText, yText, 150, 20);
        add(omegaField);
        omegaField.addActionListener(this);
        
        xText = xText+ 200;
        yStartLabel = new Label("y(0):");
        yStartLabel.setBounds(xText, yText, 60, 20);
        add(yStartLabel);
        xText = xText+10+60;
        yStartField = new TextField(""+yStart);
        yStartField.setBounds(xText, yText, 150, 20);
        add(yStartField);
        yStartField.addActionListener(this);
        
        xText = 100;
        yText = yText+30;
        stepsLabel = new Label("steps:");
        stepsLabel.setBounds(xText, yText, 40, 20);
        add(stepsLabel);
        xText = xText+10+40;
        stepsField = new TextField(""+stepsPerCycle);
        stepsField.setBounds(xText, yText, 150, 20);
        add(stepsField);
        stepsField.addActionListener(this);
        
        thread = new Thread(this); // thread is a new thread of this applet
        thread.start(); // start the thread
    }  // end init ()
 
  
    public void destroy()
    {
        thread = null; // set thread to zero if applet is stopped
    } 

    
    /** update()-method for applet
     *  (overridden to avoid flicker)
     *  @param g  the Grapics object for this applet
     */
    public void update( Graphics g )  {
        //display buffered image with oscilloscope-trace
        g.drawImage( BuffImage1, 100, 10, null);
    } // end update()
    
    
    /**
     * Paint method for applet.
     * 
     * @param  g   the Graphics object for this applet
     */
    public void paint(Graphics g)
    {
        update( g );
    } // end paint()

    
    
    
     /**
     * ActionListener Interface method.
     * Called when action events occur with registered components that
     * can fire action events.
     * @param  ae   the ActionEvent object created by the event
     */
    public void actionPerformed(ActionEvent evt)
    {
        // if a value for alpha is entered
        if (evt.getSource() == alphaField) {
            try { 
                alpha = Double.parseDouble(alphaField.getText()); 
                alphaField.setText(""+alpha);
            }
               
            catch (NumberFormatException e) {
                alphaField.setText(""+alpha); // System.exit(0);
                 }
            }
        
       // if a value for beta is entered
        if (evt.getSource() == betaField) {
            try { 
                beta = Double.parseDouble(betaField.getText()); 
                betaField.setText(""+beta);
                }
            catch (NumberFormatException e) {
                betaField.setText(""+beta); // System.exit(0);
                 }
            }
        
       // if a value for gamma is entered
        if (evt.getSource() == gammaField) {
            try {
                gamma = Double.parseDouble(gammaField.getText()); 
                gammaField.setText(""+gamma);
                }
            catch (NumberFormatException e) {
                gammaField.setText(""+gamma); // System.exit(0);
                 }
            }
       
       // if a value for delta is entered
        if (evt.getSource() == deltaField) {
            try { 
                delta = Double.parseDouble(deltaField.getText()); 
                deltaField.setText(""+delta);
                }
            catch (NumberFormatException e) {
                deltaField.setText(""+delta); // System.exit(0);
                 }
            }
       
       // if a value for omega is entered
        if (evt.getSource() == omegaField) {
            try { 
                omega = Double.parseDouble(omegaField.getText()); 
                if (omega <= 0.0) {omega = 0.333;}
                omegaField.setText(""+omega);
                }
            catch (NumberFormatException e) {
                omegaField.setText(""+omega); // System.exit(0);
                 }
            }
       
       // if a value for yStart is entered
        if (evt.getSource() == yStartField) {
            try { 
                yStart = Double.parseDouble(yStartField.getText()); 
                yStartField.setText(""+yStart);
                }
            catch (NumberFormatException e) {
                yStartField.setText(""+yStart); // System.exit(0);
                 }
            }
            
       // if a value for yMax is entered
        if (evt.getSource() == yMaxField) {
            try { 
                yMax = Double.parseDouble(yMaxField.getText()); 
                if (yMax <=0) {yMax=1.0;}
                yMaxField.setText(""+yMax);
                yMinLabel.setText("-"+yMax);
                }
            catch (NumberFormatException e) {
                yMaxField.setText(""+yMax); // System.exit(0);
                 }
            }
            
       // if a value for tMin is entered
        if (evt.getSource() == tMinField) {
            try { 
                tMin = Double.parseDouble(tMinField.getText()); 
                if (tMin < 0.0) {tMin = 0.0;}
                if (tMin >= tMax) {tMax = tMin + 1.0; tMaxField.setText(""+tMax);}
                tMinField.setText(""+tMin);
                }
            catch (NumberFormatException e) {
                tMinField.setText(""+tMin); // System.exit(0);
                 }
            }
            
       // if a value for tMax is entered
        if (evt.getSource() == tMaxField) {
            try { 
                tMax = Double.parseDouble(tMaxField.getText()); 
                if (tMax <= tMin) {tMax = tMin+1.0;}
                tMaxField.setText(""+tMax);
                }
            catch (NumberFormatException e) {
                tMaxField.setText(""+tMax); // System.exit(0);
                 }
            }
            
       
                 
       // if a value for stepsPerCycle is entered
        if (evt.getSource() == stepsField) {
            try { 
                stepsPerCycle = (int) Math.round(Double.parseDouble(stepsField.getText())); 
                if (stepsPerCycle < 10) {stepsPerCycle=10;}
                stepsField.setText(""+stepsPerCycle);
                }
            catch (NumberFormatException e) {
                stepsField.setText(""+stepsPerCycle); // System.exit(0);
                 }
            }    
            
       thread = new Thread(this); // thread is a new thread of this applet
        thread.start(); // start the thread
            
    } // end actionPerformed()
    
    
    
    
  public void run(){
    double t, y, v;  // time, position and velocity in the current state
    double dt;     // time step for numerical integration
    int k;
    double t00, y0, v0, t1, dy1, dv1, t2, dy2, dv2, t3, dy3, dv3, dy4, dv4; // variables for Runge-Kutta integrator
      
    try{
        t = 0.0;
        y = yStart;
        v = 0.0;
        dt = 2.0*Math.PI/omega/stepsPerCycle;
        g1.setColor(Color.black);
        g1.fillRect(0, 0, nx, ny);
        g1.setColor(new Color( 0 , 255, 0) );
        
        // orbit for 0 <= t <= tMin is not displayd
        while (t<tMin) {
            t00 = t;
            y0 = y;
            v0 = v;
            dv1 = dt*(gamma*Math.cos(omega*t)-delta*v-(beta*y*y+alpha)*y);
            dy1 = dt*v;
            t = t00 + dt/2;
            y = y0 + dy1/2;
            v = v0 + dv1/2;
            dv2 = dt*(gamma*Math.cos(omega*t)-delta*v-(beta*y*y+alpha)*y);
            dy2 = dt*v;
            y = y0 + dy2/2;
            v = v0 + dv2/2;
            dv3 = dt*(gamma*Math.cos(omega*t)-delta*v-(beta*y*y+alpha)*y);
            dy3 = dt*v;
            t = t00 + dt;
            y = y0 + dy3;
            v = v0 + dv3;
            dv4 = dt*(gamma*Math.cos(omega*t)-delta*v-(beta*y*y+alpha)*y);
            dy4 = dt*v;
            y = y0 + (dy1+2*dy2+2*dy3+dy4)/6;
            v = v0 + (dv1+2*dv2+2*dv3+dv4)/6;
        }
        
        // orbit for tMin <= t < tMax is displayd
        while (t<tMax) {
            g1.fillRect((int) Math.round((t-tMin)/(tMax-tMin)*nx), (int) Math.round((1-y/yMax)*ny*0.5), 1, 1);
            t00 = t;
            y0 = y;
            v0 = v;
            dv1 = dt*(gamma*Math.cos(omega*t)-delta*v-(beta*y*y+alpha)*y);
            dy1 = dt*v;
            t = t00 + dt/2;
            y = y0 + dy1/2;
            v = v0 + dv1/2;
            dv2 = dt*(gamma*Math.cos(omega*t)-delta*v-(beta*y*y+alpha)*y);
            dy2 = dt*v;
            y = y0 + dy2/2;
            v = v0 + dv2/2;
            dv3 = dt*(gamma*Math.cos(omega*t)-delta*v-(beta*y*y+alpha)*y);
            dy3 = dt*v;
            t = t00 + dt;
            y = y0 + dy3;
            v = v0 + dv3;
            dv4 = dt*(gamma*Math.cos(omega*t)-delta*v-(beta*y*y+alpha)*y);
            dy4 = dt*v;
            y = y0 + (dy1+2*dy2+2*dy3+dy4)/6;
            v = v0 + (dv1+2*dv2+2*dv3+dv4)/6;
        }
        
        for (int j=1; j<10; j++) {
            k = (int) Math.round( 0.1*nx*j );
            g1.fillRect(k, ny-2, 1, 1);  // decimal markers
            g1.fillRect(k, 1, 1, 1);  // decimal markers
            k = (int) Math.round( 0.1*ny*j );
            g1.fillRect(1, k, 1, 1);  // decimal markers
            g1.fillRect(nx-2, k, 1, 1);  // decimal markers
            }
                        
        repaint();
        
       
    } // end try
    catch (Exception e) {
      System.out.println(e);
      System.exit(0);
    }//end catch
  }//end run

    
    
}
