/*
//  double well BifurcationPlot.java
/* 
	applet to construct bifurcation diagram for motion of particle
	in double-well potential
*/
import java.awt.*;
import java.awt.event.*;
import java.lang.*;
import java.applet.Applet;

public class DWBifurcationPlot extends Applet implements ActionListener
{
	DoubleWell dw1;
	VariableSet tvars;
	plotCanvas b;		//  canvas that draws bifurcation diagram
	plotCanvas pp; //canvas for phase plot
	calcThread calculate1; // suspendable thread for calculation loop.
	double fmin, fmax, xmin, xmax; //bounding values of the bifurcation map
	double pmax; //estimate of maximum momentum.
	int transienttime = 100;							//  number of periods discarded
												//  before plotting
    int width = 630, height = 440;		//  width and height of drawing canvas		
   		
	Button startButton, stopButton, resetButton, switchPlot;	// buttons to control flow
	TextField forceTF;			//  text fields for user input of force,
	TextField transienttimeTF;	//  number of drive periods to discard,
	TextField fminTF, fmaxTF;	//  range of force plotted on canvas
	TextField xminTF, xmaxTF;	//  range of x plotted on canvas
	boolean frozen = false;		//  true if user has stopped the motion
	
   public void init () {
   	 dw1 = new DoubleWell();	//  instantiate dynamical system object
   		
  	 //width = getWidth() - 20; height=getHeight() - 80;			//  set plot size
	 // set initial values of fmin, fmax, xmin, xmax:
	 fmin = 0.65; fmax = 0.69; xmin = -.7 ; xmax = -.3 ;
     dw1.setforce(fmin + .01*(fmax-fmin));	

     b = new plotCanvas(createImage(width, height), fmin, fmax, xmin, xmax );
	 b.setBackground( Color.white );				//  set white background
     b.setHLabel("force"); b.setVLabel("x"); 
     b.drawbox();
     dw1.setdt(0.20);							//  set time step
     dw1.setomega(2.*Math.PI/5.);				//  set drive frequency
     b.ghostGraphics.drawString("Double well dynamics: bifurcation diagram and phase plot", 100, 70);
     b.ghostGraphics.drawString("Equations of motion: "+dw1.getmass()+" dx/dt = p; ", 100, 110);
     b.ghostGraphics.drawString("dp/dt = d/dx (4 x^3 - 2 ("+(dw1.getA()*dw1.getA())+") x) - "
        +dw1.getdamping()+ " p/"+ dw1.getmass()+ " + " + dw1.getforce()+ 
        " cos(" + (dw1.getomega()/Math.PI) + "pi t)", 100, 140);
     b.ghostGraphics.drawString("Bifurcation plot makes a dot every driving cycle after waiting", 100, 170);
     b.ghostGraphics.drawString("the number of cycles indicated below.",  100, 200);
     b.ghostGraphics.drawString(" Fill in blanks, then hit 'new plot'; to shift force"+ 
        "amplitude fill in new force and hit RETURN", 100, 230);
     b.ghostGraphics.drawString("To add more dots, hit RETURN (with the force blank highlighted)", 100, 260);
     b.ghostGraphics.drawString("To see phase plot, click the 'phase' button", 100, 290);
     b.ghostGraphics.drawString("force = .7258 is especially interesting", 100, 320);
    
     // make canvas for phase plot     
     pmax = 1.3*Math.sqrt(xmax*xmax + xmin*xmin) * dw1.getomega()* dw1.getmass(); 
     pp = new plotCanvas(createImage(width, height), xmin, xmax, -pmax, pmax);
     pp.setHLabel("x"); pp.setVLabel("p"); pp.drawbox();
     
     
     //  put the user interface components on the applet
     Panel p = new Panel();
     startButton = new Button("new plot");
     startButton.addActionListener( this );
   	 stopButton = new Button("Stop");
     stopButton.addActionListener( this );
     switchPlot = new Button("phase");
     switchPlot.addActionListener(this);
	 resetButton = new Button("reset");
     resetButton.addActionListener( this );
     p.add( startButton );
   	 p.add( stopButton );
   	 p.add( switchPlot );
     p.add( resetButton );
     
     Panel p2 = new Panel();
     Label transienttimeprompt = new Label("number of periods before plotting:");
     transienttimeTF = new TextField (Integer.toString(transienttime), 5);
	 transienttimeTF.addActionListener( this );
	 p2.add( transienttimeprompt );
	 p2.add( transienttimeTF );
   		
   		Panel p3 = new Panel();
   		p3.setLayout(new FlowLayout());
   		Label fprompt = new Label ("force, hit RETURN:");
	   	forceTF = new TextField (6);
		forceTF.setText(Double.toString(dw1.getforce()));
		forceTF.addActionListener( this );
	  	p3.add( fprompt );
	   	p3.add( forceTF );
 		
		Panel p4 = new Panel();
    	Label fminprompt = new Label("fmin:");
    	fminTF = new TextField (5 );
		fminTF.setText(Double.toString(fmin));
    	Label fmaxprompt = new Label("fmax:");
    	fmaxTF = new TextField (5 );
		fmaxTF.setText(Double.toString(fmax));
   		p4.add( fminprompt );
    	p4.add( fminTF );
    	p4.add( fmaxprompt );
    	p4.add( fmaxTF );

		Panel p5 = new Panel();		
    	Label xminprompt = new Label("xmin:");
    	xminTF = new TextField (5 );
		xminTF.setText(Double.toString(xmin));
    	Label xmaxprompt = new Label("xmax:");
    	xmaxTF = new TextField (5);
		xmaxTF.setText(Double.toString(xmax));
    	p5.add( xminprompt );
    	p5.add( xminTF );
    	p5.add( xmaxprompt );
    	p5.add( xmaxTF );
    	
		add(b);
		add(p2);
		add(p3);
		add(p4);
		add(p5);
		add(p);	   		
		
   }						//  end of init
   public class calcThread extends Thread {
 // thread to do the calculations and make the canvas image.
      VariableSet vars, nextvars;
      boolean die=false;
   calcThread() { //constructor sets up initial values and dw1 parameters
      setPriority(Thread.MIN_PRIORITY); die=false;
      }
   public void run()	{
	  int n_var = 2, kount;
//	  double xvec[] = new double[n_var];	// xvec[0]=position, xvec[1]=velocity
	 double time;
	 showStatus("starting calcThread. frozen = " + frozen);
      b.ghostGraphics.setColor(Color.white);
      b.ghostGraphics.drawRect(0,0,width,1);
      pp.ghostGraphics.setColor(Color.yellow);

 	  for (kount=1; kount<200*25  && !die ; kount++) { //calculation loop
	  vars = dw1.getvars();			//  get DoubleWell variables
	
	  nextvars = vars;

		//  Get new position
		nextvars = dw1.nextvars(); 
		time = nextvars.gettime();
		b.ghostGraphics.drawRect(kount/10,0,0,1); b.repaint();
		dw1.setvars(nextvars);
		pp.addLine(vars.getx()[0], vars.getx()[1], nextvars.getx()[0], nextvars.getx()[1]);
		pp.repaint();
		vars = nextvars;
		
//  discard first transienttime periods at new parameter values, and
//  use fact that period is exactly 5 time units:	
		if( (time > 5*transienttime-.001)	&&
			( Math.abs( (time/5.)-Math.round(time/5.) ) < 0.01) )	{
			b.ghostGraphics.setColor(Color.blue);
			pp.ghostGraphics.setColor(Color.blue);		
			b.addPoint(dw1.getforce(), vars.getx()[0]);	// put point (f,x) on canvas
            b.repaint();
			// showStatus("plotting...");
		}  //end of if
   // try{sleep(5);} catch(InterruptedException e) {showStatus("interrupted sleep"); }
   while(frozen && !die) {try {sleep(100); } catch(InterruptedException e) {showStatus("interrupted sleep");} }
   } //end of calculation loop
   pp.repaint();
   showStatus("calcThread ends"); 
   System.out.println("time, force, x:  "+ vars.gettime() + ",   "+ '\t'+
      dw1.getforce()+",   "+ '\t' + vars.getx()[0]);
} // end of run
   }  //end of calcThread
   
   public void actionPerformed(ActionEvent evt)		//  handle user instructions
   {   System.out.println(evt.paramString()); 
      if(evt.getSource() == switchPlot) {
           if(switchPlot.getLabel() == "phase") {
              remove(b); add(pp, 0); validate(); switchPlot.setLabel("bifurc."); return;
              }
           else {remove(pp); add(b, 0); validate(); switchPlot.setLabel("phase"); return;}
      }
      if(calculate1 != null) {calculate1.die = true; 
           try {calculate1.join();} catch(InterruptedException e) {showStatus("dieing interrupted\007");} 
           //System.out.print(" threads= "+calculate1.activeCount() );
           }  // kill the thread.
      if (evt.getSource() == stopButton)	{ frozen = true;} //suspend
      else if (evt.getSource() == resetButton)	{ //  clear plotting canvas
	     fmin = 0.65; fmax = 0.69; xmin = -.7 ; xmax = -.3 ; transienttime=100;
         dw1.setforce(fmin + .01*(fmax-fmin));	
	     fminTF.setText(Double.toString(fmin));
         fmaxTF.setText(Double.toString(fmax));
	     xminTF.setText(Double.toString(xmin));
	     xmaxTF.setText(Double.toString(xmax));
	     forceTF.setText(Double.toString(dw1.getforce()));
         transienttimeTF.setText(Integer.toString(transienttime));

         b = new plotCanvas(createImage(width, height), fmin, fmax, xmin, xmax );
	     b.setBackground( Color.white );				//  set white background
         b.setHLabel("force"); b.setVLabel("x"); 
         b.drawbox(); 
         pmax = 1.3*Math.sqrt(xmax*xmax + xmin*xmin) * dw1.getomega()* dw1.getmass(); 
         pp = new plotCanvas(createImage(width, height), xmin, xmax, -pmax, pmax);
         pp.setHLabel("x"); pp.setVLabel("p"); pp.drawbox();
         remove(0); add(b,0); switchPlot.setLabel("phase"); validate();
         dw1.setvars( dw1.resetvars );			//  reset variables
	     frozen=true;
	     }
	  else if(evt.getSource() == startButton) { //remake plotCanvas with new parameters	
	     System.out.println( "getting new parameters..." );
         String s = fminTF.getText();						//  read string for fmin
         fmin = new Double(s).doubleValue() ;
         s = fmaxTF.getText();						//  read string for fmax
         fmax = new Double(s).doubleValue() ;
         s = xminTF.getText();						//  read string for xmin
         xmin = new Double(s).doubleValue() ;
         s = xmaxTF.getText();						//  read string for xmax
         xmax = new Double(s).doubleValue() ;
         frozen=false;
         b = new plotCanvas(createImage(width, height), fmin, fmax, xmin, xmax );
         b.setBackground( Color.white );				//  set white background
         b.setHLabel("force"); b.setVLabel("x"); 
         b.drawbox(); 
         pmax = 1.3*Math.sqrt(xmax*xmax + xmin*xmin) * dw1.getomega()* dw1.getmass(); 
         pp = new plotCanvas(createImage(width, height), xmin, xmax, -pmax, pmax);
         pp.setHLabel("x"); pp.setVLabel("p"); pp.drawbox();
         remove(0); add(b,0); switchPlot.setLabel("phase"); validate();
         }	
      else frozen=false;   // end of if else
        // any event requires restarting  new calculation
    String s = forceTF.getText();						//  read string for force
    dw1.setforce(new Double(s).doubleValue() );	//  convert to double
    s = transienttimeTF.getText();		//  read string for transienttime
    transienttime = new Integer(s).intValue();	//  convert to double
    showStatus("setting up new calculation thread");
   	b.repaint(); 
    calculate1 = new calcThread();
    dw1.settime(0.);
    calculate1.start();
	}					//  end of action method


class plotCanvas extends Canvas	{ // canvas for plotting data points.  
    //set xmin, xmax, ymin, ymax to control range of plot.  
    // Draw on my ghostGraphics object or call addPoint(double x, double y) to add a point.
    Graphics ghostGraphics; Image ghostImage;
	double xwidth, yheight;						//  ranges of x and y on graph
	double xmin, xmax, ymin, ymax; //bounding values of the graphed variables
	int imin, iwidth, jmax, jheight;					//  pixel values
	String HLabel = "x", VLabel = "y";
	//  constructor for plotCanvas:
	plotCanvas(Image image, double xmin, double xmax, double ymin, double ymax)	{
		super();					//  first call Canvas constructor
		setSize(image.getWidth(null), image.getHeight(null)); 
		setBackground( Color.white );
		this.xmin=xmin; this.xmax=xmax; this.ymin=ymin; this.ymax=ymax;
		ghostImage = image;
		ghostGraphics = image.getGraphics(); 
		xwidth = xmax-xmin; // horizontal width
		yheight = ymax-ymin; //vertical height
		// set imin, iwidth, jmin, jheight based on size of the canvas:
		imin   =  3*(getSize().width/20);
		iwidth =  getSize().width - (3*imin/2);
		jmax   =  getSize().height - 2*(getSize().height/20);
		jheight = getSize().height - 3*(getSize().height/20);
			
		drawbox(ghostGraphics);		//  draw the box, numbers, and labels
	}									// end of plotCanvas constructor
	
	int xtoi(double x)	{				//  converts x to screen coord i
		return Math.round( (float) (imin + iwidth*(x-xmin)/xwidth) );
	}
	
	int ytoj(double y)	{				//  converts x to screen coord j
		return Math.round( (float) (jmax - jheight*(y-ymin)/yheight) );
	}
	public void setHLabel(String HLabel) {this.HLabel = HLabel;}
	public void setVLabel(String VLabel) {this.VLabel = VLabel;}
	public void drawbox() {drawbox(ghostGraphics);}
	public void drawbox(Graphics g)	{	//  draw box, numbers, and labels for graph
		g.setColor(Color.white);
		g.fillRect(0,0,width,height);
		g.setColor(Color.black);
		g.drawRect(imin, jmax-jheight, iwidth, jheight);
			
		g.drawString(Double.toString(xmin), imin-10, jmax+15);
		g.drawString(Double.toString(xmax), imin+iwidth-10, jmax+15);
		g.drawString(HLabel, imin+iwidth/2-20, jmax+15);
		g.drawString(Double.toString(ymin), imin-30, jmax+5);
		g.drawString(Double.toString(ymax), imin-30, jmax-jheight+5);
		g.drawString(VLabel, imin-30, jmax-jheight/2+10);
	} //end of drawbox
	public void addPoint(double x, double y) {
	   ghostGraphics.drawRect(xtoi(x), ytoj( y ), 0, 0); 
	   }
	public void addLine(double x1, double y1, double x2, double y2) {
	   ghostGraphics.drawLine(xtoi(x1), ytoj(y1), xtoi(x2), ytoj(y2));
	   }
	public void update(Graphics g) {paint(g);}
	public void paint( Graphics g )	{	
       g.drawImage(ghostImage, 0, 0, null);  //display the buffer image
       }
} // end of Canvas
}						//  end of applet