//////////////////////////////////////////////////////
// This applet created by the Java Code Generator
// of the IDEA Project at Washington State University
// Partially funded by the National Science Foundation
// Copyright 1998 by Kevin Cooper
//////////////////////////////////////////////////////

//////////////////////////////////////////////////////
// modified by F. Arenou (Sept 99): sky plot of a binary orbit
// $Revision: 1.8 $
//////////////////////////////////////////////////////


import java.applet.*;
import java.awt.*;
import java.io.*;
import java.lang.*;
import java.net.*;
import java.util.*;



/////////////////////////////////////////////////////////////////////////////
public class binary extends Applet implements Runnable {
/////////////////////////////////////////////////////////////////////////////
	
	
    // Class Variables:
    //  nVar        - number of variables in equations
    //  nPar        - number of parameters in equations
    //  fV[]        - variable values
    //  sV[]        - variable name strings
    //  fP[]        - parameter values
    //  sP[]        - parameter name strings
    //  fUL[]       - coordinates of upper left corner
    //  fLR[]       - coordinates of lower right corner
    //  fTBnd[]     - limits of display in horizontal
    //  fVBnd[]     - limits of display in vertical dir.
    //  fBound[]    - limits of display to pass to Plot2d
    //  nWind       - number of plot windows
    //  wnd[]       - plot windows
    //  SB          - the box where parameter values are set
    //  BB          - the box where axis limits are set
    //  iVertDiv    - the number of divisions in the gbl
    //  gbl         - the grid bag layout for the applet
    //  Traj        - contains all trajectories computed

    int nVar=3;
    float [] fV = new float[nVar];
    float [] fInitialCond = new float[nVar];
    String [] sV = new String[nVar];
    int nPar=11;
    float [] fP = new float[nPar];
    String [] sP = new String[nPar];
    float [] fUL = new float[2];
    float [] fLR = new float[2];
    float [] fTBnd = new float[2];
    float [] fVBnd = new float[2];
    float [] fBound = new float[2*(nVar+1)];
    Dimension d;
    int nWind=3;
    Plot2d [] wnd = new Plot2d[nWind];
    SetBox SB;
    BoundBox BB;
    Label Filler;
    int iVertDiv=9;
    public GridBagLayout gbl;
    Vector Traj;
    
    double pi=3.1415926535897, DegToRad=pi/180.0;
    double sinOmg, cosOmg, cosI, cosE, efac, nPeriod, w, a0, a1, a2 ; 
    float R0, R1, R2 ;   
    Color C1, C2 ;
    int NbPt = 200 ;
  
   private Thread animate;
   private long delay=1000; 
   boolean isStopped=true ;
   


  /////////////////////////////////////////////////////////////////////////////
  // Applet info.
  /////////////////////////////////////////////////////////////////////////////
  public String getAppletInfo() {
	return "Binary $Revision: 1.8 $, by Frederic.Arenou@obspm.fr, based on the IDEA java code generator (Copyright 1998 kcooper@math.wsu.edu)";
  }



  /////////////////////////////////////////////////////////////////////////////
  // Parameter info.
  /////////////////////////////////////////////////////////////////////////////
  public String[][] getParameterInfo() {
    String[][] info = {
	{"P (days)", 	"float", 	"period"},
	{"T (days)", 	"float", 	"time of periastron passage"},
	{"e", 		"float", 	"excentricity"},
	{"o1 (deg)", 	"float", 	"argument of periastron"},
	{"i (deg)", 	"float", 	"inclination"},
	{"O (deg)", 	"float", 	"position angle of the node"},
	{"M1 (Msun)", 	"float", 	"mass of primary"},
	{"M2 (Msun)", 	"float", 	"mass of secondary"},
	{"H1 (Mag)", 	"float", 	"absolute magnitude of primary"},
	{"H2 (Mag)", 	"float", 	"absolute magnitude of secondary"},
	{"pi (mas)", 	"float", 	"parallax"},
    } ;
    return info;
  }


  /////////////////////////////////////////////////////////////////////////////
  // just initializes names of initial values of parameters and variables
  /////////////////////////////////////////////////////////////////////////////
  public void binary() {

    double a1pa2 ;
    float GrLimit, PeLimit, TminLimit ;
    
    double [] fP39903 = { 899.2761,1845.6018,0.1117,136.49,21.81,186.85,
			  1.265,0.578,3.37,6.15,48.96 } ;

    String params[][] = getParameterInfo();
    String PassedParam ;
    
    // use parameters from star HIP 39903 by default 
    // (if no parameters passed from html source)
    for (int i=0; i < nPar; i++) {
	sP[i] = params[i][0] ; 
	PassedParam = getParameter(sP[i]); 
	if (PassedParam == null) fP[i] = (float)fP39903[i] ; 
	else fP[i] = (float)Double.valueOf(PassedParam).doubleValue();
	
    }

    a1pa2=Math.pow((fP[6]+fP[7])*Math.pow(fP[0]/365.25,2.),1./3.) ;
    GrLimit = (float)(((int)(a1pa2*100.+1))/100.) ; // alf,del limit
    PeLimit = (float)((int)(4.*fP[0]+.5)*1.) ; // 2 period
//    TminLimit = (float)((int)(fP[1]/10.+.5)*10.) ; // T0 truncated
TminLimit = 0; 
    sV[0] = "t (days)";
    sV[1] = "alfa* (AU)";
    sV[2] = "delta (AU)";
    fBound[0] = TminLimit;
    fBound[1] = TminLimit+PeLimit;
    fBound[2] = -GrLimit;
    fBound[3] = GrLimit;
    fBound[4] = -GrLimit;
    fBound[5] = GrLimit;
  }



float [] pos= {0,0,0} ;
float t=0 ;

 
  /////////////////////////////////////////////////////////////////////////////
  // 
  /////////////////////////////////////////////////////////////////////////////
  public void start()
  {
      if ((animate==null)||(!animate.isAlive())) { animate = new Thread(this); }
      animate.start(); isStopped = false ; 
  }


  /////////////////////////////////////////////////////////////////////////////
  // 
  /////////////////////////////////////////////////////////////////////////////
  public void run()
  {
      double E, Mean, v ;
      	
      while (Thread.currentThread() == animate)
      {
         try { 
	   if (!isStopped) {
		isStopped = false ; Thread.sleep(delay); 
		Graphics G = getGraphics() ; 
		wnd[0].Reset();	wnd[1].Reset(); wnd[2].Reset();	

		float deltat=fP[0]/60 ; // one period in one minute

		Mean=nPeriod*(t-fP[1]) ; E=Mean ;
		for(int j=0; j<10; j++){ E=Mean+fP[2]*Math.sin(E) ; }
		v = 2*Math.atan2(efac*Math.sin(E/2.),Math.cos(E/2.)) + w ; 
		cosE = Math.cos(E) ; 
		t += deltat ; pos[0] = t ; 
		orbPos(a1,v,pos) ; 
		G.setColor(C1) ; myCircle(G,t,pos[1],pos[2],R1) ;
		orbPos(a2,v+pi,pos) ; 
		G.setColor(C2); myCircle(G,t,pos[1],pos[2],R2) ;
		orbPos(a0,v,pos) ; 
		G.setColor(Color.white) ; myCircle(G,t,pos[1],pos[2],R0) ;
	   }
         }
         catch (InterruptedException e) { e.printStackTrace();}
      }
  }


  /////////////////////////////////////////////////////////////////////////////
  // 
  /////////////////////////////////////////////////////////////////////////////
  public void stop()
  {
      if ((animate != null) && (animate.isAlive())) { 
	  animate.stop(); isStopped = true ; 
      }
  }


  /////////////////////////////////////////////////////////////////////////////
  // 
  /////////////////////////////////////////////////////////////////////////////
  public void destroy()
  {
      animate = null;
  }
 


  /////////////////////////////////////////////////////////////////////////////
  // initializes windows, limits
  /////////////////////////////////////////////////////////////////////////////
  public void init() {

    binary(); 
    
    // set up layout
    setBackground(Color.white) ; 
    d = size();	
    gbl = new GridBagLayout();
    setLayout(gbl);
    GridBagConstraints gbc = new GridBagConstraints();
    gbc.fill = GridBagConstraints.BOTH;

    // set up boxes of controls
    SB = new SetBox(nPar,sP,fP,d,iVertDiv,gbl,this);
    BB = new BoundBox(3,sV,fBound,d,iVertDiv,gbl,this);

    // create display windows
    fUL[0] = fBound[2];
    fLR[0] = fBound[3];
    fLR[1] = fBound[4];
    fUL[1] = fBound[5];
    wnd[0] = new Plot2d(nVar,1,2,fUL,fLR);
    SB.add(wnd[0],gbl,gbc,0,0,3,iVertDiv-1,100,100);
    fUL[0] = fBound[0];
    fLR[0] = fBound[1];
    fLR[1] = fBound[2];
    fUL[1] = fBound[3];
    wnd[1] = new Plot2d(nVar,0,1,fUL,fLR);
    SB.add(wnd[1],gbl,gbc,3,0,4,3,100,100);
    fLR[1] = fBound[4];
    fUL[1] = fBound[5];
    wnd[2] = new Plot2d(nVar,0,2,fUL,fLR);
    SB.add(wnd[2],gbl,gbc,3,4,4,4,100,100);
    Filler = new Label("alpha*(t) above, delta(t) below");
    Filler.setBackground(Color.black);
    Filler.setForeground(Color.white);
    SB.add(Filler,gbl,gbc,2,3,2,1,70,20);

    // initialize the trajectory array
    Traj = new Vector(10,10);
    SetOrbTab() ; 
  }



  /////////////////////////////////////////////////////////////////////////////
  // handle clicks
  /////////////////////////////////////////////////////////////////////////////
  public boolean action(Event evt, Object arg) {
 
    int iInd; boolean flag=true ;
    
    // clear all displays and trajectories
    if (arg.equals("Clear")){
        Traj.removeAllElements(); if (!isStopped) stop() ; t=0;
        
    }
    // test for button actions
    if (SB.action(evt,arg)) {
        for(iInd=0;iInd<nPar;iInd++) fP[iInd] = SB.GetVal(iInd); checkParam() ;

	// next, handle bounding box stuff
        BB.SetVal();
        for(iInd=0;iInd<2*(1+nVar);iInd++) fBound[iInd] = BB.GetVal(iInd);
	SetOrbTab() ; 
        fTBnd[0] = fBound[0];
        fTBnd[1] = fBound[1];
        fVBnd[0] = fBound[2];
        fVBnd[1] = fBound[3];
        wnd[1].SetBound(fTBnd,fVBnd);
        fVBnd[0] = fBound[4];
        fVBnd[1] = fBound[5];
        wnd[2].SetBound(fTBnd,fVBnd);
        fTBnd[0] = fBound[2];
        fTBnd[1] = fBound[3];
        wnd[0].SetBound(fTBnd,fVBnd);

        Graphics G = getGraphics() ; paint(G) ; if (isStopped) start() ;
        
    }
    else{
        flag=super.action(evt,arg);
    }
    return true;
  }
  
  

  /////////////////////////////////////////////////////////////////////////////
  // draw a trajectory
  /////////////////////////////////////////////////////////////////////////////
  public void update(Graphics g) {
  
    Trajectory T = (Trajectory) Traj.lastElement();
    wnd[0].PlotOrbit(g,T);
    wnd[1].PlotOrbit(g,T);
    wnd[2].PlotOrbit(g,T);
  }



  /////////////////////////////////////////////////////////////////////////////
  // handle mouse down calls
  /////////////////////////////////////////////////////////////////////////////
  public boolean mouseDown(Event evt, int iX, int iY) {
  
    int iInd=-1;
    
    for(int i=0;i<nWind;i++) { if(wnd[i].IsInRect(iX,iY)) iInd = i; }
    if(iInd >= 0){
        fV = wnd[iInd].GetPhaseCoords(iX,iY);
        repaint();
    }

    return true;
  }
  
  

  /////////////////////////////////////////////////////////////////////////////
  // handle mouse movements
  /////////////////////////////////////////////////////////////////////////////
  public boolean mouseMove(Event e, int iX, int iY) {
  
    String sVal = " " ;
    
    for(int i=0;i<nWind;i++){
      if (wnd[i].IsInRect(iX,iY)) { sVal = wnd[i].VariableValue(iX,iY,sV); }
    }
    SB.SetString(sVal);
    Graphics g = getGraphics();
    SB.paint(g);
    BB.paint(g);
    
    return true;
  }

  
  
  /////////////////////////////////////////////////////////////////////////////
  // the obligatory paint function
  /////////////////////////////////////////////////////////////////////////////
  public void paint(Graphics g) {

    int iInd;
    
    for(iInd=0;iInd<nPar;iInd++) fP[iInd] = SB.GetVal(iInd); checkParam() ;
    wnd[0].Reset();
    wnd[0].PlotAllOrbits(g,Traj);
    wnd[1].Reset();
    wnd[1].PlotAllOrbits(g,Traj);
    wnd[2].Reset();
    wnd[2].PlotAllOrbits(g,Traj);
  }
  
  
  /////////////////////////////////////////////////////////////////////////////
  // creates the trajectory of primary, secondary and photocentre
  /////////////////////////////////////////////////////////////////////////////
  public void SetOrbTab() {
  
    float M, M1, M2, MinRadius ;
    double a1pa2, E, Mean, v ;
    float fLower = fBound[0];
    float fUpper = fBound[1];
    float fStepSize = (fUpper-fLower)/NbPt ;
    float [] tab = new float[nVar] ;
    
    M1 = fP[6] ; M2 = fP[7] ; M = M1+M2 ;

    // semi-major axis of primary secondary and photocentre
    a1pa2 = Math.pow(M*Math.pow(fP[0]/365.25,2.),1./3.) ;
    a1 = a1pa2*M2/M ; a2 = a1pa2-a1 ; 
    a0 = a1pa2*(M2/M-1./(1.+Math.pow(10.,0.4*(fP[9]-fP[8])))) ;
    
    // radius and color of components from mass
    MinRadius=(float)(0.002*Math.sqrt(fBound[3]-fBound[2])*(fBound[5]-fBound[4])) ;
    R1=(float)(0.004652*Math.pow((double)M1,3.9/5.2)) ;
    if (R1 < MinRadius) R1 = MinRadius ;
    R2=(float)(0.004652*Math.pow((double)M2,3.9/5.2)) ;
    if (R2 < MinRadius) R2 = MinRadius ;
    R0=(float)(0.004652*Math.pow((double)(M1+M2),3.9/5.2)) ;
    if (R0 < MinRadius) R0 = MinRadius ;
    C1 = starColor(M1) ; C2 = starColor(M2) ; 

    sinOmg = Math.sin(fP[5]*DegToRad); 
    cosOmg = Math.cos(fP[5]*DegToRad);
    cosI = Math.cos(fP[4]*DegToRad); 
    w = fP[3]*DegToRad ;
    nPeriod = (2.*pi/fP[0]) ; 
    efac = Math.sqrt((1.+fP[2])/(1.-fP[2]));

    Trajectory TA = new Trajectory(nVar);
    Trajectory TB = new Trajectory(nVar);
    Trajectory TP = new Trajectory(nVar);
    for(float t=fLower; t<=fUpper; t+=fStepSize) {
	Mean=nPeriod*(t-fP[1]) ; E=Mean ;
	for(int j=0; j<10; j++){ E=Mean+fP[2]*Math.sin(E) ; }
	v = 2*Math.atan2(efac*Math.sin(E/2.),Math.cos(E/2.)) + w ; 
	cosE = Math.cos(E) ; tab[0] = t ; 
	orbPos(a1,v,tab) ; TA.addElement(tab) ;
	orbPos(a2,v+pi,tab) ; TB.addElement(tab) ;
	orbPos(a0,v,tab) ; TP.addElement(tab) ; 
    }
    TA.trimToSize() ; TA.setColor(C1) ; Traj.addElement(TA);
    TB.trimToSize() ; TB.setColor(C2) ; Traj.addElement(TB);
    TP.trimToSize() ; TP.setColor(Color.white) ; Traj.addElement(TP);
  }


  /////////////////////////////////////////////////////////////////////////////
  // coordinates of current orbit points
  /////////////////////////////////////////////////////////////////////////////
  public void orbPos(double a, double Q, float [] coo ) {
  
    double r,sinQ,cosQ ;

    sinQ = Math.sin(Q); cosQ = Math.cos(Q);
    r = a*(1. - fP[2]*cosE);
    coo[1] = (float)(r*(cosQ*sinOmg + sinQ*cosOmg*cosI)) ;
    coo[2] = (float)(r*(cosQ*cosOmg - sinQ*sinOmg*cosI)) ;
  }
  
  

  /////////////////////////////////////////////////////////////////////////////
  // color vs temperature (well, in fact it is vs mass) 
  /////////////////////////////////////////////////////////////////////////////
  public static Color starColor(float M) {
      
    float R, G, B, sun=1, white=2, maxM=8, minM=(float)0.08 ;
    
    if (M > maxM) { R=(float)0. ; G=(float)0.; B=(float)1; }
    else if (M > white) { R=(float)(1.-(M-white)/(maxM-white));G=R;B=(float)1;} 
    else if (M > sun) { R=(float)1; G=(float)1; B=(float)((M-sun)/(white-sun));}
    else if (M > minM) { R=(float)1;G=(float)((M-minM)/(sun-minM));B=(float)0;} 
    else { R=(float)0.8 ; G=(float)0; B=(float)0; } // brown dwarfs
	
    return new Color(R,G,B) ;
  }
  
  

  /////////////////////////////////////////////////////////////////////////////
  // verify parameter bounds
  /////////////////////////////////////////////////////////////////////////////
  public void checkParam() {
      
    if (fP[2] < 0) fP[2]=0 ; if (fP[2] > (float)0.999999) fP[2]=(float)0.999999 ; 
    while (fP[3] < 0) fP[3]+=360 ; while (fP[3] > 360) fP[3]-=360 ;
    while (fP[4] < 0) fP[4]+=180 ; while (fP[4] > 180) fP[4]-=180 ;
    while (fP[5] < 0) fP[5]+=360 ; while (fP[5] > 360) fP[5]-=360 ;
    if (fP[6] < (float)0.000001) fP[6]=(float)0.000001 ; 
    if (fP[7] < (float)0.000001) fP[7]=(float)0.000001 ; 
    if (fP[8] < (float)-10) fP[8]=(float)-10 ; 
    if (fP[9] < (float)-10) fP[9]=(float)-10 ; 
    if (fP[10] < (float)0.001) fP[9]=(float)0.001 ; 
  }
    
  

  /////////////////////////////////////////////////////////////////////////////
  // draw a circle then clear it
  /////////////////////////////////////////////////////////////////////////////
  public void myCircle(Graphics G, float t, float x, float y, float r) {

    float [] tab= new float[3] ; float [] oldtab= new float[3] ;
    double theta=0, radius ;
   
    tab[0]=t ; oldtab[0]=tab[0] ; tab[1]=x+r ; tab[2]=y ; radius=r ;
    
    for (int i=0; i<=14; i++) {
        oldtab[1]=tab[1] ; oldtab[2]=tab[2] ; 
        theta += 2*pi/7 ; 
    	tab[1]=x+(float)(radius*Math.cos(theta)) ; 
    	tab[2]=y+(float)(radius*Math.sin(theta)) ; 
	wnd[0].drawLine(G,oldtab,tab) ; 
	wnd[1].drawLine(G,oldtab,tab) ; 
	wnd[2].drawLine(G,oldtab,tab) ; 
    }
    theta=0; tab[0]=t ; oldtab[0]=tab[0] ; tab[1]=x+r ; tab[2]=y ;
    for (int i=0; i<=14; i++) {
        oldtab[1]=tab[1] ; oldtab[2]=tab[2] ; 
        theta += 2*pi/7 ; if (i%2 == 1) radius=r ; else radius=0. ;
    	tab[1]=x+(float)(radius*Math.cos(theta)) ; 
    	tab[2]=y+(float)(radius*Math.sin(theta)) ; 
	wnd[0].drawLine(G,oldtab,tab) ; 
	wnd[1].drawLine(G,oldtab,tab) ; 
	wnd[2].drawLine(G,oldtab,tab) ; 
    }
  }
}

