/**
FunctionPlotter written 2004 by Jakob Magiera
jakob@magiera.net

**/

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

public class FunctionPlotter extends Applet 
                            implements ActionListener, MouseListener, ItemListener {
    
    private double startX = -5.0d;
    private double endX = 5.0d;
    private double stepX = 0.05d;
    private double maxY = 0.0d;
    private double minY = 0.0d;
    private int mouseX = 0;
    private int mouseY = 0;
    private boolean showCoords = false;
    private boolean showDeriv1 = false;
    private boolean showDeriv2 = false;
    private CalcTree functionTree = null;
    
    // GUI Fields
    private Font textFont = new Font("SansSerif", Font.PLAIN, 10);
	private GridBagLayout gridBagL;
	private GridBagLayout gridBagL2;
	private GridBagLayout gridBagL3;
	private GridBagConstraints gbc;
	private DrawingPanel drawingArea;
	private Panel inputPanel;
	private Panel optionPanel;
	private Label inputLabelFunction;
	private Label inputLabelStartX;
	private Label inputLabelEndX;
	private Label inputLabelStepX;
	private Label inputLabelZoom;
	private Label inputLabelShift;
	private TextField inputAreaFunction;
	private TextField inputAreaStartX;
	private TextField inputAreaEndX;
	private TextField inputAreaStepX;
	private TextArea outputArea;
	private Button plotButton;
	private Button zoomInButton;
	private Button zoomOutButton;
	private Button shiftLeftButton;
	private Button shiftRightButton;
	private Checkbox deriv1Checkbox;
	private Checkbox deriv2Checkbox;

    public void init() {
        
		gridBagL = new GridBagLayout();
		gridBagL2 = new GridBagLayout();
		gridBagL3 = new GridBagLayout();
		gbc = new GridBagConstraints();
		drawingArea = new DrawingPanel();
		drawingArea.addMouseListener(this);
		inputPanel = new Panel();
		optionPanel = new Panel();
		inputLabelFunction = new Label("f(x)=");
		inputLabelStartX = new Label("start:");
		inputLabelEndX = new Label("end:");
		inputLabelStepX = new Label("step:");
		inputLabelZoom = new Label("zoom:");
		inputLabelShift = new Label("shift:");
		inputAreaFunction = new TextField("atan(x)");
		inputAreaStartX = new TextField(""+startX);
		inputAreaEndX = new TextField(""+endX);
		String stepString = ""+stepX;
		inputAreaStepX = new TextField(stepString);
		outputArea = new TextArea("",5,5,TextArea.SCROLLBARS_BOTH);
		outputArea.setEditable(false);
		plotButton = new Button("plot!");
		plotButton.addActionListener(this);
		zoomInButton = new Button("+");
		zoomInButton.addActionListener(this);
		zoomOutButton = new Button("-");
		zoomOutButton.addActionListener(this);
		shiftLeftButton = new Button("<");
		shiftLeftButton.addActionListener(this);
		shiftRightButton = new Button(">");
		shiftRightButton.addActionListener(this);
		deriv1Checkbox = new Checkbox("plot 1st derivative",false);
		deriv2Checkbox = new Checkbox("plot 2nd derivative",false);
		deriv1Checkbox.addItemListener(this);
		deriv2Checkbox.addItemListener(this);
        setLayout(gridBagL);
        inputPanel.setLayout(gridBagL2);
        optionPanel.setLayout(gridBagL3);
        
        // format input panel
        
	    buildConstraints(gbc,0,0,1,1,1.1,0.0,GridBagConstraints.EAST,GridBagConstraints.NONE);
	    gridBagL2.setConstraints(inputLabelFunction,gbc);
		inputPanel.add(inputLabelFunction);
		
	    buildConstraints(gbc,1,0,1,1,6.0,0.0,GridBagConstraints.WEST,GridBagConstraints.HORIZONTAL);
	    gridBagL2.setConstraints(inputAreaFunction,gbc);
		inputPanel.add(inputAreaFunction);
		
	    buildConstraints(gbc,2,0,1,1,1.0,0.0,GridBagConstraints.EAST,GridBagConstraints.NONE);
	    gridBagL2.setConstraints(inputLabelStartX,gbc);
		inputPanel.add(inputLabelStartX);
		
	    buildConstraints(gbc,3,0,1,1,1.0,0.0,GridBagConstraints.WEST,GridBagConstraints.NONE);
	    gridBagL2.setConstraints(inputAreaStartX,gbc);
		inputPanel.add(inputAreaStartX);
		
	    buildConstraints(gbc,4,0,1,1,1.0,0.0,GridBagConstraints.EAST,GridBagConstraints.NONE);
	    gridBagL2.setConstraints(inputLabelEndX,gbc);
		inputPanel.add(inputLabelEndX);
		
	    buildConstraints(gbc,5,0,1,1,1.0,0.0,GridBagConstraints.WEST,GridBagConstraints.NONE);
	    gridBagL2.setConstraints(inputAreaEndX,gbc);
		inputPanel.add(inputAreaEndX);
		
	    buildConstraints(gbc,6,0,1,1,1.0,0.0,GridBagConstraints.EAST,GridBagConstraints.NONE);
	    gridBagL2.setConstraints(inputLabelStepX,gbc);
		inputPanel.add(inputLabelStepX);
		
	    buildConstraints(gbc,7,0,1,1,1.0,0.0,GridBagConstraints.WEST,GridBagConstraints.NONE);
	    gridBagL2.setConstraints(inputAreaStepX,gbc);
		inputPanel.add(inputAreaStepX);
		
	    buildConstraints(gbc,8,0,1,1,0.1,0.0,GridBagConstraints.EAST,GridBagConstraints.NONE);
	    gridBagL2.setConstraints(plotButton,gbc);
		inputPanel.add(plotButton);
		
	    buildConstraints(gbc,9,0,1,1,0.5,0.0,GridBagConstraints.EAST,GridBagConstraints.NONE);
	    gridBagL2.setConstraints(inputLabelZoom,gbc);
		inputPanel.add(inputLabelZoom);
		
	    buildConstraints(gbc,10,0,1,1,0.1,0.0,GridBagConstraints.WEST,GridBagConstraints.NONE);
	    gridBagL2.setConstraints(zoomInButton,gbc);
		inputPanel.add(zoomInButton);
		
	    buildConstraints(gbc,11,0,1,1,0.1,0.0,GridBagConstraints.WEST,GridBagConstraints.NONE);
	    gridBagL2.setConstraints(zoomOutButton,gbc);
		inputPanel.add(zoomOutButton);
		
	    buildConstraints(gbc,12,0,1,1,1.0,0.0,GridBagConstraints.EAST,GridBagConstraints.NONE);
	    gridBagL2.setConstraints(inputLabelShift,gbc);
		inputPanel.add(inputLabelShift);
		
	    buildConstraints(gbc,13,0,1,1,0.1,0.0,GridBagConstraints.WEST,GridBagConstraints.NONE);
	    gridBagL2.setConstraints(shiftLeftButton,gbc);
		inputPanel.add(shiftLeftButton);
		
	    buildConstraints(gbc,14,0,1,1,0.1,0.0,GridBagConstraints.WEST,GridBagConstraints.NONE);
	    gridBagL2.setConstraints(shiftRightButton,gbc);
		inputPanel.add(shiftRightButton);
		
		// format option panel
		
	    buildConstraints(gbc,0,0,1,1,0.1,0.0,GridBagConstraints.WEST,GridBagConstraints.NONE);
	    gridBagL3.setConstraints(deriv1Checkbox,gbc);
		optionPanel.add(deriv1Checkbox);
		
	    buildConstraints(gbc,1,0,1,1,0.1,0.0,GridBagConstraints.WEST,GridBagConstraints.NONE);
	    gridBagL3.setConstraints(deriv2Checkbox,gbc);
		optionPanel.add(deriv2Checkbox);
		
		// drawing area
        
	    buildConstraints(gbc,0,0,1,1,1.0,9.0,GridBagConstraints.WEST,GridBagConstraints.BOTH);
	    gridBagL.setConstraints(drawingArea,gbc);
		add(drawingArea);
		
		// input panel
		
	    buildConstraints(gbc,0,1,1,1,1.0,0.0,GridBagConstraints.WEST,GridBagConstraints.BOTH);
	    gridBagL.setConstraints(inputPanel,gbc);
		add(inputPanel);
		
		// option panel
		
	    buildConstraints(gbc,0,2,1,1,1.0,0.0,GridBagConstraints.WEST,GridBagConstraints.BOTH);
	    gridBagL.setConstraints(optionPanel,gbc);
		add(optionPanel);
		
		// output area
		
	    buildConstraints(gbc,0,3,1,1,1.0,0.0,GridBagConstraints.WEST,GridBagConstraints.BOTH);
	    gridBagL.setConstraints(outputArea,gbc);
		add(outputArea);
		
		// set the background color
		drawingArea.setBackground(new Color(0.8f,0.8f,0.8f));
		inputPanel.setBackground(new Color(0.8f,0.8f,0.8f));
		inputLabelFunction.setBackground(new Color(0.8f,0.8f,0.8f));
		inputLabelStartX.setBackground(new Color(0.8f,0.8f,0.8f));
		inputLabelEndX.setBackground(new Color(0.8f,0.8f,0.8f));
		inputLabelStepX.setBackground(new Color(0.8f,0.8f,0.8f));
		
		// calculate the first tree
		try {
		    functionTree = buildTree(inputAreaFunction.getText());
		} catch (ScanException se) {
		    output(se.getMessage());
		}
    }

    public void output(String s) {
        outputArea.append(s+"\n");
    }

    public void start() {
    }

    public void stop() {
    }
    
    public void actionPerformed(ActionEvent e) {
        String cmd = e.getActionCommand();
        try {
            startX = Double.parseDouble(inputAreaStartX.getText());
        } catch (NumberFormatException nfe ) {
            output("wrong number format in 'start'");
        }
        try {
            endX = Double.parseDouble(inputAreaEndX.getText());
        } catch (NumberFormatException nfe ) {
            output("wrong number format in 'end'");
        }
        try {
            stepX = Double.parseDouble(inputAreaStepX.getText());
        } catch (NumberFormatException nfe ) {
            output("wrong number format in 'step'");
        }
        double spanX = Math.abs(endX-startX);
        if ( cmd.equals("+") ) {
            // zoom in
            startX += spanX*0.05;
            endX -= spanX*0.05;
            inputAreaStartX.setText(""+startX);
            inputAreaEndX.setText(""+endX);
        }
        else if ( cmd.equals("-") ) {
            // zoom out
            startX -= spanX*0.05;
            endX += spanX*0.05;
            inputAreaStartX.setText(""+startX);
            inputAreaEndX.setText(""+endX);
        }
        else if ( cmd.equals("<") ) {
            // shift left
            startX -= spanX*0.05;
            endX -= spanX*0.05;
            inputAreaStartX.setText(""+startX);
            inputAreaEndX.setText(""+endX);
        }
        else if ( cmd.equals(">") ) {
            // shift right
            startX += spanX*0.05;
            endX += spanX*0.05;
            inputAreaStartX.setText(""+startX);
            inputAreaEndX.setText(""+endX);
        }
        
        try {
            functionTree = buildTree(inputAreaFunction.getText());
            //output("Tree:"+functionTree.getStringTraverse());
        } catch(ScanException se) {
            output(se.getMessage());
        }
        drawingArea.repaint();
    }
    
    public void mouseClicked(MouseEvent e) {
    }
    public void mouseEntered(MouseEvent e) {
    }
    public void mouseExited(MouseEvent e) {
    }  
    public void mouseMoved(MouseEvent e) {
    }
    public void mousePressed(MouseEvent e) {
        mouseX = e.getX();
        mouseY = e.getY();
        showCoords = true;
        drawingArea.repaint();
    }  
    public void mouseReleased(MouseEvent e)  {
        showCoords = false;
        drawingArea.repaint();
    }
    
    public void itemStateChanged(ItemEvent e) {
        if ( e.getItem().equals("plot 1st derivative") ) {
            showDeriv1 = e.getStateChange() == ItemEvent.SELECTED;
        }
        else if ( e.getItem().equals("plot 2nd derivative") ) {
            showDeriv2 = e.getStateChange() == ItemEvent.SELECTED;
        }
        drawingArea.repaint();
    }


    /////////////////////////////
    // internal methods
    /////////////////////////////
    
    
	private void buildConstraints(GridBagConstraints gbc,int x,int y, int width, int height,double weightx, double weighty,
	                                int anchor,int fill){
		gbc.gridx = x;
		gbc.gridy = y;
		gbc.gridwidth = width;
		gbc.gridheight = height;
		gbc.weightx = weightx;
		gbc.weighty = weighty;
		gbc.fill = fill;
		gbc.anchor = anchor;
		gbc.ipady = 5;
	}
	
	private CalcTree buildTree(String function) throws ScanException {
	    // clean function of any spaces
	    function = function.replaceAll("\\s+","");
	    CalcTree tree = new CalcTree();
	    CalcTree currentTree = tree;
	    String nextNumber = "";
	    String nextOperator = "";
	    boolean numDone = false;
	    boolean op1Done = false;
	    boolean op2Done = false;
	    boolean skipMode = false; // needed for parentheses
	    for(int i=0; i<function.length(); i++) {
	        String s = ""+function.charAt(i);
            //output("char:"+s+" tree:"+tree.getStringTraverse()+" ctree:"+currentTree.getStringTraverse());
            
	        if ( s.equals("(") ) {
	            CalcTree subTree = buildTree(function.substring(i+1));
	            if ( currentTree.getParent() != null ) {
	                if ( currentTree.getContents().type == OperatorNode.TYPE_UNARY_OPERATOR ) {
	                    currentTree.setRight(subTree);
	                } else {
    	                currentTree.getParent().setRight(subTree);
    	                subTree.setParent(currentTree.getParent());
	                    currentTree = subTree;
    	            }
	            } else {
	                if ( currentTree.getContents().type == OperatorNode.TYPE_UNARY_OPERATOR ) {
	                    currentTree.setRight(subTree);
	                } else {
	                    tree = subTree;
	                    currentTree = subTree;
    	            }
	            }
	            skipMode = true;
	        }
	        else if ( s.equals(")") ) {
	            if ( ! skipMode ) {
	                return tree;
	            }
	            skipMode = false;
	        } else if ( s.matches("[\\d\\.]") ) {
	            while(s.matches("[\\d\\.]")) {
	                nextNumber += s;
	                i++;
	                if ( i >= function.length() ) {
	                    break;
	                }
	                s = ""+function.charAt(i);
	            }
	            i--;
	            numDone = true;
	        }
	        else if ( s.equals("x") ) {
	            numDone = true;
	            nextNumber = s;
	        }
	        else if ( s.equals("E") ) {
	            numDone = true;
	            nextNumber = s;
	        }
	        else if ( s.equals("+") ) {
	            op2Done = true;
	            nextOperator = s;
	        }
	        else if ( s.equals("-") ) {
	            op2Done = true;
	            nextOperator = s;
	        }
	        else if ( s.equals("*") ) {
	            op2Done = true;
	            nextOperator = s;
	        }
	        else if ( s.equals("/") ) {
	            op2Done = true;
	            nextOperator = s;
	        }
	        else if ( s.equals("^") ) {
	            op2Done = true;
	            nextOperator = s;
	        }
	        else if ( s.matches("[sincotaqrlge]") ) {
	            while(s.matches("[sincotaqrlge]")) {
	                nextOperator += s;
	                i++;
	                if ( i >= function.length() ) {
	                    break;
	                }
	                s = ""+function.charAt(i);
	            }
	            op1Done = true;
	            i--;
	        }
	        else if ( s.equals("P") ) {
	            nextNumber += s;
	            i++;
	            s = ""+function.charAt(i);
	            if(s.equals("I")) {
	                nextNumber += s;
	            }
	            numDone = true;
	        }
	        else {
	            throw new ScanException("Unknown symbol: "+s);
	        }
	        
	        if ( skipMode ) {
	            nextNumber = "";
	            nextOperator = "";
	            continue;
	        }
	        
	        
	        
	        if ( numDone && ! nextNumber.equals("") ) {
	            OperatorNode node = new OperatorNode(OperatorNode.TYPE_NUMBER,nextNumber);
	            if ( currentTree.getContents() != null ) {
	                if ( currentTree.getContents().type == OperatorNode.TYPE_UNARY_OPERATOR ) {
	                    currentTree.getRight().setContents(node);
	                } else {
    	                currentTree.setContents(node);
    	            }
	            } else {
	                currentTree.setContents(node);
	            }
	            numDone = false;
	            nextNumber = "";
	        }
	        
	        if ( op2Done && ! nextOperator.equals("") ) {
	            // create a new opNode with the current Operator
	            OperatorNode nodeContents = new OperatorNode(OperatorNode.TYPE_BINARY_OPERATOR,nextOperator);
	            // create a new treeNode that contains the opNode
	            CalcTree node = new CalcTree();
	            node.setContents(nodeContents);
	            // set the left part to be the currentTree
	            node.setLeft(currentTree);
	            // right part is a new tree
	            node.setRight(new CalcTree());
	            node.getRight().setParent(node);
	            // if the currentTree had a parent node, 
	            // let the new node have this parent now
	            if ( currentTree.getParent() != null ) {
	                currentTree.getParent().setRight(node);
	                node.setParent(currentTree.getParent());
	            } else {
	                tree = node;
	            }
	            currentTree.setParent(node);
	            currentTree = node.getRight();
	            op2Done = false;
	            nextOperator = "";
	        }
	        
	        if ( op1Done && ! nextOperator.equals("") ) {
	            // create a new opNode with the current Operator
	            OperatorNode nodeContents = new OperatorNode(OperatorNode.TYPE_UNARY_OPERATOR,nextOperator);
	            // create a new treeNode that contains the opNode
	            CalcTree node = new CalcTree();
	            node.setContents(nodeContents);
	            // set the right part to be the currentTree
	            node.setRight(currentTree);
	            // right part is a new tree
	            node.setLeft(new CalcTree());
	            node.getLeft().setParent(node);
	            // if the currentTree had a parent node, 
	            // let the new node have this parent now
	            if ( currentTree.getParent() != null ) {
	                currentTree.getParent().setRight(node);
	                node.setParent(currentTree.getParent());
	            } else {
	                tree = node;
	            }
	            currentTree.setParent(node);
	            currentTree = node;
	            op1Done = false;
	            nextOperator = "";
	        }
	        
	    }
	    
	    return tree;
	}

    
    /////////////////////////////
    // internal classes
    /////////////////////////////
    
    private class DrawingPanel extends Panel {
        
    
        public void paint(Graphics g) {
            g.setFont(textFont);
            double sizeY = getHeight();
            double sizeX = getWidth();
            double spanX = Math.abs(endX-startX);
            g.setColor(Color.black);
            maxY = 0.0d;
            minY = 0.0d;
            
            // step optimization
            // to avoid too small/big steps
            if ( spanX/stepX > sizeX ) {
                stepX = spanX/sizeX;
                inputAreaStepX.setText(""+stepX);
            }
            if ( spanX/stepX < sizeX/4 ) {
                stepX = 2*spanX/sizeX;
                inputAreaStepX.setText(""+stepX);
            }
            
            // first get maxY and minY
            int nvalues = (int)Math.round(spanX/stepX) + 1;
            double values[] = new double[nvalues];
            int count = 0;
            for(double x=startX; x<=endX; x += stepX) {
                
                double y = 0.0;
                try {
                    y = functionTree.calculateTraverse(x);
                } catch( CalcException ce) {
                    output(ce.getMessage());
                    break;
                }
                //output("x:"+x+" y:"+y);
                values[count] = y;
                if ( y > maxY ) {
                    maxY = y;
                }
                if ( y < minY ) {
                    minY = y;
                }
                count++;
            }
            
            // avoid too small y span
            double spanY = Math.abs(minY-maxY);
            if ( spanY < 1.0d ) {
                minY = minY -1.0d;
                maxY = maxY + 1.0d;
                spanY = Math.abs(minY-maxY);
            }
            
            // draw the axes
            // x-axis
            double yPercent = 1-(-minY)/spanY;
            int yPoint = (int)((sizeY-1)*yPercent);
            g.drawLine(0,yPoint, (int)sizeX-1, yPoint);
            
            // y-axis
            double xPercent = (-startX)/spanX;
            int xPoint = (int)((sizeX-1)*xPercent);
            g.drawLine(xPoint,0, xPoint, (int)sizeY-1);
            
            // draw min value on y-axis
            g.drawLine(xPoint-2, (int)sizeY-1, xPoint+2, (int)sizeY-1);
            String stringValue = ""+minY;
            g.drawString(stringValue,xPoint+2,(int)sizeY-1);
            
            // draw max value on y-axis
            g.drawLine(xPoint-2,0, xPoint+2, 0);
            stringValue = ""+maxY;
            g.drawString(stringValue,xPoint+2,10);
            
            
            // draw min value on x-axis
            g.drawLine(0,yPoint-2, 0, yPoint+2);
            stringValue = ""+startX;
            g.drawString(stringValue,0,yPoint-2);
            
            // draw max value on x-axis
            g.drawLine((int)sizeX-1, yPoint-2, (int)sizeX-1, yPoint+2);
            stringValue = ""+endX;
            // calculate width of string
            Graphics2D g2d = (Graphics2D)g;
            java.awt.geom.Rectangle2D rect = textFont.getStringBounds(stringValue,0,stringValue.length(),g2d.getFontRenderContext());
            // and draw it at the appropriate position
            g.drawString(stringValue,(int)sizeX-(int)rect.getWidth()-1,yPoint-2);
            
            
            // then plot the curve
            count = 0;
            int pointsX[] = new int[nvalues];
            int pointsY[] = new int[nvalues];
            int pointsYD1[] = new int[nvalues];
            int pointsYD2[] = new int[nvalues];
            double valuesD1[] = new double[nvalues];
            double valuesD2[] = new double[nvalues];
            for(double x=startX; x<=endX; x += stepX) {
                g.setColor(Color.red);
                double y = values[count];
                double percentX = (x-startX)/spanX;
                int pointX = (int)(sizeX*percentX);
                double percentY = 1 - (y-minY)/spanY;
                int pointY = (int)(sizeY*percentY);
                pointsX[count] = pointX;
                pointsY[count] = pointY;
                if ( count > 0 ) {
                    g.drawLine(pointX,pointY,pointsX[count-1],pointsY[count-1]);
                } else {
                    g.drawRect(pointX,pointY,0,0);
                }
                
                // plot the first and second derivative
                if ( showDeriv1 || showDeriv2 ) {
                    valuesD1[count] = y;
                    valuesD2[count] = valuesD1[count];
                    if ( count > 0 ) {
                        valuesD1[count] = (y-values[count-1]) / stepX;
                        if ( showDeriv2 ) {
                            valuesD2[count] = (valuesD1[count]-valuesD1[count-1]) / stepX;
                        }
                    } 
                    
                    double percentYD1 = 1 - (valuesD1[count]-minY)/spanY;
                    pointsYD1[count] = (int)(sizeY*percentYD1);
                    if ( showDeriv2 ) {
                        double percentYD2 = 1 - (valuesD2[count]-minY)/spanY;
                        pointsYD2[count] = (int)(sizeY*percentYD2);
                    }
                    
                    if ( count > 1 ) {
                        if ( showDeriv1 ) {
                            g.setColor(Color.blue);
                            g.drawLine(pointX,pointsYD1[count],pointsX[count-1],pointsYD1[count-1]);
                        }
                        if ( showDeriv2 ) {
                            g.setColor(Color.green);
                            g.drawLine(pointX,pointsYD2[count],pointsX[count-1],pointsYD2[count-1]);
                        }
                    } else {
                        if ( showDeriv1 ) {
                            g.setColor(Color.blue);
                            g.drawRect(pointX,pointsYD1[count],0,0);
                        }
                        if ( showDeriv2 ) {
                            g.setColor(Color.green);
                            g.drawRect(pointX,pointsYD2[count],0,0);
                        }
                    }
                }
                
                    
                
                // if the mouse is pressed and near the curve,
                // show the coordinates
                if ( showCoords ) {
                    g.setColor(Color.black);
                    // main curve
                    String coords = "";
                    yPoint = 0;
                    if ( Math.abs(pointX-mouseX) < 2 && Math.abs(pointY-mouseY) < 4 ) {
                        coords = "x="+x+" y="+values[count];
                        yPoint = pointY;
                    }
                    // 1st derivative
                    if ( showDeriv1 && Math.abs(pointX-mouseX) < 2 && Math.abs(pointsYD1[count]-mouseY) < 4 ) {
                        coords = "x="+x+" y="+valuesD1[count];
                        yPoint = pointsYD1[count];
                    }
                    // 2nd derivative
                    if ( showDeriv2 && Math.abs(pointX-mouseX) < 2 && Math.abs(pointsYD2[count]-mouseY) < 4 ) {
                        coords = "x="+x+" y="+valuesD2[count];
                        yPoint = pointsYD2[count];
                    }
                    //output("mx:"+mouseX+" my:"+mouseY+" x:"+pointX+" y:"+pointY);
                     // calculate width of string
                    rect = textFont.getStringBounds(coords,0,coords.length(),g2d.getFontRenderContext());
                    int xdiff = 0;
                    int ydiff = 0;
                    // make sure it's not drawn outside the panel
                    if ( yPoint - rect.getHeight() < 0 ) {
                        ydiff = (int)rect.getHeight();
                    }
                    if ( pointX + rect.getWidth() > sizeX-1 ) {
                        xdiff = (int)rect.getWidth();
                    }
                    // draw the coords
                    g.drawString(coords,pointX-xdiff,yPoint+ydiff);
                }
                
                //output(/*"x:"+x+"y:"+y+*/"sx:"+sizeX+" sy:"+sizeY+" px:"+pointX+"py:"+pointY);
                count++;
            }
        }
    }
    
}

