import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
import java.net.URL;
import java.text.*;
import java.util.Vector;
import java.util.Map;
import java.util.Hashtable;
//import java.util.jar.Attributes;
import java.util.jar.*;

// Module 1 Exercise 4
public class Mod1Plot extends Panel {

    private static final long serialVersionUID = 352364;

    private static int NMAX = 5000;  //was (NMAX=10000)
    private static int xscale = 3;   // number of real pixels per "pixel"
    private static int yscale = 3;   // number of real pixels per "pixel"
    private static int numberPixelsPerGrid = 3;  // was 6
    private double nmPerPixel;  // nano meters per pixel
    private double pixelsPerNm;  // pixels per nano meter
    private double pixelsPerNmOver4piepsilon;
    private double gridsPerNmOver4piepsilon;

    int ncharges;  // # of charges in 2D area
    int xpos[], ypos[];  // positions of charges, in nm
    double charge[];  // signed charge of each charge, nColoumbs
    double elemcharge[];  // signed charge of each charge as mult of elem charg
    Point pixelPt[];  // actual pixel point (NOT grid point)

    Vector<Charge> chargeVector = new Vector<Charge>();

    int numEquiLines;
    //7;  // was 15
    private static final int EQUI_LINES_INIT = 16;   //15-7 X 2

    // the chosen ranges for the plot: (1) is the min, (2) is the max
    //  and (3) is delta for large tics
    double xrange[] = new double[3];
    double yrange[] = new double[3];

    static int nxgrid , nygrid;
    double potential[][];
    double Emax;
    double potentialMax, potentialMin;

    int max_number_charges = NMAX;

    Mod1State state;
    Image im;
    Graphics imG;
    Graphics2D g2d;

    int mainWidth = 630;  // 619
    int mainHeight = 576;
    //plotPanel.setBounds(12,58,635,581);

    int yMin, yMax;
    int xMin, xMax;
    int yBorder, yOffset, xBorder, xOffset;
    DecimalFormat dec = new DecimalFormat("###.#");

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


    double deltaY, deltaX;
    FontMetrics fm;
    int fontHeight,ticLength;

    public Button small;


    public Mod1Plot(Mod1State state) {
        super();
        this.state = state;
    }

    public void initialPlot() {
        setLayout(null);
        setBackground(bgColor);
        //im = createImage(mainWidth,mainHeight);
        //imG = im.getGraphics();

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

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

        numEquiLines = EQUI_LINES_INIT;

        // where to draw on the screen:
        //plotPanel.setBounds(12,58,494,480);
        yOffset = 10;
        yBorder = 20;
        xOffset = 10;
        xBorder = 20;

        xMin = xBorder+xOffset;
        //xMax = 438;    // for 9.5 nm   ... 43 pixels per nm
        xMax = 589;    // for 9 nm
        yMin = yBorder+yOffset; 
        yMax = 546;

        deltaY = (yMax - yMin)/12.0;  // these are both the same
        deltaX = deltaY;

        g2d = (Graphics2D)imG;
        //g2d.scale(1.11,1.0);  // b/c vertical pixels larger than horizontal

        nmPerPixel = 1.0/deltaX;   // 1 nanometer per deltaX and deltaY
        pixelsPerNm = deltaX/1.0;   
        pixelsPerNmOver4piepsilon = pixelsPerNm/(4*Math.PI*8.85E-12*1.0E-9);
        gridsPerNmOver4piepsilon = pixelsPerNmOver4piepsilon/
            numberPixelsPerGrid;

        //ngrid = Math.min(mainWidth-(int)xoff,mainHeight-(int)yoff) /
        //    numberPixelsPerGrid;

        // grid has 12 nm in y direction and 13 nm in x direction:
        nygrid = (int)(12*pixelsPerNm / numberPixelsPerGrid);
        nxgrid = (int)(13*pixelsPerNm / numberPixelsPerGrid);

        potential = new double[nxgrid][nygrid];
        xrange[0] = xMin;
        xrange[1] = xMax;
        xrange[2] = deltaX;  // spacing for axis tic marks
        yrange[0] = yMin;
        yrange[1] = yMax;
        yrange[2] = deltaY;  // spacing for axis tic marks


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

        paintImage();
        repaint();
  }



    // want sliderVal to go from 15 to 1
    public boolean changeDeltaV(int sliderVal) {
        int newNumEquiLines;
        //newNumEquiLines = (int) ((sliderVal+2) * 1.5);
        newNumEquiLines = (sliderVal+1) * 2;
        if (newNumEquiLines != numEquiLines) {
            numEquiLines = newNumEquiLines;
            return true;
        }
        return false;
    }

/*
    public boolean OLDchangeDeltaV(int sliderVal) {
        int newNumEquiLines;
        int i = sliderVal - 10;
        if (i == 0) newNumEquiLines = EQUI_LINES_INIT;
        //else if (i > 0) newNumEquiLines = (int)(EQUI_LINES_INIT*1.2*i);
        //else newNumEquiLines = (int) (EQUI_LINES_INIT/(1.2*Math.abs(i)));
        else newNumEquiLines = (int) (EQUI_LINES_INIT - (1.5*i));
        if (newNumEquiLines != numEquiLines) {
            numEquiLines = newNumEquiLines;
            return true;
        }
        return false;
    }
*/


    public void getVoltageAtCursor(Point pt) {
        // convert pt from pixels to "places" on this big plot
        // get voltage AND electric field at this place
        // set state.voltageAtCursor and state.efieldAtCursor
        Point gridPt = getGridPointFromPixelPoint(pt);
        state.voltageAtCursor = getPotentialValueAtPoint(gridPt);
        ActualVector efield = getEfieldValueAtPoint(gridPt);
        state.efieldAtCursor = efield.ee;
        state.efieldThetaAtCursor = efield.theta;
        return;
    }



    public void moveCharge() {
        // convert state.moveFromPoint and state.moveToPoint to
        // the "places" on this big plot
        //  add the charge(s) (or just one?) to the movetoPoint
        Point fromGridPt, toGridPt;
        Point fromPt = new Point(state.moveFromPoint.x,state.moveFromPoint.y);
        Point toPt = new Point(state.moveToPoint.x,state.moveToPoint.y);
        fromGridPt = getGridPointFromPixelPoint(fromPt);
        toGridPt = getGridPointFromPixelPoint(toPt);
        Charge c;
        Charge cToMove = null;
        for (int i = 0; i < chargeVector.size(); i++) {
            c = chargeVector.elementAt(i);
            xpos[i] = c.xpos;  ypos[i] = c.ypos;
            if (withinReasonableRange(xpos[i],ypos[i],
                                      fromGridPt.x,fromGridPt.y)) {
                cToMove = c;
                break;
            }
        }
        if (cToMove != null) {
            cToMove.xpos = toGridPt.x;
            cToMove.ypos = toGridPt.y;
            cToMove.pixelPt = new Point(toPt);
        }
        return;
    }



    private boolean withinReasonableRange(int x1, int y1, int x2, int y2) {
        // NOTE: assumed that these coordinates are for GRIDS not PIXELS
        if (x2 >= (x1 - 3) && x2 <= (x1 + 3))
            if (y2 >= (y1 - 3) && y2 <= (y1 + 3)) return true;
        return false;
    }


    public void reset() {
        numEquiLines = EQUI_LINES_INIT;
        chargeVector = new Vector<Charge>();
        state.moveFromPoint = new Point(-1,-1);
        state.moveToPoint = new Point(-1,-1);
        //imG.clearRect(0,0,mainWidth,mainHeight);
        imG.setColor(bgColor);
        imG.fillRect(0,0,mainWidth,mainHeight);
    }


    public void placeCharge(Point pt) {
        // convert pt from pixels to "places" on this big plot
        // place a (or more? if a slider ...) negative charge at this "place"
        Point gridPt;
        gridPt = getGridPointFromPixelPoint(pt);
        Charge c = new Charge(gridPt.x, gridPt.y, state.chargeToAdd*
                              state.elementaryCharge, state.chargeToAdd, pt);
        chargeVector.addElement(c);
        return;
    }


    public void removeCharge(Point pt) {
        // convert pt from pixels to "places" on this big plot
        // remove charge (or more? if a slider ...) at this "place"
        Point gridPt;
        gridPt = getGridPointFromPixelPoint(pt);
        Charge c;
        for (int i = 0; i < chargeVector.size(); i++) {
            c = chargeVector.elementAt(i);
            xpos[i] = c.xpos;  ypos[i] = c.ypos;
            if (withinReasonableRange(xpos[i],ypos[i],gridPt.x,gridPt.y)) {
                chargeVector.removeElementAt(i);
            }
        }        
        return;
    }



    public void changeCharge(Point pt) {
        // convert pt from pixels to "places" on this big plot
        // remove charge (or more? if a slider ...) at this "place"
        Point gridPt;
        gridPt = getGridPointFromPixelPoint(pt);
        Charge c;
        for (int i = 0; i < chargeVector.size(); i++) {
            c = chargeVector.elementAt(i);
            xpos[i] = c.xpos;  ypos[i] = c.ypos;
            if (withinReasonableRange(xpos[i],ypos[i],gridPt.x,gridPt.y)) {
                c.charge = state.chargeToAdd*state.elementaryCharge;
                c.elemcharge = state.chargeToAdd;
            }
        }        
        return;
    }
        

    private Point getGridPointFromPixelPoint(Point pt) {
        Point newPt = new Point(0,0);
        newPt.x = (int) ((pt.x - xrange[0])/numberPixelsPerGrid);
        newPt.y = (int) ((pt.y - yrange[0])/numberPixelsPerGrid);
        if (newPt.x >= nxgrid) newPt.x = nxgrid-1;
        else if (newPt.x < 0) newPt.x = 0;
        if (newPt.y >= nygrid) newPt.y = nygrid-1;
        else if (newPt.y < 0) newPt.y = 0;
        return newPt;
    }

    private Point getPixelPointFromGridPoint(Point pt) {
        Point newPt = new Point(0,0);
        newPt.x = (int)((pt.x*numberPixelsPerGrid)+xrange[0]);
        newPt.y = (int)((pt.y*numberPixelsPerGrid)+yrange[0]);
        return newPt;
    }

    private double getPotentialValueAtPoint(Point pt) {
        // pt already a grid pt, NOT a pixel point
        return potential[pt.x][pt.y];
    }

    private ActualVector getEfieldValueAtPoint(Point pt) {
        double exi, eyi, ex, ey, ee, ri, riSquared, ei;
        double vmax = state.vmax;
        int x = pt.x;
        int y = pt.y;
        ex = 0.0;  ey = 0.0;
        double arbitraryMax = 50.0; // to simulate infinity
        for (int k = 0; k < ncharges; k++) {
            if (x == xpos[k] && y == ypos[k]) {
                if (charge[k] >= 0.0) {exi = vmax; eyi = vmax;}
                else {exi = -1.0*vmax; eyi = -1.0*vmax;}
            } else {
                riSquared = square(x-xpos[k])+square(y-ypos[k]);
                ei = charge[k]/riSquared;
                ri = Math.sqrt(riSquared);
                exi = ((x-xpos[k])/ri) * ei;
                eyi = ((y-ypos[k])/ri) * ei;
            }
            
            if (Math.abs(exi) < vmax)
                ex = ex + exi;
            else {
                if (exi > 0.0) 
                    ex = vmax;
                else ex = -1.0*vmax;
            }
            if (Math.abs(eyi) < vmax)
                ey = ey + eyi;
            else {
                if (eyi > 0.0) 
                    ey = vmax;
                else ey = -1.0*vmax;
            }
        }
        ex *= gridsPerNmOver4piepsilon;
        ey *= gridsPerNmOver4piepsilon;
        //if (Math.abs(ex) > state.efieldMax) {
        if (Math.abs(ex) > arbitraryMax) {
            if (ex > 0.0) ex = arbitraryMax;
            else ex = -1.0*arbitraryMax;
        }
        //if (Math.abs(ey) > state.efieldMax) {
        if (Math.abs(ey) > arbitraryMax) {
            if (ey > 0.0) ey = arbitraryMax;
            else ey = -1.0*arbitraryMax;
        }
        ee = Math.sqrt(square(ex) + square(ey));
        //System.out.println("ee = "+String.valueOf(ee));
        //return ee;
        double theta = Math.atan2(-ey,ex);
        ActualVector efield = new ActualVector(ee,theta);
        return efield;
    }

    private double OLDgetEfieldValueAtPoint(Point pt) {
        // pt already a grid pt, NOT a pixel point
        double dx, dy, invdx, invdy, ex, ey, ee;
        dx = (xrange[1]-xrange[0])/(nxgrid-1.0);
        dy = (yrange[1]-yrange[0])/(nygrid-1.0);
        invdx = 1.0/dx;
        invdy = 1.0/dy;
        if (pt.x < nxgrid-1)
            ex = invdx*(potential[pt.x+1][pt.y]-potential[pt.x][pt.y]);
        else
            ex = invdx*(potential[pt.x][pt.y]-potential[pt.x-1][pt.y]);
        if (pt.y < nygrid-1)
            ey = invdy*(potential[pt.x][pt.y+1]-potential[pt.x][pt.y]);
        else
            ey = invdy*(potential[pt.x][pt.y]-potential[pt.x][pt.y-1]);
        ee = Math.sqrt(square(ex) + square(ey));
        //System.out.println("ee = "+String.valueOf(ee));
        return ee;
    }


    public void drawPlot() {
        if (state.currentAction == state.placeCharge ||
            state.currentAction == state.removeCharge ||
            state.currentAction == state.moveCharge ||
            state.currentAction == state.changeChargeValue) {
            
            ncharges = chargeVector.size();
            xpos = new int[ncharges];
            ypos = new int[ncharges];
            charge = new double[ncharges];
            elemcharge = new double[ncharges];
            pixelPt = new Point[ncharges];
            
            Charge c;
            for (int i=0; i<ncharges; i++) {
                c = chargeVector.elementAt(i);
                xpos[i] = c.xpos; ypos[i] = c.ypos;
                charge[i] = c.charge;
                elemcharge[i] = c.elemcharge;
                pixelPt[i] = c.pixelPt;
            }
        }
        
        //comment out for now; debug one at a time:
        //imG.clearRect(0,0,mainWidth,mainHeight);

        imG.setColor(bgColor);
        imG.fillRect(xMin,yMin,xMax-xMin,yMax-yMin);
        
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                             RenderingHints.VALUE_ANTIALIAS_ON);
        
        if (state.currentAction == state.placeCharge ||
            state.currentAction == state.removeCharge ||
            state.currentAction == state.moveCharge ||
            state.currentAction == state.changeChargeValue) {
            calculate_potential_field();
        }
        if (state.displayPotentialField) 
            draw_potential_field();
        if (state.displayEquiPotentialLines) {
            draw_isopotential_lines();
        }   
        if (state.displayElectricField)
            draw_electricfield_vectors();
        draw_charges();

        paintImage();
        repaint();
    }



    private void calculate_potential_field() {
        double x, y;
        int i, j, k;
        double v, vi;
        //double vmax = 50;
        //double vmax = 10;
        //double vmax = 6*state.elementaryCharge;
        double vmax = state.vmax;

        potentialMax = -10000.0;
        potentialMin = 10000.0;

        for (i = 0; i < nxgrid; i++) {
            x = i;
            for (j = 0; j < nygrid; j++) {
                y = j;
                v = 0.0;
                for (k = 0; k < ncharges; k++) {
                    if (x == xpos[k] && y == ypos[k]) {
                        vi = charge[k];
                        //if (charge[k] >= 0.0) vi = vmax;
                        //else vi = -1.0*vmax;
                    } else {
                        vi = charge[k]/Math.sqrt(square(x-xpos[k])+
                                                 square(y-ypos[k]));
                    }
                    
                    if (Math.abs(vi) < vmax)
                        v = v + vi;
                    else {
                        if (vi > 0.0) 
                            v = vmax;
                        else v = -1.0*vmax;
                    }
               }
                v *= gridsPerNmOver4piepsilon;
                if (Math.abs(v) > state.potentialMax) {
                  if (v > 0.0) v = state.potentialMax;
                  else v = -1.0*state.potentialMax;
                }
                potential[i][j] = v;
                if (potential[i][j] > potentialMax)
                    potentialMax = potential[i][j];
                if (potential[i][j] < potentialMin)
                    potentialMin = potential[i][j];
            }
        }

        return;
    }
    
    
    private void draw_potential_field() {
        int i, j;
        double colorValue;
        int index;
        Color color;

        Graphics2D g2d = (Graphics2D)imG;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                             RenderingHints.VALUE_ANTIALIAS_OFF);
        
        // NOTE: potential is -100 to +100
        for (i=0; i<nxgrid; i++)
            for (j=0; j<nygrid; j++)
                if (potential[i][j] >= 0.0) {
                    index = (int)(10*potential[i][j]);
                    colorValue = state.lookup[index];
                    /*  green:
                    color = new Color(1-(float)(colorValue),
                                      (float)1.0,
                                      1-(float)(colorValue));
                    */

                    color = new Color(1-(float)(colorValue),
                                      1-(float)(colorValue),
                                      (float)1.0);

                    imG.setColor(color);
                    g2d.fillRect((int)xrange[0]+i*numberPixelsPerGrid,
                                 (int)yrange[0]+j*numberPixelsPerGrid,
                                 numberPixelsPerGrid,
                                 numberPixelsPerGrid);
                } else {
                    index = (int)(-10.0*potential[i][j]);
                    colorValue = state.lookup[index];
                    color = new Color((float)1.0,
                                      1-(float)(colorValue),
                                      1-(float)(colorValue));
                    imG.setColor(color);
                    imG.fillRect((int)xrange[0]+i*numberPixelsPerGrid,
                                 (int)yrange[0]+j*numberPixelsPerGrid,
                                 numberPixelsPerGrid,
                                 numberPixelsPerGrid);
                }
        
        return;
    }
        
        
        
    private void draw_isopotential_lines() {
        double deltav;
        double x[] = new double[1000];
        double y[] = new double[1000];
        double rmax, rmin;
        int i, j;
        double contours[] = new double[1000];
        int ncontours;
        Color lineColor = Color.orange;

        
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                             RenderingHints.VALUE_ANTIALIAS_ON);


        for (i=0; i<nxgrid; i++)
            //x[i] = xrange[0] + i*dx;
            x[i] = i;
        for (j=0; j<nygrid; j++)
            //y[j] = yrange[0] + j*dy;
            y[j] = j;

        rmax = potentialMax;
        rmin = potentialMin;
        deltav = (rmax - rmin)/(numEquiLines-1);
        //rmax = potential[0][0];
        //rmin = rmax;
        //for (i=0; i<nxgrid; i++)
        //    for (j=0; j<nygrid; j++) {
        //        if (potential[i][j] > rmax) rmax = potential[i][j];
        //        if (potential[i][j] < rmin) rmin = potential[i][j];
        //    }

        ncontours = 0;
        contours[ncontours] = 0.0;
        while (contours[ncontours] < rmax && ncontours < 1000) {
            ncontours++;
            if (ncontours < 1000)
                contours[ncontours] = contours[ncontours-1]+deltav;
        }
        if (contours[ncontours] <= rmax) ncontours++;

        if (ncontours < 1000)
            contours[ncontours] = contours[0]-deltav;
        while (contours[ncontours] > rmin && ncontours < 1000) {
            ncontours++;
            if (ncontours < 1000)
                contours[ncontours] = contours[ncontours-1]-deltav;
        }
        if (contours[ncontours] < rmin) ncontours--;

        // redo contour values to be logarithmic rather than linear
        int index;
        double zeroToOne;
        double scaler1 = 1000/rmax;
        double scaler2 = 1000/rmin;
        for (i=0; i<=ncontours; i++) {
            if (contours[i] > 0) {
                index = (int)(1000 - contours[i]*scaler1);
                zeroToOne = state.lookup[index];
                contours[i] = (1.0-zeroToOne)*rmax;
            } else {
                index = (int)(1000 - contours[i]*scaler2);
                zeroToOne = state.lookup[index];
                contours[i] = (1.0-zeroToOne)*rmin;
            }
        }
        
        plot_contours(x,y,contours,ncontours);

        return;
    }



    private void draw_electricfield_vectors() {
        //int stride = 15;
        int stride = 10;  //7
        double x, y;
        int i, j, k;
        int ii,jj, len1,len2;
        double ex, ey, exi, eyi;
        double riSquared, ri, ei;
        double vmax = state.vmax;
        double efieldx[][], efieldy[][];
        double Emax;

        
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                             RenderingHints.VALUE_ANTIALIAS_ON);


        len1 = nxgrid/stride +1;
        len2 = nygrid/stride +1;

        efieldx = new double[len1][len2];
        efieldy = new double[len1][len2];

        Emax = 0.0;
        ii = 0;
        for (i = 3; i < nxgrid && ii<len1; i+=stride) {
            x = i;
            jj = 0;
            for (j = 3; j < nygrid && jj<len2; j+=stride) {
                y = j;
                ex = 0.0;  ey = 0.0;
                for (k = 0; k < ncharges; k++) {
                    if (x == xpos[k] && y == ypos[k]) {
                        if (charge[k] >= 0.0) {exi = vmax; eyi = vmax;}
                        else {exi = -1.0*vmax; eyi = -1.0*vmax;}
                    } else {
                        riSquared = square(x-xpos[k])+square(y-ypos[k]);
                        ei = charge[k]/riSquared;
                        ri = Math.sqrt(riSquared);
                        exi = ((x-xpos[k])/ri) * ei;
                        eyi = ((y-ypos[k])/ri) * ei;
                    }
                    
                    if (Math.abs(exi) < vmax)
                        ex = ex + exi;
                    else {
                        if (exi > 0.0) 
                            ex = vmax;
                        else ex = -1.0*vmax;
                    }
                    if (Math.abs(eyi) < vmax)
                        ey = ey + eyi;
                    else {
                        if (eyi > 0.0) 
                            ey = vmax;
                        else ey = -1.0*vmax;
                    }
                }
                ex *= gridsPerNmOver4piepsilon;
                ey *= gridsPerNmOver4piepsilon;
                if (Math.abs(ex) > state.efieldMax) {
                    if (ex > 0.0) ex = state.efieldMax;
                    else ex = -1.0*state.efieldMax;
                }
                if (Math.abs(ey) > state.efieldMax) {
                    if (ey > 0.0) ey = state.efieldMax;
                    else ey = -1.0*state.efieldMax;
                }
                efieldx[ii][jj] = ex;
                efieldy[ii][jj] = ey;

                if (Math.abs(efieldx[ii][jj]) > Emax)
                    Emax = Math.abs(efieldx[ii][jj]);
                if (Math.abs(efieldy[ii][jj]) > Emax)
                    Emax = Math.abs(efieldy[ii][jj]);
                
                jj++;
            }
            ii++;
        }

        double from[] = new double[2];
        double to[] = new double[2];
        double invEmax;
        double ee;
        int index;
        double colorValue;
        double color[] = new double[3];
        double invnxgrid, invnygrid;
        double linewidth;

        //linewidth=15.0;
        //linewidth=7.0;
        linewidth=10.0;
        invnxgrid = 1.0/nxgrid;
        invnygrid = 1.0/nxgrid;

        invEmax = 1.0/Emax;

        /*
        from[0] = -20+8; // 40X40 box to display the arrow
        to[0] = 20-8;    // line is -12 to +12 long
        */
        /*
        from[0] = -10+4; // 20X20 box to display the arrow
        to[0] = 10-4;    // line is -6 to +6 long
        */
        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;

        Point pixelPt;
        double theta;
        ii = 0;
        for (i=3; i<nxgrid && ii < len1; i+=stride) {
            jj = 0;
            for (j=3; j<nygrid && jj < len2; j+=stride) {
                pixelPt = getPixelPointFromGridPoint(new Point(i,j));
                theta = Math.atan2(-efieldy[ii][jj],efieldx[ii][jj]);
                
                // 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 void draw_charges() {
        int scale;
        String params = new String();
        double location[] = new double[2];
        String string = new String();
        double rmax;
        int i;
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                             RenderingHints.VALUE_ANTIALIAS_ON);
        
        rmax = xrange[1]-xrange[0];
        if (yrange[1]-yrange[0] > rmax) rmax = yrange[1]-yrange[0];

        scale = numberPixelsPerGrid;

        for (i=0; i<ncharges; i++) {
            imG.setColor(Color.orange);
            Point pt = pixelPt[i];
            imG.fillOval(pt.x-6,pt.y-6,13,13);
            imG.setColor(Color.black);
            imG.drawOval(pt.x-6,pt.y-6,13,13);

            if (TheFonts.PC_OS) {
                if (elemcharge[i] >= 0.0) 
                    imG.drawString("+",pt.x-3,pt.y+7);
                else
                    imG.drawString("-",pt.x-3,pt.y+5);
            } else {
                
                if (elemcharge[i] >= 0.0) 
                    imG.drawString("+",pt.x-5,pt.y+5);
                else
                    imG.drawString("-",pt.x-4,pt.y+5);
            }
            imG.drawString(String.valueOf(Math.abs(elemcharge[i])),
                           pt.x+9,pt.y+5);
        }
        return;
    }
    
    
    //private void plot_arrowline_local(double linewidth, double color[],
    //                                  double from[], double to[],
    //                                  double theta, Point pixelPt) {
    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.orange.darker());
        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;
    }


  public void paintImage() {
      g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                           RenderingHints.VALUE_ANTIALIAS_ON);

      // 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);

      imG.setColor(bgColor);
      imG.fillRect(0,0,xMin,mainHeight);
      imG.fillRect(0,0,mainWidth,yMin);
      imG.fillRect(0,yMax,mainWidth,mainHeight-yMax);
      imG.fillRect(xMax,0,mainWidth-xMax,mainHeight);

      drawAxis(imG);
      drawAxisTicks(imG);
  }




  private void drawAxis(Graphics g) {
      g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);

    // Draw x and y axis:
    //g.setColor(Color.black);
    //g.setColor(Color.white);
      g.setColor(Color.red.darker());
    drawLine(g,xMin,yOffset,xMin,yMax); // the y-axis
    drawLine(g,xMin,yMax,xMax+xBorder,yMax); // the x-axis
    MyArrows.drawDblUpArrow(g,xMin,yOffset);
    MyArrows.drawDblRightArrow(g,xMax+xBorder,yMax);
  }


  private void drawAxisTicks(Graphics g) {
      g2d = (Graphics2D)g;
      g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);

      // Draw x and y axis:
      //g2d.setColor(Color.black);
      //g2d.setColor(Color.white);
      g2d.setColor(Color.red.darker());
      g2d.setFont(TheFonts.sanSerif12);

      int x, y;
      // the y-axis:
      for(int i=0;i<13;i++){
          //y = (int)(i*deltaY)+yOffset+yBorder;
          y = (int)(i*deltaY)+yMin;
          g2d.drawLine(xMin-ticLength,y,xMin,y);
          String vs = dec.format(12-i);
          g2d.drawString(vs,xMin-ticLength-fm.stringWidth(vs)-2,
                       y+(int)(0.5*fontHeight));
      }
      //g.drawString("y",xMin+ticLength,(int)(1.5*fontHeight)+yOffset-10);
      //g.drawString("nm",xOffset-2,(int)(1.5*fontHeight)+yOffset-12);
      //g.drawString("y,nm",0,(int)(fontHeight-5));
      g.drawString("y(nm)",0,fontHeight-5);
      

      // On the x-axis:
      for(int i=1;i<14;i++){
          x = (int)(i*deltaX)+xMin;
          g2d.drawLine(x,yMax+ticLength,x,yMax);
          String xs = dec.format(i);
          g2d.drawString(xs,x-(int)(0.5*fm.stringWidth(xs)),
                       yMax+ticLength+fontHeight-2);
      }
      //g.drawString("x",xMax-fm.stringWidth("x"),yMax-2);
      //g.drawString("nm",xMax+fm.stringWidth("nm"),yMax+ticLength+fontHeight-2);
      //g.drawString("x,nm",xMax+fm.stringWidth("m"),yMax+ticLength+fontHeight-2);
      g.drawString(" x(nm)",xMax,yMax+ticLength+fontHeight+6);

      g2d.setFont(TheFonts.bold16);      
      return;
  }


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


  public void paint(Graphics g) {
    g.drawImage(im,0,0,this);
  }


  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);
        }
    }
    


    // Draws isolines of the given 2D function at the given function values.
    // Each of the contour lines is made up of a number of segments,
    // each of which goes from one edge of a "pixel" to another.
    // The pixels are defined as the edges of the grid defined by {\tt xvec} 
    //  and {\tt yvec}.  (NOTE: a "pixel" is really multiple pixels - scaled)

    // REAL z(nx*ny) ! arranged: (iy-1)*nx + ix
    // Above no longer true; now using potential[nx][ny]
    public void plot_contours(double xvec[], double yvec[],
                              double contours[], int ncontours) {
        int i, j, jj;
        int L;
        double xdenom, ydenom;
        double xmin1, xmax1, ymin1, ymax1;
        //double xscale, yscale;
        int jstart, jend;

        double zvalue;
        int n;
        double x[];
        double y[];

        int nx = nxgrid;
        int ny = nygrid;
        double xmin = xrange[0];
        double xmax = xrange[1];
        double ymin = yrange[0];
        double ymax = yrange[1];

        int ilep=0;

        //xscale = p_plotsize_inches(1);
        //yscale = p_plotsize_inches(2);

        imG.setColor(Color.black);

        // scale the input vectors
        //xdenom = xmax - xmin;
        //ydenom = ymax - ymin;
        xdenom = nxgrid;
        ydenom = nygrid;
        
        for (i = 0; i < nx; i++) {
            xvec[i] = xvec[i] / (xdenom - 1);  // scale from 0 to 1
        }
        for (i = 0; i < ny; i++)
            yvec[i] = yvec[i] / (ydenom - 1);  // scale from 0 to 1

        for (i=0; i<=ncontours; i++) {
            zvalue = contours[i];
            ContourReturn c = get_contour_line(xvec,yvec,nx,ny,zvalue);
            x = c.getX();
            y = c.getY();
            n = c.n;
            
            //xscale = Math.min(mainWidth-(int)xoff,mainHeight-(int)yoff);
            //yscale = xscale;
            xscale = (int)(xrange[1]-xrange[0]);
            yscale = (int)(yrange[1]-yrange[0]);

            /*
            if (i == 1) {
                System.out.println(" zvalue = "+String.valueOf(zvalue));
                System.out.println(" POTENTIALS: ");
                xscale = 3*(nxgrid-1);
                yscale = 3*(nygrid-1);
                for (j=0; j<x.length-1; j+=2) {
                    imG.drawLine((int)(x[j]*xscale+xrange[0]),
                                 (int)(y[j]*yscale+yrange[0]),
                                 (int)(x[j+1]*xscale+xrange[0]),
                                 (int)(y[j+1]*yscale+yrange[0]));
                    int jx = (int)(x[j]*(nxgrid-1));
                    int jy = (int)(y[j]*(nygrid-1));
                    System.out.println(String.valueOf(potential[jx][jy]));
                    jx = (int)(x[j+1]*(nxgrid-1));
                    jy = (int)(y[j+1]*(nygrid-1));
                    System.out.println(String.valueOf(potential[jx][jy]));
                }
            }
            */

            for (j=0; j<x.length-1; j+=2) {
                imG.drawLine((int)(x[j]*xscale+xrange[0]),
                             (int)(y[j]*yscale+yrange[0]),
                             (int)(x[j+1]*xscale+xrange[0]),
                             (int)(y[j+1]*yscale+yrange[0]));
            }
        }
        
        //  Now unscale the input vectors:
        for (i = 0; i < nx; i++)
            xvec[i] *= xdenom;
        for (i = 0; i < ny; i++)
            yvec[i] *= ydenom;
        return;
    }


    // Find all line segments in a contour line.
    // USES:
    //     get_segments
    // CALLED FROM:
    //     plot_contours
    
    // Returns x,y coords of iso-z-line specified by the given zvalue.
    // Also returns number of points in this line: n.
    /*  The line is actually many segments: moveto/lineto.
        Since there could be more than one "connected line segment"
        for any zvalue, we just return these segments.
        Figuring out how the segments are arranged into
        connected line segments is not attempted.  */

    private ContourReturn get_contour_line(double xvec[], double yvec[],
                                           int nx, int ny, double zvalue) {
        int i, j, k;
        int nmax;
        double xs[] = new double[10];
        double ys[] = new double[10];
        int nsegs;
        double x1, x2, y1, y2;
        double z11, z12, z21, z22;

        // OUTPUT PARAMETERS,  INSIDE ContourReturn
        //double x[], y[];  // actually Vectors
        //int n;
        ContourReturn c = new ContourReturn();
        
        nmax = NMAX;
        c.n = 0;   // was 0

        nx = nxgrid; // debug
        ny = nygrid; // debug
        for (i=0; i<(nx-1); i++) {
            x1 = xvec[i];
            x2 = xvec[i+1];
            for (j=0; j<(ny-1); j++) {
                y1 = yvec[j];
                y2 = yvec[j+1];
                //z11 = z[(j  )*nx + i  ];
                //z12 = z[(j+1)*nx + i  ];
                //z21 = z[(j  )*nx + i+1];
                //z22 = z[(j+1)*nx + i+1];
                z11 = potential[i][j];
                z12 = potential[i][j+1];
                z21 = potential[i+1][j];
                z22 = potential[i+1][j+1];
                SegmentReturn s = get_segments(x1,x2,y1,y2,
                                               z11,z12,z21,z22,zvalue);
                for (k=0; k<(s.len-1); k+=2) {
                    //x[n] = xs[k];
                    //y[n] = ys[k];
                    c.x.addElement(new Double(s.xs[k]));
                    c.y.addElement(new Double(s.ys[k]));
                    //x[n] = xs[k+1];
                    //y[n] = ys[k+1];
                    c.x.addElement(new Double(s.xs[k+1]));
                    c.y.addElement(new Double(s.ys[k+1]));
                    c.n++;
                    c.n++;
                } // endfor
            }
        }
        return c;
    }


    // Find all line segments in a pixel.
    //
    // CALLED FROM:
    //     get_contour_line
    //
    /* ! A pixel has 4 edges, defined by the 4 variables {\tt x1}, {\tt x2},
       {\tt y1}, and {\tt y2}.
       The values of the function at each corner of the pixel are defined
       by the variables {\tt z11}, {\tt z12}, {\tt z21}, and {\tt z22}.
       The first subscript refers to the x-coordinate, the second to the y-coordinate.
       This is shown in the figure below:

                    z12            z22
       y2         *---------------*
                  |               |
                  |               |
                  |               |
                  |               |
                  |               |
                  | z11           | z21
       y1         *---------------*
       
                  x1              x2
       
       If the z-value is between any pair of z's on the edges, we note where
       along that edge that would occur.
       If there are 2 of these, we connect them with a line segment and return it.
       If there are 3 or 4 of these, we connect them, somewhat arbitrarily,
       and return multiple line segments.
    */
    
    private SegmentReturn get_segments(double x1,double x2,double y1,double y2,
                                       double z11,double z12,double z21,double z22,
                                       double zvalue) {

        // OUTPUT STUFF:  inside SegmentReturn
        //double xs[] = new double[10];
        //double ys[] = new double[10];
        //int nsegs;
        // END OUTPUT STUFF
        SegmentReturn s = new SegmentReturn();

        double zmin, zmax, m1, m2;
        int n_intercepts;
        double xint[] = new double[10];
        double yint[] = new double[10];
        double frac;

        m1 = Math.min(z11, z12);
        m2 = Math.min(z21, z22);
        zmin = Math.min(m1, m2);
        m1 = Math.max(z11, z12);
        m2 = Math.max(z21, z22);
        zmax = Math.max(m1, m2);


        //System.out.println("get_segments: start");

        if (zvalue < zmin || zvalue > zmax) {
            // contour not possible
            s.nsegs = 0;
            return s;
        }

        // go through each edge of rectangle, finding intercepts with zvalue:
        n_intercepts=-1;

        // 1. bottom edge:
        //zmin = Math.min(z11, z21);
        //zmax = Math.max(z11, z21);
        int minat;
        if (z11 < z21) {
            zmin = z11;
            zmax = z21;
            minat = 1;
        } else {
            zmin = z21;
            zmax = z11;
            minat = 2;
        }
        if (zvalue >= zmin && zvalue <= zmax &&
            Math.abs(zmax-zmin) >= 1.0E-6) {
            n_intercepts= n_intercepts+1;
            frac=(x2-x1)*(zvalue-zmin)/(zmax-zmin);
            //if (Math.abs(zmin-z11) <= 0.1*Math.abs(zmin)) {
            if (minat == 1) {
                // zmin is at x1:
                xint[n_intercepts] = x1 + frac;
            } else {
                // zmin is at x2:
                xint[n_intercepts] = x2 - frac;
            }
            yint[n_intercepts] = y1;
        }

        // 2. left edge:
        //zmin = Math.min(z11,z12);
        //zmax = Math.max(z11,z12);
        if (z11 < z12) {
            zmin = z11;
            zmax = z12;
            minat = 1;
        } else {
            zmin = z12;
            zmax = z11;
            minat = 2;
        }
        if (zvalue >= zmin && zvalue <= zmax &&
            Math.abs(zmax-zmin) >= 1.0E-6) {
            n_intercepts= n_intercepts+1;
            frac=(y2-y1)*(zvalue-zmin)/(zmax-zmin);
            //if (Math.abs(zmin-z11) <= 0.1*Math.abs(zmin)) {
            if (minat == 1) {
                // zmin is at y1:
                yint[n_intercepts] = y1 + frac;
            } else {
                // zmin is at y2:
                yint[n_intercepts] = y2 - frac;
            }
            xint[n_intercepts] = x1;
        }

        // 3. top edge:
        //zmin = Math.min(z12,z22);
        //zmax = Math.max(z12,z22);
        if (z12 < z22) {
            zmin = z12;
            zmax = z22;
            minat = 1;
        } else {
            zmin = z22;
            zmax = z12;
            minat = 2;
        }
        if (zvalue >= zmin && zvalue <= zmax &&
            Math.abs(zmax-zmin) >= 1.0E-6) {
            n_intercepts= n_intercepts+1;
            frac=(x2-x1)*(zvalue-zmin)/(zmax-zmin);
            //if (Math.abs(zmin-z12) <= 0.1*Math.abs(zmin)) {
            if (minat == 1) {
                // zmin is at x1:
                xint[n_intercepts] = x1 + frac;
            } else {
                // zmin is at x2:
                xint[n_intercepts] = x2 - frac;
            }
            yint[n_intercepts] = y2;
        }

        // 4. right edge:
        //zmin = Math.min(z21,z22);
        //zmax = Math.max(z21,z22);
        if (z21 < z22) {
            zmin = z21;
            zmax = z22;
            minat = 1;
        } else {
            zmin = z22;
            zmax = z21;
            minat = 2;
        }
        if (zvalue >= zmin && zvalue <= zmax &&
            Math.abs(zmax-zmin) >= 1.0E-6) {
            n_intercepts= n_intercepts+1;
            frac=(y2-y1)*(zvalue-zmin)/(zmax-zmin);
            //if (Math.abs(zmin-z21) <= 0.1*Math.abs(zmin)) {
            if (minat == 1) {
                // zmin is at y1:
                yint[n_intercepts] = y1 + frac;
            } else {
                // zmin is at y2:
                yint[n_intercepts] = y2 - frac;
            }
            xint[n_intercepts] = x2;
        }

        n_intercepts++; // to put back to count instead of index
        
        if (n_intercepts == 0) {
            s.nsegs = 0;
            s.len = 0;
        } else if (n_intercepts == 1) {
            // NOT POSSIBLE
            s.nsegs = 0;
            s.len = 0;
        } else if (n_intercepts == 2) {
            // connect them with a line:
            s.nsegs = 1;
            s.xs[0] = xint[0];
            s.xs[1] = xint[1];
            s.ys[0] = yint[0];
            s.ys[1] = yint[1];
            s.len = 2;
        } else if (n_intercepts == 3) {
            // connect as 2 lines (a guess)
            s.nsegs = 2;
            s.xs[0] = xint[0];
            s.xs[1] = xint[1];
            s.ys[0] = yint[0];
            s.ys[1] = yint[1];

            s.xs[2] = xint[1];
            s.xs[3] = xint[2];
            s.ys[2] = yint[1];
            s.ys[3] = yint[2];
            s.len = 4;
        }else if (n_intercepts == 4) {
            // connect as 3 lines (a guess)
            s.nsegs = 3;
            s.xs[0] = xint[0];
            s.xs[1] = xint[1];
            s.ys[0] = yint[0];
            s.ys[1] = yint[1];

            s.xs[2] = xint[1];
            s.xs[3] = xint[2];
            s.ys[2] = yint[1];
            s.ys[3] = yint[2];

            s.xs[4] = xint[2];
            s.xs[5] = xint[3];
            s.ys[4] = yint[2];
            s.ys[5] = yint[3];
            s.len = 6;
        }
        return s;
    }


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



class ContourReturn {
    public Vector<Double> x;
    public Vector<Double> y;
    int n;

    public ContourReturn() {
        super();
        this.x = new Vector<Double>();
        this.y = new Vector<Double>();
        this.n = 0;
    }

    
    public double[] getX() {
        double newX[];
        n = x.size();
        newX = new double[n];
        for (int i=0; i<n; i++)
            newX[i] = (x.elementAt(i)).doubleValue();
        return newX;
    }
    
    public double[] getY() {
        double newY[];
        n = y.size();
        newY = new double[n];
        for (int i=0; i<n; i++)
            newY[i] = (y.elementAt(i)).doubleValue();
        return newY;
    }
}


class SegmentReturn{
    double xs[] = new double[10];
    double ys[] = new double[10];
    int nsegs;
    int len;

    public SegmentReturn() {
        super();
    }
}



class Charge {
    public int xpos, ypos;  // position of charge, in grid coordinates
    public double charge;   // signed charge of each charge, nColoumbs
    public double elemcharge;  // signed charge of each charge as mult of elem charg
    public Point pixelPt;   // actual pixel point (NOT grid point)
    
    public Charge() {
        super();
    }

    public Charge(int xpos, int ypos, double charge, double elemcharge,
                  Point pt) {
        this.xpos = xpos;
        this.ypos = ypos;
        this.charge = charge;
        this.elemcharge = elemcharge;
        this.pixelPt = pt;
    }

    public Charge(Charge ci) {
        super();
        this.xpos = ci.xpos;
        this.ypos = ci.ypos;
        this.charge = ci.charge;
        this.elemcharge = ci.charge;
        this.pixelPt = ci.pixelPt;
    }
}


class ActualVector {
    double ee;
    double theta;

    public ActualVector() {
        super();
    }

    public ActualVector(double ee, double theta) {
        this.ee = ee;
        this.theta = theta;
    }
}
