Families Of Graphs
/************************************************************************* * * * This source code file, and compiled classes derived from it, can * * be used and distributed without restriction, including for commercial * * use. (Attribution is not required but is appreciated.) * * * * David J. Eck * * Department of Mathematics and Computer Science * * Hobart and William Smith Colleges * * Geneva, New York 14456, USA * * Email: eck@hws.edu WWW: http://math.hws.edu/eck/ * * * *************************************************************************/ // An applet belonging to the class FamiliesOfGraphs displays a graph // of a function that can depend on one or more parameters. The values of // the parameters are controlled by the user using sliders at the bottom of // the applet. import java.awt.*; import java.applet.Applet; import java.util.*; import edu.hws.jcm.draw.*; import edu.hws.jcm.data.*; import edu.hws.jcm.functions.*; import edu.hws.jcm.awt.*; public class FamiliesOfGraphs extends GenericGraphApplet { // Declare some private variables that are created in one method in // this class and used in a second method. private Function func; // The function that is graphed. private Graph1D graph; // The graph of the function. private Vector sliders; // Elements of this vector are the VariableSlider // objects that represent the parameter values. // The sliders are created in the setUpParser() method. protected void setUpParser() { // Override this to add VariableSliders to parser. // Get the data for the sliders from applet params named "Parameter", "Parameter1", ... // The sliders are created and the variables are added to the parser by the // addParameter() method, which is defined below. sliders = new Vector(); int ct = 0; String param = getParameter("Parameter"); if (param == null) { ct++; param = getParameter("Parameter" + ct); } while (true) { if (param == null) break; addParameter(param); ct++; param = getParameter("Parameter" + ct); } // If no parameters were specified in applet params, create one with name "k". if (sliders.size() == 0) addParameter("k"); super.setUpParser(); // Call this last so function definitions // in applet params can use the parameter names // that have just been added to the parser // (even though it's probably not a good idea). // Note that this also defines the independent variable, // whose name is given by the applet param "Variable" // and which is referred to as xVar in this program. VariableSlider slide = (VariableSlider)sliders.elementAt(0); String def = getParameter("Function", "sin(" + slide.getName() + " * " + xVar.getName() + ")"); parameterDefaults = new Hashtable(); // I want to set a different default value for // the "Function" applet param. parameterDefaults.put("Function",def); } // end setUpParser() private void addParameter(String data) { // Create a VariableSlider from the information in name and add it to the // Vector of sliders. The data must contain the name of the variable // associated with the slider. The name can be followed by a ";" and up to // three numbers. (If there is no ";", a space after the name will do.) // The numbers can be separated by commas, spaces, or tabs. The first // number gives the minimum value on the slider, the second gives the maximum, // and the third gives the initial value of the slider variable. double min = -5, max = 5, val = 0; // min, max, and value for slider data = data.trim(); int pos = data.indexOf(';'); if (pos < 0) pos = data.indexOf(' '); String name; // The name of the parameter if (pos < 0) { // If there is no space or ";", the data is just the name of the variable. name = data; } else { // Get the name from the front of the data, then look for min, max, and val. String nums = data.substring(pos+1); name = data.substring(0,pos).trim(); StringTokenizer toks = new StringTokenizer(nums," ,\t"); try { if (toks.hasMoreElements()) min = (new Double(toks.nextToken())).doubleValue(); if (toks.hasMoreElements()) max = (new Double(toks.nextToken())).doubleValue(); if (toks.hasMoreElements()) val = (new Double(toks.nextToken())).doubleValue(); } catch (NumberFormatException e) { min = -5; max = 5; val = 0; } } // Create the slider, adding the associated variable to the parser, and set its value. VariableSlider slide = new VariableSlider(name, new Constant(min), new Constant(max), parser); slide.setVal(val); sliders.addElement(slide); // Save the slider in the array of sliders for later use. } // end setUpParser(); protected void setUpBottomPanel() { // Overridden to add the sliders at the bottom of the applet. super.setUpBottomPanel(); // Do the default setup. // Create a panel holding all the sliders, with a display label for each slider to show its value. JCMPanel sliderPanel = new JCMPanel(); sliderPanel.setLayout(new GridLayout(0,1,3,3)); sliderPanel.setBackground(getColorParam("PanelBackground", Color.lightGray)); for (int i = 0; i < sliders.size(); i++) { JCMPanel p = new JCMPanel(); VariableSlider slide = (VariableSlider)sliders.elementAt(i); p.add(slide, BorderLayout.CENTER); p.add(new DisplayLabel(" " + slide.getName() + " = # ", new Value[] { slide.getVariable() } ), BorderLayout.EAST); sliderPanel.add(p); slide.setOnUserAction(mainController); } // If there is a functionInput box, then the SOUTH position of the mainPanel already contains // the inputPanel that contains that box. If so, add the new panel to the SOUTH position of // the inputPanel. (This is a good place, in general, to put extra input objects.) // If there is no inputPanel, then the SOUTH position of the mainPanel is empty, so put // the newly created panel there. if (inputPanel != null) inputPanel.add(sliderPanel, BorderLayout.SOUTH); else mainPanel.add(sliderPanel, BorderLayout.SOUTH); } // end setUpBottomPanel() protected void setUpCanvas() { // Overridden to add the graph to the canvas. super.setUpCanvas(); // Do the default setup. // When setUpCanvas() is called, the functionInput already exists, if one is // to be used, since it is created in setUpBopttomPanel(), which is called // before setUpCanvas. If functionInput exists, add a graph of the function // from functionInput to the canvas. If not, create a graph of the function // specified by the parameter named "Function" (or use sin(k*x) if none is specified). if (functionInput != null) func = functionInput.getFunction(xVar); else { String def = getParameter("Function"); // default value is set in setUpParser() func = new SimpleFunction( parser.parse(def), xVar ); } // Create a graph of the function and add it to the canvas. graph = new Graph1D(func); graph.setColor(getColorParam("GraphColor", Color.magenta)); canvas.add(graph); } // end setUpCanvas protected void doLoadExample(String example) { // This method is called when the user loads an example from the // example menu (if there is one). It overrides an empty method // in GenericGraphApplet. // For the FamiliesOfGraphs applet, the example string should contain // an expression that defines the function to be graphed. This can optionally // be followed by a semicolon and a list of four or more numbers. // The first four numbers give the x- and y-limits to be used for the // example. If they are not present, then -5,5,-5,5 is used. The // remaining numbers occur in groups of three and specify the minimumn, // maximum and values of the parameters, in the // same order that they were encountered in the setUpParser() method. int pos = example.indexOf(";"); double[] limits = { -5,5,-5,5 }; // x- and y-limits to use if (pos > 0) { // Get limits from example text. String nums = example.substring(pos+1); example = example.substring(0,pos); StringTokenizer toks = new StringTokenizer(nums, " ,"); if (toks.countTokens() >= 4) { for (int i = 0; i < 4; i++) { try { Double d = new Double(toks.nextToken()); limits[i] = d.doubleValue(); } catch (NumberFormatException e) { } } } int i = 0; while (i < sliders.size() && toks.hasMoreElements()) { // Look for a value for the i-th slider. try { double min = (new Double(toks.nextToken())).doubleValue(); double max = (new Double(toks.nextToken())).doubleValue(); double d = (new Double(toks.nextToken())).doubleValue(); VariableSlider slider = ((VariableSlider)sliders.elementAt(i)); slider.setMin(new Constant(min)); slider.setMax(new Constant(max)); slider.setVal(d); } catch (Exception e) { } i++; } } // Set up the example data and recompute everything. if (functionInput != null) { // If there is a function input box, put the example text in it. functionInput.setText(example); } else { // If there is no user input, set the function in the graph directly. try { func = new SimpleFunction( parser.parse(example), xVar ); graph.setFunction(func); } catch (ParseError e) { // There should't be parse error's in the Web-page // author's examples! If there are, the function // just won't change. } } CoordinateRect coords = canvas.getCoordinateRect(0); coords.setLimits(limits); coords.setRestoreBuffer(); mainController.compute(); } // end doLoadExample() public static void main(String[] a){ javax.swing.JFrame f = new javax.swing.JFrame(); Applet app = new FamiliesOfGraphs(); app.init(); f.getContentPane().add (app); f.pack(); f.setSize (new Dimension (500, 500)); f.setVisible(true); } } // end class FamiliesOfGraphs