import java.io.*;
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
import java.awt.geom.*;
import java.net.URL;
import java.text.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;

// Chapter 3 module 3 (DIVERGENCE)
public class Mod3Plot extends Panel {

    Mod3State state;
    Image im;
    Graphics imG;
    Graphics2D g2d;
    int mainWidth = 700;
    int mainHeight = 560;

    private Image plotKey;
    private int keyWidth = 25;
    //private int keyHeight = 240; // 240*1 pixels each color
    private int keyHeight = 240; // 120*2 pixels each color

    DecimalFormat dec = new DecimalFormat("#.#");
    DecimalFormat dec2 = new DecimalFormat("0.###E0");


    int yMin, yMax, yMid;
    int xMin, xMax, xMid;
    int yMin_, yMax_, xMin_, xMax_;
    int yBorder, yOffset, xBorder, xOffset;

    private static int numberPixelsPerGrid = 3;  // was 6
    int numberGridsPerDeltaX;  // should be 39/3 = 13
    int numberGridsPerDeltaY;  //        ""
    double dX, dY;
    double lookupScaler;
    //
    double[][] fVals;
    int numXgrids, numYgrids;
    private double fMax, fMin;
    //
    double[][] gradXvals, gradYvals;
    double[][] gradMagnitude;
    private double gradMax, gradMin;
    int stride = 10;  //7
    int numGradXpts;
    int numGradYpts;
    double maxLengthOverMaxMinusMin = 1;
    // maxVectorLength actually 26, but subtract minLength:
    static final double maxVectorLength = 22.0;
    static final double minVectorLength = 4.0;


    //static Color bgColor = new Color(194,235,255);
    //private static final Color bgColor = Color.lightGray;
    private static final Color bgColor = new Color(255,255,255);

    
    static double piOver4 = Math.PI/4;
    static double piOver180 = Math.PI/180.0;
    double deltaY, deltaX;
    FontMetrics fm;
    int fontHeight,ticLength;
    //static Font boldFont = new Font("Serif",Font.BOLD, 18);


    public Button small;

    private BufferedImage nabla_dot;


    public Mod3Plot(Mod3State state) {
        super();
        this.state = state;
    }

    public void initialPlot() {
        setLayout(null);
        setBackground(bgColor);

	getImages();
	
        //im = createImage(mainWidth,mainHeight);

        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gs = ge.getDefaultScreenDevice(); 
        GraphicsConfiguration gc = gs.getDefaultConfiguration();
        im = gc.createCompatibleImage(mainWidth, mainHeight, Transparency.TRANSLUCENT);



        // NOW create the plot bar image: (this remains the SAME)
        //plotKey = createImage(keyWidth,keyHeight);
        plotKey = gc.createCompatibleImage(keyWidth, keyHeight);

        imG = plotKey.getGraphics();
        g2d = (Graphics2D)imG;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                             RenderingHints.VALUE_ANTIALIAS_OFF);
        Color color;
        int l = state.tableLength - 1;
        for (int i = 0; i < state.tableLength; i++) {
            color = state.colorTable[l-i];
            imG.setColor(color);
            g2d.fillRect(0,i*2,keyWidth,2);
        }

        // RESET
        imG = im.getGraphics();
        g2d = (Graphics2D)imG;
        

        small = new Button("Instructions");
        //add(small);
	//small.setBackground(Color.white);
	//small.setBounds(7,7,80,20);
        //small.setFont(TheFonts.sanSerif11);


        // where to draw on the screen:
        yOffset = 10;  
        yBorder = 30;

        //yOffset = 90;
        yOffset = 20;

        xOffset = 10;  
        xBorder = 30;

        //xOffset = 50;
        xOffset = 20;

        xMin = xBorder+xOffset;
        //xMax = 365;
        xMax = 440;
        xMid = (int)(0.5*(xMax+xMin));
        yMin = yBorder+yOffset; 
        //yMax = 365;
        yMax = 440;
        yMid = (int)(0.5*(yMax+yMin));

        xMin_ = xMin - 5;
        xMax_ = xMax + 5;
        yMin_ = yMin - 5;
        yMax_ = yMax + 5;

        deltaY = (yMax - yMin)/10.0;
        deltaX = (xMax - xMin)/10.0;

        // NOTE that deltaX and deltaY are 39.0, so are EVENLY divisible by
        //  the number of pixels per grid (which is 3) ... MUST BE evenly
        //  divisible to have the color plot not have any holes in it, and
        //  be smooth.
        //System.out.println("deltaY="+deltaY+"  deltaX="+deltaX);

        numberGridsPerDeltaX = (int)(deltaX/numberPixelsPerGrid);
        numberGridsPerDeltaY = (int)(deltaY/numberPixelsPerGrid);
        dX = 1.0/numberGridsPerDeltaX;
        dY = 1.0/numberGridsPerDeltaY;
        //lookupScaler = state.tableLength/(fMax-fMin);
        //
        numXgrids = (xMax - xMin)/numberPixelsPerGrid + 1;
        numYgrids = (yMax - yMin)/numberPixelsPerGrid + 1;
        fVals = new double[numXgrids][numYgrids];

        numGradXpts = numXgrids/stride +1;
        numGradYpts = numYgrids/stride +1;
        //
        gradXvals = new double[numGradXpts][numGradYpts];
        gradYvals = new double[numGradXpts][numGradYpts];
        gradMagnitude = new double[numGradXpts][numGradYpts];

        // Get font sizes:
        setFont(TheFonts.bold16);
        fm = getFontMetrics(getFont());
        fontHeight = fm.getHeight();
        ticLength  = fm.stringWidth("w") - 4; // just a wide letter.

        //paintImage();
        //repaint();
        drawPlot();

        //double val;
        //val = MathPow.pow(2,-2.0);
        //System.out.println("2 to the -2 is "+val);
        //val = MathPow.pow(0.9,2.2);
        //System.out.println("0.9 to the 2.2 is "+val);
  }


    private void getImages() {
        // read in the nabla images, because CHEERPJ does not render this
	//  symbol in any font!  (java to javascript converter)
        try {
            nabla_dot = ImageIO.read(getClass().getResource("nabla_dot.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }



    public void drawPlot() {
        calcDivergence();
        calcFunctionVectors();
        paintImage();
        repaint();
    }


    private void calcDivergence() {
        int xgrid = 0, ygrid = 0;
        double xi, yi;
        double fVal;
        fMin = 9999.0;
        fMax = -9999.0;
        //System.out.println("numXgrids="+numXgrids+
        //                   "  numYgrids="+numYgrids);
        for (xi = -5.0; xi < 5.0; xi += dX) {
            for (yi = -5.0; yi < 5.0; yi += dY) {
                fVal = getDivergenceValue(xi,yi);
                fVals[xgrid][ygrid] = fVal;
                if (fVal > fMax) fMax = fVal;
                if (fVal < fMin) fMin = fVal;
                ygrid++;
           }
            xgrid++;
            ygrid = 0;
        }

        if (fMax == fMin) {
            fMax += 0.1;
            fMin -= 0.1;
        }
        lookupScaler = (state.tableLength-1)/(fMax-fMin);
   }


    private double getDivergenceValue(double xVal, double yVal) {
        // NOTE: Math.pow(a,b) returns a raised to the power of b
        //       Math.exp(a)  returns e(2.718...) raised to the power of a
        double val;
        switch(state.currentFunction) {
        case Mod3State.polynomial:
            //val = (state.aVal*state.mVal*MathPow.pow(xVal,(state.mVal-1.0) )) +
            //    (state.bVal*state.nVal*MathPow.pow(yVal,(state.nVal-1.0) ));
            val = (state.coeff1*MathPow.pow(xVal,state.exp1)) +
                (state.coeff2*MathPow.pow(yVal,state.exp2));
            break;
        case Mod3State.exponential:
            //val = (state.aVal*state.mVal*Math.exp(state.mVal*xVal)) +
            //    (state.bVal*state.nVal*Math.exp(state.nVal*yVal));
            val = (state.coeff1*Math.exp(state.exp1*xVal)) +
                (state.coeff2*Math.exp(state.exp2*yVal));
            break;
        case Mod3State.sinusoidal:
            //val = (-state.aVal*state.mVal*Math.sin(state.mVal*xVal)) +
            //    (state.bVal*state.nVal*Math.cos(state.nVal*yVal));
            val = (state.coeff1*Math.sin(state.coeff2*xVal)) +
                (state.coeff3*Math.cos(state.coeff4*yVal));
            break;
        default:
            val = 0.0;
        }
        return val;
    }


    private void calcFunctionVectors() {
        // xgrid and ygrid refer to "vector grids" - much larger than the 
        //  regular 3X3 grids used to plot divergence values (in color).
        // NOTE that i, j, and numXgrids & numYgrids refer to the regular
        //   3X3 grids.
        int xgrid, ygrid;
        double xi, yi;
        double[] gVal = new double[2];
        gradMin = 9999.0;
        gradMax = -9999.0;
        xgrid = 0;
        xi = -5.0 + (3*dX);
        for (int i = 3; i < numXgrids && xgrid<numGradXpts; i+=stride,xgrid++) {
            ygrid = 0;
            yi = -5.0 + (3*dY);
            for (int j = 3; j < numYgrids && ygrid<numGradYpts; j+=stride,ygrid++) {
                gVal = getFunctionValues(xi,yi);
                gradXvals[xgrid][ygrid] = gVal[0];
                gradYvals[xgrid][ygrid] = gVal[1];
                //System.out.println("gvalx="+gradXvals[xgrid][ygrid]+
                //                   " xgrid="+xgrid+" ygrid="+ygrid);
                gradMagnitude[xgrid][ygrid] =
                    Math.sqrt((gVal[0]*gVal[0])+(gVal[1]*gVal[1]));
                if (gradMagnitude[xgrid][ygrid] > gradMax)
                    gradMax = gradMagnitude[xgrid][ygrid];
                if (gradMagnitude[xgrid][ygrid] < gradMin)
                    gradMin = gradMagnitude[xgrid][ygrid];
                yi += (stride*dY);
                //ygrid++;
            }
            xi += (stride*dX);
        }
        //System.out.println("gradMax="+gradMax+"   gradMin="+gradMin);
        // DO FOR SPECIAL CASE where min = max (all divergences the same value)
        if (gradMax == gradMin) {
            gradMax += 1;
            gradMin -= 1;
        }
        maxLengthOverMaxMinusMin = maxVectorLength/(gradMax-gradMin);
    }


    private double[] getFunctionValues(double xVal, double yVal) {
        // NOTE: Math.pow(a,b) returns a raised to the power of b
        //       Math.exp(a)  returns e(2.718...) raised to the power of a
        double[] val = new double[2];
        
        switch(state.currentFunction) {
        case Mod3State.polynomial:
            //val[0] = state.coeff1*MathPow.pow(xVal,state.exp1);
            //val[1] = state.coeff2*MathPow.pow(yVal,state.exp2);
            val[0] = state.aVal*MathPow.pow(xVal,state.mVal);
            val[1] = state.bVal*MathPow.pow(yVal,state.nVal);
            break;
        case Mod3State.exponential:
            //val[0] = state.coeff1*Math.exp(state.exp1*xVal);
            //val[1] = state.coeff2*Math.exp(state.exp2*yVal);
            val[0] = state.aVal*Math.exp(state.mVal*xVal);
            val[1] = state.bVal*Math.exp(state.nVal*yVal);
            break;
        case Mod3State.sinusoidal:
            //val[0] = state.coeff1*Math.sin(state.coeff2*xVal);
            //val[1] = state.coeff3*Math.cos(state.coeff4*yVal);
            val[0] = state.aVal*Math.cos(state.mVal*xVal);
            val[1] = state.bVal*Math.sin(state.nVal*yVal);
            break;
        default:
            val[0] = 0.0;
            val[1] = 0.0;
        }
        return val;
    }



    /*
    public void resetPlot() {
        imG.clearRect(0,0,mainWidth,mainHeight);
        repaint();
    }
    */


    public void paintImage() {
      // clear the borders:
      //imG.clearRect(0,0,xMin,mainHeight);
      //imG.clearRect(0,0,mainWidth,yMin);
      //imG.clearRect(0,yMax,mainWidth,mainHeight-yMax);
      //imG.clearRect(xMax,0,mainWidth-xMax,mainHeight);

        imG.setColor(bgColor);
        imG.fillRect(0,0,mainWidth,mainHeight);

      drawFunctionPlot(imG);
      drawDivergencePlot(imG);
      drawAxis(imG);
      drawAxisTicks(imG);
      drawBarKey(imG);
      drawPlotInfo(imG);
    }
        
    

    public void update(Graphics g) { paint(g); }
    
    
    public void paint(Graphics g) {
        g.drawImage(im,0,0,this);
	// Have to redo this, because CHEERPJ conversion cuts off this
	//  text in SOME browsers!
	String s;	
	g.drawImage(nabla_dot,xMid-40,yMax+57,this);
	s = STR.BOLDITAL+"f"+STR.ENDBOLDITAL+STR.BOLD+" in color"+STR.ENDBOLD;
        int endingX = STR.displayString(s,g,xMid-20,yMax+70); 

    }


    private void drawFunctionPlot(Graphics g) {
        int i, j;
        int index;
        Color c;
        int x = xMin;
        int y = yMax;

        Graphics2D g2d = (Graphics2D)g;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                             RenderingHints.VALUE_ANTIALIAS_OFF);

        //for (xi = -5.0; xi <= 5.0; xi += dX) {
        //    for (yi = -5.0; yi <= 5.0; yi += dY) {
        for (i = 0; i < numXgrids; i++) {
            for (j = 0; j < numYgrids; j++) {
                index = getLookupIndex(fVals[i][j]);
                c = state.colorTable[index];
                g.setColor(c);
                g2d.fillRect(x,y,numberPixelsPerGrid,numberPixelsPerGrid);
                y -= numberPixelsPerGrid;
            }
            y = yMax;
            x += numberPixelsPerGrid;
        }       
    }



    private int getLookupIndex(double fVal) {
        /*
        fVal -= fMin;
        
        int index = (int)(fVal*lookupScaler);
        if (index < 0) index = 0;
        if (index > state.tableLength - 1)
            index = state.tableLength - 1;


        vectorLength = ((magnitude-gradMin)*maxLengthOverMaxMinusMin) +
            minVectorLength;
        */
        int index = (int)((fVal - fMin)*lookupScaler);

        // TEMP:
        //if (index >= state.tableLength || index < 0) {
        //    System.out.println("index="+index+" fVal="+fVal+" fMax="+fMax+
        //                       " fMin="+fMin+"  tableLength="+state.tableLength);
        //}

        return index;
    }



    private void drawDivergencePlot(Graphics g) {
        // xgrid and ygrid refer to "gradient grids" - much larger than the 
        //  regular 3X3 grids used to plot function values (in color).
        // NOTE that i, j, and numXgrids & numYgrids refer to the regular
        //   3X3 grids.
        int xgrid, ygrid;
        //double xi, yi;

        double from[] = new double[2];
        double to[] = new double[2];
        double vectorLength[] = new double[2];
        double invGmax;
        double linewidth;

        Point pixelPt;
        double theta;

        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                             RenderingHints.VALUE_ANTIALIAS_ON);
 
        //linewidth=10.0;
        linewidth=4.0;  // NEW 3/14
        //invnxgrid = 1.0/numXgrids;
        //invnygrid = 1.0/numYgrids;

        invGmax = 1.0/gradMax;
        // NOW ... (3/14) really a 30X30 box to display the arrow ...
        //from[0] = -12+4; // 20X20 box to display the arrow
        //to[0] = 12-4;    // line is -8 to +8 long

        from[1] = 0;     // y coordinates; will rotate arrow later
        to[1] = 0;

        xgrid = 0;
        for (int i=3; i<numXgrids && xgrid < numGradXpts; i+=stride,xgrid++) {
            ygrid = 0;
            for (int j=3; j<numYgrids && ygrid < numGradYpts; j+=stride,ygrid++) {
                pixelPt = getPixelPointFromGridPoint(new Point(i,j));
                theta = Math.atan2(gradYvals[xgrid][ygrid],
                                   gradXvals[xgrid][ygrid]);
                vectorLength = getVectorLength(gradMagnitude[xgrid][ygrid]);
                from[0] = vectorLength[0];
                to[0] = vectorLength[1];
                
                
                // color is deeper blue if near EMax, white if near zero
                //ee = 0.5*invEmax*Math.sqrt(square(efieldx[ii][jj]) + 
                //                       square(efieldy[ii][jj]));
                //index = (int)(1000.0*ee);
                //colorValue = state.lookup[index];
                //color[0] = 1.0-colorValue;
                //color[1] = 1.0-colorValue;
                //color[2] = 1.0;
        
                //if (ee > 1.0E-10)
                //    plot_arrowline_local(linewidth,color,from,to,theta,pixelPt);
                plot_arrowline_local(linewidth,from,to,theta,pixelPt);
                //jj++;
            }
            //ii++;
        }
        return;
    }


    private double[] getVectorLength(double magnitude) {
        // use gradMax and gradMin, and MaxLengthOverMaxMinusMin
        double val[] = new double[2];
        double vectorLength;
        // note: val[0] will be -ve 1/2 of vector length,
        //   and val[1] will be +ve 1/2 of vector length.
        vectorLength = ((magnitude-gradMin)*maxLengthOverMaxMinusMin) +
            minVectorLength;
        val[0] = -0.5*vectorLength;
        val[1] = -1*val[0];
        return val;
    }



    private Point getPixelPointFromGridPoint(Point pt) {
        Point newPt = new Point(0,0);
        newPt.x = (int)((pt.x*numberPixelsPerGrid) + xMin);
        newPt.y = (int)(yMax - (pt.y*numberPixelsPerGrid));
        return newPt;
    }



    private void plot_arrowline_local(double linewidth, 
                                      double from[], double to[],
                                      double theta, Point pixelPt) {
        // color(3)          !  0=none, 1=lots
        // both these coordinates are in inches from lower left corner:
        // from(2)           !  x,y coords in inches
        // to(2)             !  x,y coords in inches

        // Draws a straight line from 'from' to 'to' (x,y coords in inches),
        // and puts an arrowhead on the 'to' end.
        // uses linewidth to scale the linewidth and the arrowhead.

        double x[] = new double[7];
        double y[] = new double[7];
        double dto[] = new double[2];

        double L, lw, hw;
        // lw is linewidth in inches
        // hw is (headwidth-1)/2
            
        int i;
        double headwidth;    // multiple of linewidth

        /*
        //calculate the outline of the arrow-line and arrow-head
        // then just fill it in.
        //
        //        y-axis
        //         /|\                         3
        //          |                          |\
        //          |                          | \
        //          |                          |  \
        //          1--------------------------2   \
        //          |                               \
        //          |                                \                  \
        // -------------------------------------------4------------------
        //          |                                /           x-axis /
        //          |                               /
        //          7--------------------------6   /
        //          |                          |  /
        //          |                          | /
        //          |                          |/
        //          |                          5
        //          |
        //          |
        //  points along the path are numbered.
        //
        //-------------------------------------------------------------
        */

        /*
        linewidth=3.0;
        headwidth=5.0;
        */

        /*   didn't look right even for arrow length = 7
        linewidth=1.5;
        headwidth=2.5;
        */

        linewidth=2;
        headwidth=4;


        lw = linewidth;    //linewidth in pixels
        hw = (headwidth-1.0)*0.5;

        // angle of the arrowhead:
        dto[0] = to[0] - from[0];
        dto[1] = to[1] - from[1];
        //theta = Math.atan2(dto[1],dto[0]);

        // calc the arrowhead starting at 0,0, and going to L,0
        L = Math.sqrt(square(dto[0]) + square(dto[1]));

        x[0] = 0.0;
        y[0] = 0.5*lw;
        x[1] = L-(headwidth-1.0)*lw;
        y[1] = y[0];
        x[2] = x[1];
        y[2] = y[1]+hw*lw*0.6;
        x[3] = L;
        y[3] = 0.0;
        // mirror the top to the bottom:
        x[4] = x[2];
        y[4] = -1.0*y[2];
        x[5] = x[1];
        y[5] = -1.0*y[1];
        x[6] = x[0];
        y[6] = -1.8*y[0];
        
        //original:
        //rotate_polyline(x,y,theta,12.0,1.5); // half of xlen and half of lw
        //translate_polyline(x,y,pixelPt.x-12,pixelPt.y);

        // lep:
        double lepxcent= 0.5*L;
        double lepycent= 0.0;
        rotate_polyline(x,y,theta,lepxcent,lepycent);
        translate_polyline(x,y,pixelPt.x-lepxcent,pixelPt.y-lepycent);

        // Draw the arrow (fill the polygon):
        //imG.setColor(new Color((float)color[0],(float)color[1],(float)color[2]));
        //imG.setColor(Color.blue);
        imG.setColor(Color.black);  // NEW 3/14
        int ix[] = new int[x.length];
        int iy[] = new int[y.length];
        for (i=0; i<x.length; i++) {
            ix[i] = (int)x[i];
            iy[i] = (int)y[i];
        }
        imG.fillPolygon(ix,iy, x.length);
        // Outline the arrow:
        //imG.setColor(Color.black);
        //imG.drawPolygon(ix,iy, x.length);

        return;
    }


    private void rotate_polyline(double x[], double y[], double theta,
                                 double xcent, double ycent) {
        //  x(n),y(n) are coordinates of each point
        //  theta is the amount to rotate points counterclockwise, radians
        // xcent, ycent  is the center of rotation
        double xx, yy;
        int i;
        for (i=0; i<x.length; i++) {
            xx = (x[i]-xcent)*Math.cos(theta) - (ycent-y[i])*Math.sin(theta);
            yy = (x[i]-xcent)*Math.sin(theta) + (ycent-y[i])*Math.cos(theta);
            x[i] = xx+xcent;
            y[i] = ycent-yy;
        }
        return;
    }


    
    private void translate_polyline(double x[], double y[], 
                                    double dx, double dy) {
        // x(n),y(n) are coordinates of each point
        // dx,dy     are amount to translate points
        
        int i;
        for (i=0; i<x.length; i++) {
            x[i] = x[i] + dx;
            y[i] = y[i] + dy;
            //y[i] = dy - y[i];
        }
        return;
    }


    private double square(double number) {
        double newNumber;
        newNumber = number*number;
        return newNumber;
    }

    
    
    private void drawAxis(Graphics g) {
        // Draw x and y axis:
        g.setColor(state.axisColor);
        drawLine(g,xMid,yMin_,xMid,yMax_); // the middle y-axis
        
        drawLine(g,xMin_,yMid,xMax_,yMid); // the middle x-axis

        drawLine(g,xMin_,yMax_,xMin_,yMin_); // Left axis
        drawLine(g,xMax_,yMax_,xMax_,yMin_); // rt. axis
        drawLine(g,xMin_,yMin_,xMax_,yMin_);  // top axis
        drawLine(g,xMin_,yMax_,xMax_,yMax_); // bot. axis
        //MyArrows.drawDblUpArrow(g,xMid,yOffset);
        
        //MyArrows.drawDblRightArrow(g,xMax+xBorder,yMid);
    }

    
    private void drawLine(Graphics g, int x1, int y1, int x2, int y2) {
        if (y1 != y2) {
            g.drawLine(x1,y1,x2,y2);
            g.drawLine(x1+1,y1,x2+1,y2);
        } else {
            g.drawLine(x1,y1,x2,y2);
            g.drawLine(x1,y1+1,x2,y2+1);
        }
    }
    
    private void drawPolyline(Graphics g, int[] xpts, int[] ypts, int len) {
        int x1, y1, x2, y2;
        for (int i = 0; i < (len-1); i++) {
            x1 = xpts[i]; y1 = ypts[i];
            x2 = xpts[i+1]; y2 = ypts[i+1];
            drawLine(g,x1,y1,x2,y2);
        }
    }
    

    private void drawAxisTicks(Graphics g) {
        // Ticks on the Left and right (y)axes:
        g.setColor(state.axisColor);
        g.setFont(TheFonts.bold16);
        
        for(int i=0;i<11;i++){
            int y = (int)(i*deltaY)+yOffset+yBorder;
            g.drawLine(xMin_,y,xMin_+(ticLength/2),y);
            g.drawLine(xMax_,y,xMax_-(ticLength/2),y);
            int v = 5-i;
            String vs = String.valueOf(v);
            g.drawString(vs,xMin_-(ticLength/2)-fm.stringWidth(vs)-5,
                         y+(int)(0.5*fontHeight)-5);
        }
        g.drawString("y",xMid-(ticLength/4),fontHeight+yOffset-2);        
        
        // On the lower and upper x-axis:
        // dots per cm:
        int screenRes = (int) (getToolkit().getScreenResolution()/2.54);
        for(int i=0;i<11;i++){
            int x = (int)(i*deltaX)+xMin;
            if( x > xMax) break;
            int v = i-5;
            g.drawLine(x,yMax_,x,yMax_-(ticLength/2));
            g.drawLine(x,yMin_,x,yMin_+(ticLength/2));
            String xs = String.valueOf(v);
            g.drawString(xs,x-(int)(0.5*fm.stringWidth(xs))+3,
                         yMax_+fontHeight);
        }
        g.drawString("x",xMax_+5,yMid+5);
    }


    private void drawBarKey(Graphics g) {
        DecimalFormat fm;
        g.setFont(TheFonts.bold12);
        g.drawImage(plotKey,xMax+25,yMid-(int)(keyHeight/2),this);
        // print f(x,y) max and min values next to the color bar key
        if (Math.abs(fMax) > 999.999 ||
            Math.abs(fMax) < .001) fm = dec2;
        else fm = dec;
        g.drawString(fm.format(fMax),xMax+55,
                     yMid-(int)(keyHeight/2)+(int)(0.5*fontHeight));
        if (Math.abs(fMin) > 999.999 ||
            Math.abs(fMin) < .001) fm = dec2;
        else fm = dec;
        g.drawString(fm.format(fMin),xMax+55,yMid+(int)(keyHeight/2));
    }


    private void drawPlotInfo(Graphics g) {
        int endingX;
        String s;
        g.setFont(TheFonts.bold16);
        s = STR.BOLDITAL+"f"+STR.ENDBOLDITAL+STR.BOLD+" in arrows"
            +STR.ENDBOLD;
        endingX = STR.displayString(s,g,xMid-33,yMax+50);

	// this code replaced with code below, d/t CHEERPJ issues with spec.
	//  characters
	/*
        s = STR.PLUS2FONT+Character.toString((char)0x2207)+
            Character.toString((char)0x22C5)+STR.ENDPLUS2FONT+
            STR.BOLDITAL+"f"+STR.ENDBOLDITAL+STR.BOLD+" in color"+STR.ENDBOLD;
        endingX = STR.displayString(s,g,xMid-40,yMax+70); 
	*/

	// (text images drawn -13 pixels on y-axis  (y = 70 - 13))
	g.drawImage(nabla_dot,xMid-40,yMax+57,this);
	s = STR.BOLDITAL+"f"+STR.ENDBOLDITAL+STR.BOLD+" in color"+STR.ENDBOLD;
        endingX = STR.displayString(s,g,xMid-20,yMax+70); 
    }



    private void drawRectGrid(Graphics g) {
        int x, y;
        //Graphics2D g2d = (Graphics2D)g;
        //g2d.setPaint(Color.lightGray.brighter());
        g2d.setPaint(Color.white);
        //g2d.setStroke(new BasicStroke((float)0.25));
        g2d.setStroke(new BasicStroke((float)0.5));
        for(int i=0;i<11;i++){
            x = (int)(i*deltaX)+xMin;
            g2d.drawLine(x,(int)(yMin),x,(int)(yMax));
            //g.drawLine(x,yOffset,x,yMax+yBorder);
            y = (int)(i*deltaY)+yMin;
            //g2d.drawLine((int)(xOffset+deltaX),y,(int)(xMax+xBorder-deltaX),y);
            g2d.drawLine((int)(xMin),y,(int)(xMax),y);
            //g.drawLine(xOffset,y,xMax+xBorder,y);
        }
      g2d.setStroke(new BasicStroke(1));
    }



    private void drawCylGrid(Graphics g) {
        //Graphics2D g2d = (Graphics2D)g;
      //g2d.setPaint(Color.lightGray.brighter());
      //g2d.setStroke(new BasicStroke((float)0.25));
      g2d.setPaint(Color.white);
      g2d.setStroke(new BasicStroke((float)0.5));
      g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                           RenderingHints.VALUE_ANTIALIAS_ON);
      //Arc2D.Double (double X, double Y, double Width, double Height,
      //              double Theta, double Delta, int Type)
      for(int i=1;i<6;i++) {
          //Arc2D.Double arc_one = new Arc2D.Double(xMid-deltaX+1,yMid-deltaY+1,
          //                                        2*deltaX,2*deltaY,
          // 
          //                             0,360,0);
          Arc2D.Double arc_one = new Arc2D.Double(xMid-(i*deltaX),
                                                  yMid-(i*deltaY),
                                                  2*i*deltaX,2*i*deltaY,
                                                  0,360,0);
          g2d.draw(arc_one);
      }
      double theta = 0.0;
      double deltaTheta = (90.0/4.0)*piOver180;
      int x,y;
      for (int i=0; i<16; i++) {
          //x = (int)((xMax-xMid+deltaX)*Math.cos(theta));
          //y = (int)((yMax-yMid+deltaY)*Math.sin(theta));
          x = (int)((xMax-xMid)*Math.cos(theta));
          y = (int)((yMax-yMid)*Math.sin(theta));
          g2d.drawLine(xMid,yMid,xMid+x,yMid-y);
          theta += deltaTheta;
      }
      
      g2d.setStroke(new BasicStroke(1));
      g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_OFF);
    }


    private boolean pointInQuadrant1(MyPoint pt) {
        if (pt.x > 0 && pt.y > 0) return true;
        return false;
    }
    private boolean pointInQuadrant2(MyPoint pt) {
        if (pt.x < 0 && pt.y > 0) return true;
        return false;
    }
    private boolean pointInQuadrant3(MyPoint pt) {
        if (pt.x < 0 && pt.y < 0) return true;
        return false;
    }
    private boolean pointInQuadrant4(MyPoint pt) {
        if (pt.x > 0 && pt.y < 0) return true;
        return false;
    }

    
}

class MyPoint {
    
    public double x, y;
    public static final double maxX = 5.1;
    public static final double maxY = 5.1;
    
    public MyPoint(double x, double y) {
        super();
        this.x = x;
        this.y = y;
    }
}
