import java.applet.Applet; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionListener; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.border.BevelBorder; import functionParser.Evaluator; import functionParser.FunctionParser; import functionParser.FunctionParsingException; /** * LCGraph.java, a class designed to take a user inputed function and graph * its level curve. * * Only works for functions of two variables. * *

* @author Andrew Trusty */ public class LCGraph extends Applet { private JTextField functionInput; private JTextField variableInput; private JTextField nInput; private JTextField xInput; private JTextField epsInput; private JPanel graph; private double h = Math.pow(10, -10); private boolean corrector = false; private boolean graphed = false; private boolean axes = false; private int xmid = 190; private int ymid = 90; private int scale = 60; private double[][] thePoints; /** * Initialize the applet. */ public void init() { setLayout(null); setSize(new Dimension(510, 300)); setBackground(Color.WHITE); graph = new JPanel(); graph.addMouseMotionListener(new MouseMotionListener() { public void mouseDragged(MouseEvent arg0) { // do nothing } public void mouseMoved(MouseEvent arg0) { displayLoc(arg0.getPoint()); } }); JScrollPane graphPane = new JScrollPane(graph); graphPane.setBorder(new BevelBorder(0)); graphPane.setBounds(10, 10, 380, 180); JPanel inputPanel = new JPanel(null); inputPanel.setBounds(0, 200, 400, 110); inputPanel.setBackground(Color.WHITE); // need epsilon, N, and x0 JLabel nLabel = new JLabel("N:"); nLabel.setBounds(10, 5, 20, 20); nInput = new JTextField("65"); nInput.setBounds(23, 5, 40, 20); JLabel epsLabel = new JLabel("epsilon:"); epsLabel.setBounds(70, 5, 45, 20); epsInput = new JTextField(".1"); epsInput.setBounds(115, 5, 60, 20); JLabel xLabel = new JLabel("X(0):"); xLabel.setBounds(183, 5, 100, 20); xInput = new JTextField("1,0"); xInput.setBounds(210, 5, 170, 20); JLabel varLabel = new JLabel("Variables (x,y):"); varLabel.setBounds(27, 30, 105, 20); variableInput = new JTextField("x,y"); variableInput.setBounds(115, 30, 170, 20); JLabel inLabel = new JLabel("Function: "); inLabel.setBounds(61, 50, 75, 20); functionInput = new JTextField("(x^2)+(y^2)"); functionInput.setBounds(115, 50, 170, 20); JButton graphButton = new JButton("Graph"); graphButton.setBounds(295, 40, 80, 20); graphButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { parseFunctionInput(functionInput.getText(), variableInput.getText(), nInput.getText(), xInput.getText(), epsInput.getText()); graphed = true; } }); JCheckBox useCorrector = new JCheckBox("Use Correction?"); useCorrector.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { corrector = !corrector; if (graphed) { parseFunctionInput(functionInput.getText(), variableInput.getText(), nInput.getText(), xInput.getText(), epsInput.getText()); } } }); useCorrector.setBackground(inputPanel.getBackground()); useCorrector.setBounds(115, 70, 200, 20); JLabel scaleLbl = new JLabel("Scale"); scaleLbl.setBounds(432, 6, 100, 30); JButton pScale = new JButton(">>"); pScale.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { scale += 3; draw(); } }); pScale.setBounds(450, 30, 50, 20); JButton mScale = new JButton("<<"); mScale.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { scale -= 3; draw(); } }); mScale.setBounds(400, 30, 50, 20); JLabel xLbl = new JLabel("X Viewpoint"); xLbl.setBounds(417, 60, 100, 20); JButton pX = new JButton(">>"); pX.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { xmid -= 3; draw(); } }); pX.setBounds(450, 80, 50, 20); JButton mX = new JButton("<<"); mX.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { xmid += 3; draw(); } }); mX.setBounds(400, 80, 50, 20); JLabel yLbl = new JLabel("Y Viewpoint"); yLbl.setBounds(417, 110, 100, 20); JButton pY = new JButton(">>"); pY.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ymid += 3; draw(); } }); pY.setBounds(450, 130, 50, 20); JButton mY = new JButton("<<"); mY.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ymid -= 3; draw(); } }); mY.setBounds(400, 130, 50, 20); inputPanel.add(useCorrector); inputPanel.add(nLabel); inputPanel.add(nInput); inputPanel.add(xLabel); inputPanel.add(xInput); inputPanel.add(epsLabel); inputPanel.add(epsInput); inputPanel.add(graphButton); inputPanel.add(varLabel); inputPanel.add(variableInput); inputPanel.add(inLabel); inputPanel.add(functionInput); add(scaleLbl); add(mScale); add(pScale); add(xLbl); add(mX); add(pX); add(yLbl); add(mY); add(pY); add(inputPanel); add(graphPane); } /** * Parses the input from the text fields and creates the necessary * variables to begin calculating the thePoints and calls the point * calculation method. *

* @param equation the string descriptioin of the equation * @param variables the variables in the equation * @param n the number of steps * @param xnot the first point * @param eps epsilon */ public void parseFunctionInput(String equation, String variables, String n, String xnot, String eps) { // SETUP VARIABLES int numVars = variables.split(",").length; String[] coords = xnot.split(","); // code only works for 2 variables, check for 2 if ((numVars == 2) && (coords.length == 2)) { try { // get number of steps to take int steps = Integer.parseInt(n); // get epsilon double epsilon = Double.parseDouble(eps); // get starting point X0 double[] point = new double[2]; point[0] = Double.parseDouble(coords[0]); point[1] = Double.parseDouble(coords[1]); // create the thePoints array and add X0 thePoints = new double[steps + 1][steps + 1]; thePoints[0] = point; // SETUP FUNCTION // create a function with the given variables FunctionParser parser = new FunctionParser(variables); // create an Evaluator with the necessary number of variables Evaluator eqEval = new Evaluator(numVars); // parse the function with the new evaluator try { parser.parse(equation, eqEval); } catch (FunctionParsingException fpe) { System.err.println("Error parsing function."); } // begin loop of point approximation calculatethePoints(steps, epsilon, eqEval); } catch (NumberFormatException nfe) { System.err.println("Invalid Input"); } } } /** * Determines the gradient at the given values using the given function * evaluator. *

* @param eqEval the funciton evaluator * @param x value * @param y value * @return gradient array */ public double[] gradientAt(Evaluator eqEval, double x, double y) { double xprime; double yprime; double fx; double fxh; double[] gradient = new double[2]; xprime = x + h; yprime = y + h; // find f(x current) fx = eqEval.evaluate(x, y); // find gradient component for the derivative of x at current x fxh = eqEval.evaluate(xprime, y); gradient[0] = (fxh - fx) / h; // find gradient component for the derivative of y at current y fxh = eqEval.evaluate(x, yprime); gradient[1] = (fxh - fx) / h; return gradient; } /** * Returns a vector perpindicular to the given vector. *

* @param vector to get a perpindicular vector for * @return a perpindicular vector */ public double[] perpindicularVector(double[] vector) { return new double[] {vector[1], -vector[0]}; } /** * Normalizes the given vector. *

* @param vector to normalize * @return the normalized vector */ public double[] normalizeVector(double[] vector) { double sum1; double sum2; double[] u = new double[2]; sum1 = vector[0] * vector[0]; sum2 = vector[1] * vector[1]; sum1 += sum2; sum1 = 1 / Math.sqrt(sum1); u[0] = sum1 * vector[0]; u[1] = sum1 * vector[1]; return u; } /** * Computes the norm of the given vector. *

* @param vector to find norm of * @return the norm */ public double vectorNorm(double[] vector) { double sum1; double sum2; sum1 = vector[0] * vector[0]; sum2 = vector[1] * vector[1]; return sum1 + sum2; } /** * Using the given values approximates enough points to fill the * Points array then calls the draw function to graph the the points. *

* @param steps how many steps to calculate * @param epsilon to us * @param eqEval the equation evaluator */ private void calculatethePoints(int steps, double epsilon, Evaluator eqEval) { double x; double y; double fxnew; double fxold; double normSqrd; double component; double[] gradient; double[] v; double[] u; for (int j = 0; j < steps; j++) { x = thePoints[j][0]; y = thePoints[j][1]; gradient = gradientAt(eqEval, x, y); // get the vector perpindicular to the gradient v = perpindicularVector(gradient); // normalize v to obtain u and multiple u by epsilon u = normalizeVector(v); // scale u by epsilon u[0] *= epsilon; u[1] *= epsilon; // add current x to this point to obtain the next x u[0] = thePoints[j][0] + u[0]; u[1] = thePoints[j][1] + u[1]; if ((j > 0) && corrector) { // Extra Credit Corrector // compute f(x,y), if not equal to f(x-1,y-1) then correct fxnew = eqEval.evaluate(u[0], u[1]); fxold = eqEval.evaluate(thePoints[j - 1][0], thePoints[j - 1][1]); if (fxnew != fxold) { // need to correct gradient = gradientAt(eqEval, u[0], u[1]); // get norm^2 of gradient normSqrd = Math.pow(vectorNorm(gradient), 2); // calculate the number to multiply by the gradient component = (fxold - fxnew) / normSqrd; // scale the component by the gradient gradient[0] *= component; gradient[1] *= component; // find new point by adding old point to changed gradient u[0] += gradient[0]; u[1] += gradient[1]; } } // set the next x thePoints[j + 1][0] = u[0]; thePoints[j + 1][1] = u[1]; } // end for loop draw(); } /** * Connects the set of thePoints into a set of lines drawn * on the graph JPanel. */ public void draw() { Graphics g = graph.getGraphics(); g.setColor(graph.getBackground()); g.fillRect(0, 0, 380, 190); g.setColor(Color.BLACK); g.drawLine(0, ymid, 380, ymid); g.drawLine(xmid, 0, xmid, 180); if (thePoints != null) { for (int i = 0; i < thePoints.length; i++) { if (i < (thePoints.length - 2)) { g.drawLine((int) (thePoints[i][0] * scale) + xmid, -(int) (thePoints[i][1] * scale) + ymid, (int) (thePoints[i + 1][0] * scale) + xmid, -(int) (thePoints[i + 1][1] * scale) + ymid); } else { g.drawLine((int) (thePoints[i][0] * scale) + xmid, -(int) (thePoints[i][1] * scale) + ymid, (int) (thePoints[0][0] * scale) + xmid, -(int) (thePoints[0][1] * scale) + ymid); } } } } /** * Displays the location of the cursor. *

* @param p the point of the cursor */ private void displayLoc(Point p) { if (!graphed && !axes) { draw(); axes = true; } Graphics g = graph.getGraphics(); g.setColor(graph.getBackground()); g.fillRect(4, 4, 55, 15); g.setColor(Color.BLACK); g.drawString(((p.x - xmid) / scale) + ", " + (-(p.y - ymid) / scale), 5, 15); } /** * Paints the applet. *

* @param arg0 the Graphics object */ public void paint(Graphics arg0) { super.paint(arg0); draw(); } }