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

abstract public class PhysicsApplet extends Applet
                                              implements Runnable {

    //  data arrays on a 2-d lattice:   0 .. Nx ,  0 .. Ny
  int Nx, Ny;
  int pixelsize;
  int halfpix;
  int xsize, ysize;   // actual size of applet's main panel
  int leftmargin;
  String title;
  TitleBanner Title;
  Panel Picture;
  Panel Controls;
  int mouseMode;
  Thread solver;
  int delay = 0;
  boolean timetostop;  

  PhysicsApplet(String thetitle, int nx, int ny, int Pixelsize, 
                                                      int extraxsize){
    Nx = nx;  Ny = ny;
    pixelsize = Pixelsize;
    halfpix = pixelsize/2;
    xsize = extraxsize + (nx+4)*pixelsize;
    ysize = (ny+4)*pixelsize;
    leftmargin = extraxsize/2 + 1;
    title = thetitle;
    mouseMode = 0;
    timetostop = true;
  }

  public void init(){
    setLayout(new BorderLayout(0,0));
    Title = new TitleBanner(title,24);
    add(Title,"North");
    Picture = new Panel();
    Picture.setBackground(Color.white);
    Picture.setLayout(new BorderLayout(0,10));
    buildPicture();
    add(Picture,"Center");
    Controls = new Panel();
    buildControls();
    add(Controls,"South");
  }


     //  Thread stuff

  public void startThread(){
     if (solver == null){
       timetostop = false;
       solver = new Thread(this);
       solver.start();
     }
  }
  
  public void stopThread(){
      timetostop = true; 
       /*  check for this condition in the loop of run() or solve()  */
  }

  public void run(){
      solve();
      solver = null;
  }
      
  public void pause(){
      try{ solver.sleep(delay); } catch (InterruptedException e){;}
  }

    /*          classes for creating a GUI:

   ---------    TitleBanner       ----------------------  */  

  public class TitleBanner extends Component{
    
    String mytitle;
    Font TitleFont;
    FontMetrics fm;
    int Fontsize;

    TitleBanner(String title, int fontsize){
      mytitle = title;
      Fontsize = fontsize;
      TitleFont = new Font("SansSerif",Font.PLAIN,fontsize);
      setBackground(Color.white);
      setFont(TitleFont);
      //      setSize(xsize,Fontsize+20);
      fm = getFontMetrics(TitleFont); 
   }
   
    public void paint(Graphics g){
      int width = fm.stringWidth(mytitle);
      int x = (xsize - width)/2;
      g.drawString(mytitle,x,Fontsize + 5);
    }

    public void write(String S){
      mytitle = S;
      repaint();
    }

      // sizing

   public Dimension getSize(){
       return new Dimension(xsize, Fontsize+20);
   }

   public Dimension getPreferredSize(){
       return getSize();
   }

   public Dimension getMinimumSize(){
       return getSize();
   }

   public Dimension getMaximumSize(){
       return getSize();
   }
  }

    /*  ---------    ArrayDisplay       ----------------------  */
  
  class ArrayDisplay extends Component 
                     implements MouseListener, MouseMotionListener {

     double[][]  A;
     int[][]  ID;
   // ID describes the state of each grid point on the lattice
     Color Color1, Color2;
     Color[] DisplayColors;
     Color[] altDisplayColors;

     public ArrayDisplay(double[][]  AA, int[][] id,
           Color COLOR1, Color COLOR2, Color MAXDisplayColor, Color BASEalt){
       A = AA;
       ID = id;
       Color1 = COLOR1;
       Color2 = COLOR2;
       DisplayColors = new Color[11];
       altDisplayColors = new Color[11];
       int dR = 255 - MAXDisplayColor.getRed();
       int dG = 255 - MAXDisplayColor.getGreen();
       int dB = 255 - MAXDisplayColor.getBlue();
       int BRa = 255 - BASEalt.getRed();
       int BGa = 255 - BASEalt.getGreen();
       int BBa = 255 - BASEalt.getBlue();
       int dRa = ((255-BRa)* dR)/2550;
       int dGa = ((255-BGa)* dG)/2550;
       int dBa = ((255-BBa)* dB)/2550;
       for (int m = 0; m<= 10; m++){
         DisplayColors[m] = new Color(255-m*(dR/10),
                                               255-m*(dG/10),255-m*(dB/10));
         altDisplayColors[m] = new Color(255 - (BRa + dRa * m),
                        255 - (BGa + dGa * m), 255 - (BBa + dBa * m));
      }
      addMouseListener(this);
      addMouseMotionListener(this);
     }

    public void paint(Graphics g){
       g.setColor(Color.black);
       g.fillRect(leftmargin,0,2*pixelsize,(Ny+3)*pixelsize); 
       g.fillRect(leftmargin,0,(Nx+3)*pixelsize, 2*pixelsize); 
       g.fillRect(leftmargin,(Ny+1)*pixelsize,(Nx+3)*pixelsize,
                                              2*pixelsize); 
       g.fillRect(leftmargin+(Nx+1)*pixelsize, 0,2*pixelsize,
                                              (Ny+3)*pixelsize); 
       for (int i = 1; i <=  Nx-1; i++){
         for(int j = 1; j <= Ny-1; j++){
           paintCell(g,i,j);
         }
       }
       int hp = pixelsize/2;
       int nnx = Nx/10;
       int nny = Ny/10;
       g.setColor(Color.black);
       for(int i = 1 ; i <= nnx -1; i++){
         for (int j = 1; j <= nny -1; j++){
           g.fillRect(leftmargin + (10*i + 1)* pixelsize + hp, 
                                     (10*j+1)*pixelsize + hp, 2, 2);
         }
       }
   }

   public void update(Graphics g){
     paint(g);
   }

   public void refresh(){
     Graphics g = getGraphics();
     paint(g);
     g.dispose();
   }

   void paintCell(Graphics g, int i, int j){
      Color c = findColor(A[i][j],ID[i][j]);
      g.setColor(c);
      g.fillRect(leftmargin+(i+1)*pixelsize, (j+1)*pixelsize,pixelsize,
                                                      pixelsize);
   }

    //  mouse stuff

   public void mouseClicked(MouseEvent e){
     int i = (e.getX()-leftmargin)/pixelsize -1;
     int j = e.getY()/pixelsize -1;
     if ( i > 0 && i < Nx-1 && j > 0 && j < Ny-1){
       writeFieldValue(A[i][j], mouseMode);
     }
   }

   public void mouseDragged(MouseEvent e){
     int i = (e.getX()-leftmargin)/pixelsize -1;
     int j = e.getY()/pixelsize -1;
     if ( i > 1 && i < Nx-1 && j > 1 && j < Ny-1){
       Graphics g = getGraphics();
       for (int k = -1; k <= 1; k++){
	  for (int l = -1; l <= 1; l++){
            int ik = i + k;
            int jl = j + l;
            setArrays(ik,jl, mouseMode);
            paintCell(g,ik,jl);
          }
        }
     g.dispose();
     }
  }
        
   public void mousePressed(MouseEvent e){}
   public void mouseReleased(MouseEvent e){}
   public void mouseEntered(MouseEvent e){}
   public void mouseExited(MouseEvent e){}
   public void mouseMoved(MouseEvent e){}

      // sizing

   public Dimension getSize(){
       return new Dimension(xsize, ysize);
   }

   public Dimension getPreferredSize(){
       return getSize();
   }

   public Dimension getMinimumSize(){
       return getSize();
   }

   public Dimension getMaximumSize(){
       return getSize();
   }

 }
 
    /*  ---------    VectorDisplay       ----------------------  */


  class VectorDisplay extends Component
                     implements MouseListener, MouseMotionListener {

      /* the grid square (i,j) has its uppper left-hand corner 
          at  (leftmargin + i*pixelsize, (Ny-j+1)*pixelsize)
          and its center at  
	(leftmargin + i*pixelsize + halfp, (Ny-j+1)*pixelsize + halfpix) */

     double[][]  Bx;
     double[][]  By;
     int[][]  ID;
   // ID describes the state of each grid point on the lattice
     double Bscale;
     Color Color1, Color2;

     public VectorDisplay(double[][]  BBx, double[][] BBy, int[][] id,
           double BScale, Color COLOR1, Color COLOR2){
       Bx = BBx;   By = BBy;
       ID = id;
       Bscale = BScale;
       Color1 = COLOR1;
       Color2 = COLOR2;
       addMouseListener(this);
       addMouseMotionListener(this);
     }

    public void paint(Graphics g){
       g.setColor(Color.black);
       g.fillRect(leftmargin,0,2*pixelsize,(Ny+3)*pixelsize); 
       g.fillRect(leftmargin,0,(Nx+3)*pixelsize, 2*pixelsize); 
       g.fillRect(leftmargin,(Ny+1)*pixelsize,(Nx+3)*pixelsize,
                                              2*pixelsize); 
       g.fillRect(leftmargin+(Nx+1)*pixelsize, 0,2*pixelsize,
                                              (Ny+3)*pixelsize); 
       for (int i = 1; i <=  Nx-1; i++){
         for(int j = 1; j <= Ny-1; j++){
           paintCell(g,i,j);
         }
       }
       for(int i = 10 ; i < Nx; i+= 10){
         for (int j = 10; j < Ny; j+= 10){
	   g.setColor(Color1);
           int X = (int) (Bx[i][j]/Bscale);
           int Y = (int) (By[i][j]/Bscale);
           int startx = leftmargin + (i+1)*pixelsize + halfpix - X/2;
           int starty = (Ny + 1 - j)*pixelsize + halfpix + Y/2;
           int endx = startx + X;
           int endy = starty - Y;
           g.setColor(Color1);
           g.drawLine(startx, starty, endx, endy);
           g.setColor(Color2);
           g.fillRect(endx-1, endy-1, 3,3);
           g.setColor(Color.black);
           g.fillRect(leftmargin + (i + 1)* pixelsize + halfpix-1, 
                                   (Ny+1-j)*pixelsize + halfpix-1, 3, 3);
         }
       }
     }

   public void update(Graphics g){
     paint(g);
   }

   public void refresh(){
     Graphics g = getGraphics();
     paint(g);
     g.dispose();
   }

   void paintCell(Graphics g, int i, int j){
      Color c = findColor(1.0,ID[i][j]);
      g.setColor(c);
      g.fillRect(leftmargin+(i+1)*pixelsize, (Ny+1-j)*pixelsize,pixelsize,
                                                      pixelsize);
   }

   public void resetscale(double Bsc){
     Bscale = Bsc;
   }

    //  mouse stuff

   public void mouseClicked(MouseEvent e){
     int i = (e.getX()-leftmargin)/pixelsize -1;
     int j = (Ny+2) - e.getY()/pixelsize;
     if ( i > 0 && i < Nx-1 && j > 0 && j < Ny-1){
       writeVectorValue(Bx[i][j], By[i][j],mouseMode);
     }
   }

   public void mouseDragged(MouseEvent e){
     int i = (e.getX()-leftmargin)/pixelsize -1;
     int j = (Ny+2) - e.getY()/pixelsize;
     if ( i > 1 && i < Nx-2 && j > 1 && j < Ny-2){
       Graphics g = getGraphics();
       for (int k = -1; k <= 1; k++){
	  for (int l = -1; l <= 1; l++){
            int ik = i + k;
            int jl = j + l;
            setArrays(ik,jl, mouseMode);
            paintCell(g,ik,jl);
          }
        }
     g.dispose();
     }
  }
        
   public void mousePressed(MouseEvent e){}
   public void mouseReleased(MouseEvent e){}
   public void mouseEntered(MouseEvent e){}
   public void mouseExited(MouseEvent e){}
   public void mouseMoved(MouseEvent e){}

      // sizing

   public Dimension getSize(){
       return new Dimension(xsize, ysize);
   }

   public Dimension getPreferredSize(){
       return getSize();
   }

   public Dimension getMinimumSize(){
       return getSize();
   }

   public Dimension getMaximumSize(){
       return getSize();
   }

 }
 
    /*  ---------    CurveDisplay       ----------------------  */

  class CurveDisplay extends Component 
                     implements MouseListener, MouseMotionListener {

     int height, zero, lift;
     double[] f;
     double[] h;
     double fscale, hscale;
     Color fcolor, hcolor;
     int lastx, lasty;
     int myMouseMode;

     public CurveDisplay(int Height, int ZeroPosition, double [] F, 
                               double[] H, double Fscale, double Hscale, 
                               Color FColor, Color HColor, int MouseMode){
       height = Height;
       zero = height + pixelsize - ZeroPosition;
       lift = height - ZeroPosition;
       f = F;
       h = H;
       fscale = Fscale;
       hscale = Hscale;
       fcolor = FColor;
       hcolor = HColor;
       myMouseMode = MouseMode;
       addMouseListener(this);
       addMouseMotionListener(this);
     }

     public void resetfscale(double fsc){
       fscale = fsc;
     }

     public void resethscale(double hsc){
       hscale = hsc;
     }

    public void paint(Graphics g){
       g.setColor(Color.black);
       g.fillRect(leftmargin,0,pixelsize,height+2*pixelsize); 
       g.fillRect(leftmargin,0,(Nx+2)*pixelsize, pixelsize); 
       g.fillRect(leftmargin, height + pixelsize,(Nx+2)*pixelsize,
                                              pixelsize); 
       g.fillRect(leftmargin+(Nx+1)*pixelsize, 0,pixelsize,
                                                   height+2*pixelsize); 
       g.drawLine(leftmargin, zero, leftmargin + (Nx+1)*pixelsize, zero);
       for (int i = 1; i <  Nx; i++){
           paintCell(g,i);
       }
   }

   public void update(Graphics g){
     paint(g);
   }

   public void refresh(){
     Graphics g = getGraphics();
     paint(g);
     g.dispose();
   }

   void paintCell(Graphics g, int i){
      int left = leftmargin + i*pixelsize;
      g.setColor(Color.white);
      g.fillRect(left,pixelsize,pixelsize,height);
      g.setColor(Color.black);
      g.drawLine(left,zero,left+pixelsize,zero);
      g.setColor(fcolor);
      int flstep = (int) (f[i]*lift/fscale);
      int frstep = (int) (f[i+1]*lift/fscale);
      g.drawLine(left, zero - flstep, left + pixelsize, zero - frstep);
      g.setColor(hcolor);
      int hlstep = (int) (h[i]*lift/hscale);
      int hrstep = (int) (h[i+1]*lift/hscale);
      g.drawLine(left, zero - hlstep, left + pixelsize, zero - hrstep);
   }

    //  mouse stuff

   public void mousePressed(MouseEvent e){
     if (mouseMode != myMouseMode) return;
     lastx = e.getX();
     lasty = e.getY();
   }

   public void mouseDragged(MouseEvent e){
     if (mouseMode != myMouseMode) return;
     int newx = e.getX();
     int newy = e.getY();
     int lasti = (lastx - leftmargin)/pixelsize -1;
     double lastf = ((zero -lasty) * fscale)/lift;
     int newi = (newx - leftmargin)/pixelsize - 1;
     double newf = ((zero- newy) * fscale)/lift;
     double finc = (newf - lastf)/(newi - lasti);
     Graphics g = getGraphics();
     if (newi > lasti){
       if (lasti < 0) { 
           lastf += - finc * lasti;
           lasti = 0;
       }
       if (newi > Nx){
           newf -= finc * (newi - Nx);
           newi = Nx;
       }
       double ff = lastf;
       for (int i = lasti+1; i <= newi; i++){
           ff += finc;
           f[i] = ff;
           paintCell(g,i-1);
       }
    } else if ( newi < lasti) { 
       if (lasti > Nx) { 
           lastf -= finc * (lasti - Nx);
           lasti = Nx;
       }
       if (newi < 0){
           newf += - finc * newi;
           newi = 0;
       }
       double ff = lastf;
       for (int i = lasti-1; i >= newi; i--){
           ff += finc;
           f[i] = ff;
           paintCell(g,i+1);
       }
     }
     lastx = newx;
     lasty = newy;
   }
        
   public void mouseClicked(MouseEvent e){}
   public void mouseReleased(MouseEvent e){}
   public void mouseEntered(MouseEvent e){}
   public void mouseExited(MouseEvent e){}
   public void mouseMoved(MouseEvent e){}

      // sizing

   public Dimension getSize(){
       return new Dimension(xsize, height + 3*pixelsize);
   }

   public Dimension getPreferredSize(){
       return getSize();
   }

   public Dimension getMinimumSize(){
       return getSize();
   }

   public Dimension getMaximumSize(){
       return getSize();
   }
 }

    /*  ---------    PlotDisplay       ----------------------  */


   class PlotDisplay extends Component{

    int plotxsize, plotysize;
    int maxplotsize = 2000;
    int plotleftmargin = 20;   // extra margins for the plotter
    int plotbottommargin = 20;    
    int dotoffset = 0;        
       // set dotoffset = 1 to move plotted points by 1 pixel in x and y

    double xa, xb, ya, yb;                 // plot limits in x and y
    double xticksize, yticksize;       //   tick spacing in x and y 
    double xpixel, ypixel;               //  value of one pixel in x and y
    boolean xticksint,  yticksint;
    Font plotfont;
    int tick = 5;     //   tick  size
    int bigtick = 10;    //  major tick size
    int bar = 3;          //   half-width of error bars
    double TINY = 1.0e-30;
    int whichColor;
    Image theplot;
    Graphics theplotg;
	
    public PlotDisplay(double Xa, double Xb, double Ya, double Yb,
		       double Xtick, double Ytick){
      xa = Xa; xb = Xb; ya = Ya; yb = Yb;
      if ((xa >= xb) || (ya >=yb))  
        System.out.println("  PlotDisplay assumes xa < xb, ya < yb ");
      plotxsize = Nx * pixelsize;
      plotysize = Ny * pixelsize;
      if ((plotxsize > maxplotsize) || (plotysize > maxplotsize))
        System.out.println(
         " Please choose a size less than the maximum plot size of "+
                      maxplotsize+" . ");
      setTicks(Xtick,Ytick);
      xpixel = (xb-xa)/plotxsize;
      ypixel = (yb-ya)/plotysize;
      plotfont = new Font("SansSerif",Font.PLAIN,16);
      whichColor = 0;
        /*  plot is an abstract method of the PhysicsApplet class  */
    }

    public void paint(Graphics g){
      theplot = createImage(plotxsize+100,plotysize+100);
      theplotg = theplot.getGraphics();
      theplotg.setColor(Color.white);
      theplotg.fillRect(0, 0, plotxsize+100, plotysize+100);
      theplotg.setColor(Color.black);
      drawRect(theplotg, xa,ya,xb,yb);
      makexTicks(theplotg);
      makeyTicks(theplotg);
      setplotClip(theplotg);
      plot(theplotg);     
      g.drawImage(theplot,0,0,plotxsize+100,plotysize+100,this);
    }

    public void refresh(){
      Graphics g = getGraphics();
      g.drawImage(theplot,0,0,plotxsize+100,plotysize+100,this);
    }
	
    public void update(Graphics g){
      refresh();
    }

    public void renew(){
     Graphics g = getGraphics();
     paint(g);
     g.dispose();
    }

    public void setTicks(double xtick, double ytick){
      xticksize = xtick;   yticksize = ytick;
      xticksint = false;   yticksint = false;
      int xt = (int) (5.0* xticksize);
      int yt = (int) (5.0 * yticksize);
      if (5.0*xticksize - xt < 1.0e-10)  xticksint = true;
      if (5.0*yticksize - yt < 1.0e-10)  yticksint = true;
    }
	
    void setplotClip(Graphics g){
      ppoint p1 = new ppoint(xa,yb);
      ppoint p2 = new ppoint(xb, ya);
      g.setClip(p1.x, p1.y, p2.x-p1.x, p2.y-p1.y);
    }
	
    public void drawLine(Graphics g, 
              double x1, double y1, double x2, double y2){
      ppoint p1 = new ppoint(x1,y1);
      ppoint p2 = new ppoint(x2,y2);
      g.drawLine(p1.x, p1.y, p2.x, p2.y);
    }

    void verticalAxis(Graphics g, double X){
      ppoint p1 = new ppoint(X, ya);
      ppoint p2 = new ppoint(X, yb);
      g.drawLine(p1.x, p1.y, p2.x, p2.y);
    }
	
    void verticalAxis(Graphics g){
      ppoint p1 = new ppoint(0.0, ya);
      ppoint p2 = new ppoint(0.0, yb);
      g.drawLine(p1.x, p1.y, p2.x, p2.y);
    }
	
    void horizontalAxis(Graphics g, double Y){
      ppoint p1 = new ppoint(xa,Y);
      ppoint p2 = new ppoint(xb,Y);
      g.drawLine(p1.x, p1.y, p2.x, p2.y);
    }

    void horizontalAxis(Graphics g){
      ppoint p1 = new ppoint(xa,0.0);
      ppoint p2 = new ppoint(xb,0.0);
      g.drawLine(p1.x, p1.y, p2.x, p2.y);
    }
	
    void drawRect(Graphics g, double x1, double y1, double x2, double y2){
      ppoint p1 = new ppoint(x1,y1);
      ppoint p2 = new ppoint(x2,y2);
      g.drawRect(p1.x,p2.y, p2.x - p1.x, p1.y-p2.y);
    }
	
    void drawString(Graphics g, String str, double x, double y){
      ppoint p = new ppoint(x,y);
      g.drawString(str, p.x, p.y);
    }
	
    void plotPoint(Graphics g, 
               double X, double Xerr, double Y, double Yerr){
      ppoint p = new ppoint(X,Y);
      g.fillOval(p.x-2, p.y-2,5,5);
      if (Xerr > 0.0){
        ppoint pleft = new ppoint(X-Xerr,Y);
        ppoint pright = new ppoint(X+Xerr,Y);
        g.drawLine(pleft.x, pleft.y,pright.x, pright.y);
        g.drawLine(pleft.x,pleft.y-bar,pleft.x, pleft.y+bar);
        g.drawLine(pright.x,pright.y-bar,pright.x, pright.y+bar);
      }
      if (Yerr > 0.0){
        ppoint pdown = new ppoint(X, Y - Yerr);
        ppoint pup = new ppoint(X, Y+Yerr);
        g.drawLine(pdown.x, pdown.y ,pup.x, pup.y);
        g.drawLine(pdown.x-bar,pdown.y,pdown.x+bar, pdown.y);
        g.drawLine(pup.x-bar,pup.y,pup.x+bar, pup.y);
      }
    }  
	
    void makexTicks(Graphics g){
      int np = (int) (xb/xticksize);
      int nm = (int) (xa/xticksize);
      g.setFont(plotfont);
      FontMetrics FM = getFontMetrics(plotfont);
      int labelheight = FM.getAscent();
      for (int i = nm ; i <= np ; i++){
        if ((i/5)*5 == i){           // larger tick
          double place = i*xticksize;
          drawLine(g, place, ya,place,  ya + bigtick*ypixel);
          drawLine(g, i*xticksize, yb, i*xticksize, yb - bigtick*ypixel);
          String Pl = ""+place;
          if (xticksint) Pl = ""+ ((int) place);
          int labellength = FM.stringWidth(Pl);
          drawString(g, Pl, place - 0.5*labellength*xpixel,
                                        ya - (labelheight+5)*ypixel);
        }  else {
          drawLine(g, i*xticksize, ya, i*xticksize,  ya + tick*ypixel);
          drawLine(g, i*xticksize, yb, i*xticksize,  yb - tick*ypixel);
        }
      }
    }
	
    void makeyTicks(Graphics g){
      int np = (int) (yb/yticksize);
      int nm = (int) (ya/yticksize);
      g.setFont(plotfont);
      FontMetrics FM = getFontMetrics(plotfont);
      int labelheight = FM.getAscent();
      for (int i = nm ; i <= np ; i++){
        if ((i/5)*5 == i){           // larger tick
          double place = i*yticksize;
          drawLine(g, xa, place, xa + bigtick*xpixel, place); 
          drawLine(g, xb, place, xb - bigtick*xpixel, place); 
          String Pl = ""+place;
          if (yticksint) Pl = ""+ ((int) place);
          int labellength = FM.stringWidth(Pl);
          drawString(g, Pl, xa - (labellength+5)*xpixel, 
                                 place - 0.5 * labelheight*ypixel);
        } else {
         drawLine(g, xa, i*yticksize, xa + tick*xpixel, i*yticksize); 
          drawLine(g, xb, i*yticksize, xb - tick*xpixel, i*yticksize); 
        }
      }
    }
	
    public void plotPoints(Graphics g, plotStream PS){
      for (int i = 0; i< PS.size(); i++){
        plotPoint(g, PS.x(i), PS.xerr(i), PS.y(i), PS.yerr(i));
      }
    }
	
    public void plotLines(Graphics g, plotStream PS){
      ppoint p1 = new ppoint(PS.x(0),PS.y(0));
      int p1x = p1.x; int p1y = p1.y;
      for (int i = 1; i< PS.size(); i++){
        ppoint p2 = new ppoint(PS.x(i),PS.y(i));
        int p2x = p2.x;  int p2y = p2.y;
        drawLine(g, p1x,p1y,p2x,p2y);
        p1x = p2x;  p1y = p2y;
      }
    }

    public void plotHistogram(Graphics g, plotStream PS){
      int n = PS.size();
      double[]  limits = new double[n+1];
      double[]  values = new double[n+1];
      double x0, x1;
      x0 = PS.x(0);
      x1 = PS.x(1);
      limits[0] = x0 - 0.5 * (x1-x0);
      x1 = x0;
      for (int i = 1; i < n; i++){
        x0 = x1;
        x1 = PS.x(i);
        values[i-1] = PS.y(i-1);
        limits[i] = 0.5*(x0 + x1);
      }
      values[n-1] = PS.y(n-1);
      limits[n] = x1 + 0.5 * (x1 - x0);
      values[n] = ya;
      drawLine(g, limits[0],ya,limits[0],values[0]);
      for (int j = 0; j < n; j++){
        drawLine(g, limits[j], values[j], limits[j+1], values[j]);
        drawLine(g,limits[j+1], values[j], limits[j+1], values[j+1]);
      }
    }
	
    public void plotCurve(Graphics g, plotStream PS){
        // construct spline interpolators for x and y as a function of s
      Dspline DS = new Dspline(PS);
      double send = DS.end();
  // walk along the curve; whenever the pixel changes, lay down a short line
       double sinc = 1.1;
       double s = 0.0;
      ppoint p = new ppoint(DS.x(0.0), DS.y(0.0));
      int px = p.x;
      int py = p.y;
      while (s < send){
        s +=  sinc;
        p.reset(DS.x(s),DS.y(s));
        int pxn = p.x;
        int pyn = p.y;
        if ( (pxn != px )|| (pyn != py) ){
          g.drawLine(px,py,pxn,pyn);
          px = pxn;
          py = pyn;
        }
      }
    }

    public void switchColor(Graphics g){
      whichColor++;
      switch(whichColor){
        case 1:
         g.setColor(Color.green);
          break;
         case 2:
          g.setColor(Color.red);
          break;
        case 3:
          g.setColor(Color.blue);
          break;
        case 4:
          g.setColor(Color.orange);
          break;
        case 5:
          g.setColor(Color.cyan);
          break;
        case 6:
          g.setColor(Color.magenta);
          break;
        case 7:
          g.setColor(Color.gray);
          break;
        case 8:
          g.setColor(Color.pink);
          break;
        case 9:
          g.setColor(Color.black);
          whichColor = 0;
          break;
        default:
          break;
       }
    }
      // sizing

     public Dimension getSize(){
       return new Dimension(xsize + plotleftmargin, ysize + plotbottommargin);
     }

     public Dimension getPreferredSize(){
       return getSize();
     }

     public Dimension getMinimumSize(){
       return getSize();
     }

     public Dimension getMaximumSize(){
       return getSize();
     }
    // ppoint class used by PlotDisplay
	 	
   class ppoint{
      int x, y;
		
      ppoint(double X, double Y){
        reset(X,Y);
      }
	 	
      void reset(double X, double Y){
        x = leftmargin+plotleftmargin + (int) ((X-xa)/xpixel + TINY);
        y = plotysize + plotbottommargin - (int) ((Y-ya)/ypixel + TINY);
      }
    }

    // Dspline class used by PlotDisplay
	 	
  class Dspline{     
          // spline interpolation in x and y as a function of s

    double []  xvals;
    double []  yvals;
    double []  svals;
    double []  x2vals;
    double []  y2vals;
    double send;
    int n;
    int lastx, lasty;
	
    Dspline(plotStream PS){
      n = PS.size();
      xvals = new double[n];
      yvals = new double[n];
      svals = new double[n];   //  path length in pixels
      x2vals = new double[n];   
      y2vals = new double[n]; 
               //  second derivatives, used in spline interpolation
      xvals[0] = PS.x(0);
      yvals[0] = PS.y(0);
      svals[0] = 0.0;
      for (int i = 1; i < n ; i++){
        xvals[i] = PS.x(i);
        yvals[i] = PS.y(i);
        svals[i] = svals[i-1]  + Math.sqrt(
               (xvals[i] - xvals[i-1])*(xvals[i]-xvals[i-1])/(xpixel*xpixel)
             + (yvals[i]-yvals[i-1])*(yvals[i]-yvals[i-1])/(ypixel*ypixel));
     }
     send = svals[n-1];
   /*   realization of the splines is based on that in Numerical Recipes, 
    	    	   by Press, Teukolsky, Vetterling, and Flannery     */
     double [] ux = new double[n];
     double [] uy = new double[n];
     x2vals[0] = 0.0;
     y2vals[0] = 0.0;
     ux[0] = 0.0;
     uy[0] = 0.0;
     for (int i = 1 ;  i < (n-1); i++){
       double sig = (svals[i] - svals[i-1])/(svals[i+1] - svals[i-1]);
       double px =  sig * x2vals[i-1] + 2.0;
       x2vals[i] = (sig - 1.0)/px;
       double py =  sig * y2vals[i-1] + 2.0;
       y2vals[i] = (sig - 1.0)/py;
       ux[i] = (xvals[i+1] - xvals[i])/(svals[i+1] - svals[i])  
    	    -   (xvals[i] - xvals[i-1])/(svals[i] - svals[i-1]);
       ux[i] = (6.0 * ux[i]/(svals[i+1] - svals[i-1])  - sig * ux[i-1])/px;
       uy[i] = (yvals[i+1] - yvals[i])/(svals[i+1] - svals[i])  
    	      -   (yvals[i] - yvals[i-1])/(svals[i] - svals[i-1]);
       uy[i] = (6.0 * uy[i]/(svals[i+1] - svals[i-1])  - sig * uy[i-1])/py;
     }
     x2vals[n-1] = 0.0;
     for (int k = n-2; k >= 0; k--){
         x2vals[k] = x2vals[k]* x2vals[k+1] + ux[k];
         y2vals[k] = y2vals[k]* y2vals[k+1] + uy[k];
     }
     lastx = 0;
     lasty = 0;
   }
	
   double end(){
      return send;
   }
	
 /*   the plotter uses only an increasing series of values of s, 
        so search accordingly for the interval in which to interpolate    */
   double x(double s){
    while (s > svals[lastx+1]  && s < send)  lastx++;
    double h = svals[lastx+1] - svals[lastx];
    double a = (svals[lastx+1] - s)/h;
    double b = (s - svals[lastx])/h;
    double xs = a * xvals[lastx] + b * xvals[lastx+1] 
      + ( (a*a*a - a)*x2vals[lastx] + (b*b*b - b)*x2vals[lastx+1])* h*h/6.0;
    return xs;
  }
    		
  double y(double s){
    while (s > svals[lasty+1] && s < send)  lasty++;
    double h = svals[lasty+1] - svals[lasty];
    double a = (svals[lasty+1] - s)/h;
    double b = (s - svals[lasty])/h;
    double ys = a * yvals[lasty] + b * yvals[lasty+1] 
      + ( (a*a*a - a)*y2vals[lasty] + (b*b*b - b)*y2vals[lasty+1])* h*h/6.0;
    return ys;
  }
 }

 }


    // plotStream class used by PlotDisplay  

  class plotStream{

    Vector XX;
    Vector YY;
    Vector XXerr;
    Vector YYerr;
	
    plotStream(){
      XX = new Vector();
      YY = new Vector();
      XXerr = new Vector();
      YYerr = new Vector();
    }
	
    public int size(){
      return XX.size();
    }
	 
    public void add(double X, double Y){
      add( X, 0.0, Y, 0.0);
    }		

    public void add(double X, double Y, double Yerr){
      add(X, 0.0, Y, Yerr);
    }		

    void add(double X, double Xerr, double Y, double Yerr){
      XX.addElement(new Double(X));
      YY.addElement(new Double(Y));
      XXerr.addElement(new Double(Xerr));
      if (Xerr < 0.0)  
         System.out.println("  The error on x should be positive.");
      YYerr.addElement(new Double(Yerr));
      if (Yerr < 0.0)  
         System.out.println("  The error on y should be positive.");
    }		

    double x(int i){
      return ((Double) XX.elementAt(i)).doubleValue();
    }
	
    double y(int i){
      return ((Double) YY.elementAt(i)).doubleValue();
    }

    double xerr(int i){
      return ((Double) XXerr.elementAt(i)).doubleValue();
    }
	
    double yerr(int i){
      return ((Double) YYerr.elementAt(i)).doubleValue();
    }
  }


    /*  ---------    Buttons and Scrollbars   ----------------------  */


  class CommandButton extends Button{

    int Code;

    CommandButton(String name, int CommandCode){
      super(name);
      Code = CommandCode;
      addActionListener(new ActionListener(){
       public void actionPerformed(ActionEvent e){  
          doAction(Code); mouseMode = 0;}
      });
    }
  }
    
  class ModeButton extends Button{

    int Code;

    ModeButton(String name, int ModeCode){
      super(name);
      Code = ModeCode;
      addActionListener(new ActionListener(){
       public void actionPerformed(ActionEvent e){  
          mouseMode = Code; }
      });
    }
  }
    
  class BoldLabel extends Label{

   BoldLabel(String text){
      super(text);
      setFont(new Font("SansSerif",Font.PLAIN,12));
   }
  }
    
  class PhysicsScrollbar extends Scrollbar{

     int Code;
     double Scale;

     PhysicsScrollbar(double Value, double Minimum, double Maximum,
                                                     int CommandCode){
       super(Scrollbar.HORIZONTAL);
       double logmax = Math.log(Maximum);
       double logmin = Math.log(Minimum);
       Scale = (logmax - logmin)/200.0;
       double logval = Math.log(Value);
       int val = (int) (logval/Scale);
       int min = (int) (logmin/Scale);
       int max = (int) (logmax/Scale);
       setValues(val, 0, min, max);
       Code = CommandCode;
       addAdjustmentListener(new AdjustmentListener(){
          public void adjustmentValueChanged(AdjustmentEvent e){
                doAction(Code);
       } });
     }
    
     double getReading(){
       int val = super.getValue();
       double value = Math.exp(val * Scale);
       return value;
     }
  }

  abstract void buildPicture();
  abstract void resetArrays();
  abstract void resetAll();
  abstract void refreshPicture();
  abstract void buildControls();
  abstract Color findColor(double A, int S);
  abstract void writeFieldValue(double Val, int mode);
  abstract void writeVectorValue(double Vx, double Vy, int mode);
  abstract void setArrays(int i, int j, int mode);
  abstract void doAction(int Code);
  abstract void solve();
  abstract void plot(Graphics g);
}























  
