//package org.opensourcephysics.sip.ch06;

import org.opensourcephysics.controls.*;
import org.opensourcephysics.frames.*;
import org.opensourcephysics.numerics.RK4;

/**
 * PoincareSpinMagApp plots a phase diagram and a Poincare map for a spinning magnet in a magnetic field
 *
 * @authir Shivakumar Jolad
 * @E mail: saj169@psu.edu
 */
public class PoincareSpinMagApp extends AbstractAnimation {
   final static double PI = Math.PI;    // defined for brevity
   PlotFrame phaseSpace = new PlotFrame("theta", "ang vel", "Phase space plot");
   PlotFrame poincare = new PlotFrame("theta", "ang vel", "Poincare plot");
   int nstep = 1000, count=0;                     // # iterations between Poincare plot , count is to skip transient points
   double lambda;   // parameter that determines the transition to chaos
   SpinMagnet spinmag = new SpinMagnet();
   RK4 odeMethod = new RK4(spinmag);  // ODE method is RK4

   /**
    * Constructs a Poincare Spin Magnet Application.
    */
   public PoincareSpinMagApp() {
      odeMethod.setStepSize(PI/nstep); // dt = PI/nsteps
      phaseSpace.setMarkerShape(0,2);  // second argument indicates a circle
      phaseSpace.setMarkerSize(1, 1); // smaller size gives better resolution
      poincare.setMarkerSize(1, 1); // smaller size gives better resolution
      poincare.setMarkerColor(0,java.awt.Color.BLUE); // I like BlUE!
      phaseSpace.setMessage("t="+0);
   }

   /**
   * Resets all parameters to their defaults.
   */
   public void resetAnimation() {
      control.setValue("theta", 0.2); // Initial  angle
      control.setValue("angular velocity", 0.6);  // Initial Angular velocity
      control.setValue("omega", 1.0);    // frequency of applied Magnetic field
      control.setValue("A", 0.6);       // amplitude
   }

   /**
    * Does a step by advancing the time by PI.
    *
    * Multiple data points are added to the phase space plot in a single step in order
    * to produce a smooth curve.
    *
    * Because the angular frequency of the external force equals two, a single data point
    * is added to the Poincare map at the end of the step.
    */
   public void doStep() {
      double state[] = spinmag.getState();   // gets the current state
      double dt=PI/nstep;
      double numstep=2*PI/(spinmag.omega*dt);
      for (int istep = 0; istep < numstep; istep++) {
         odeMethod.step(); // Increments steps by ODE method
	 count++;
         if (state[0] > PI) {
            state[0] = state[0] - 2.0*PI;
         }
         else if (state[0] < -PI) {
            state[0] = state[0] + 2*PI;
         }
         if(count>100){
	    phaseSpace.append(0,state[0], state[1]);
          }
      }
      poincare.append(1,state[0], state[1]);
      phaseSpace.setMessage("t="+decimalFormat.format(state[2]));
      poincare.setMessage("t="+decimalFormat.format(state[2]));
     // if(phaseSpace.isShowing())phaseSpace.render();
	 //if(poincare.isShowing())poincare.render();
	 poincare.repaint();
	 phaseSpace.repaint();
   }

   /**
    * Initializes the animation and clears the plots.
    */
   public void initializeAnimation() {
      double theta = control.getDouble("theta");           // initial angle
      double omega0= control.getDouble("angular velocity"); // initial angular velocity
      spinmag.omega = control.getDouble("omega");         // frequency of magnetic field
      spinmag.A = control.getDouble("A");                 // amplitude of external force
      spinmag.initializeState(new double[]{theta,omega0,0});
      lambda=Math.sqrt(2*spinmag.A/(spinmag.omega*spinmag.omega));
      System.out.println("Lambda = "+lambda);
      control.println("Lambda  ="+lambda);
      clear();
   }

   /**
    * Clears the plots.
    */
   public void clear() {
      phaseSpace.clearData();
      poincare.clearData();
      phaseSpace.render();
      poincare.render();
   }

   /**
    * Starts the Java application.
    * @param args  command line parameters
    */
   public static void main(String[] args) {
      PoincareSpinMagApp spinmag = new PoincareSpinMagApp();
	AnimationControl control = SimulationControl.createApp(spinmag);
     spinmag.setControl(control);
   }
}
