Mega Code Archive

 
Categories / Java / Advanced Graphics
 

Draw the function

/************************************************************************* *                                                                        * *  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/            * *                                                                        * *************************************************************************/ 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.*; // The MultiApplet can display the graphs of several functions, in different colors. // By default, there is only one function, but you can configure the applet to // use more than one function with applet params. // The definitions of these functions can, optionally, use parameters whose // values are controled by sliders at the bottom of the applet.   public class MultiGraph extends GenericGraphApplet {    private Vector sliders;  // Elements of this vector are the VariableSlider                             //   objects that represent the parameter values.                             //   The sliders are created in the setUpParser() method.                                 private ExprIn[] inputs;  // The function input boxes (or null if inputs aren't used)    private Graph1D[] graphs; // The graphs of the functions, in the case function input boxes are NOT used    private int functionCt;   // Number of functions -- size of inputs or graphs array        private Color[] graphColors = { Color.magenta, new Color(0,180,0),                             Color.red, new Color(0,200,200),                            Color.orange, Color.gray, Color.blue, Color.pink };    private static class ColorPatch extends Canvas {            // a canvas with a preferred size       ColorPatch(Color c) {          setBackground(c);       }       public Dimension getPreferredSize() {          return new Dimension(25,10);       }       public void paint(Graphics g) {          g.drawRect(0,0,getSize().width-1,getSize().height-1);       }    }        private static class ExprIn extends ExpressionInput {              // Doesn't throw an error if empty, just sets function in graph to null       Graph1D graph;  // Graph associated with this function input.       Function func;  // The function of x defined by this graph.       ExprIn(String definition, Parser p, Graph1D g, Variable v) {          super(definition,p);          graph = g;          func = getFunction(v);          if (definition.trim().length() > 0)             graph.setFunction(func);       }       public void checkInput() { // (will be called during constructor -- hence the funny bit with checking if graphe is null)          boolean hasChanged = previousContents == null || !previousContents.equals(getText());          if (!hasChanged)             return;          String text = getText().trim();          if (text.length() == 0) {  // set graph's function to null so it doesn't have to do any computations.             if (graph != null)                graph.setFunction(null);             previousContents = getText();          }          else {             super.checkInput();             if (graph != null)                graph.setFunction(func);          }       }    }    protected void setUpParser() {  // Override this to add VariableSliders to parser.           // Get the data for any 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);       }              super.setUpParser();  // Call this last so function definitions                              // in applet params can use the parameter names                                                              } // 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();            private void getColors() { // get graph colors from color parameters, if any.              Vector vec = new Vector();       int ct = 0;       Color c = getColorParam("GraphColor");       if (c == null) {          ct++;          c = getColorParam("GraphColor" + ct);       }       while (true) {          if (c == null)             break;          vec.addElement(c);          ct++;          c = getColorParam("GraphColor" + ct);       }       if (vec.size() > 0) {          graphColors = new Color[vec.size()];          for (int i = 0; i < vec.size(); i++)             graphColors[i] = (Color)vec.elementAt(i);       }    }        private Vector getFunctions() {  // Read applet parms "Function", "Funcion1", ...                                     // Return a vector containing the function definition strings       Vector functions = new Vector();       int ct = 0;       String c = getParameter("Function");       if (c == null) {          ct++;          c = getParameter("Function" + ct);       }       while (true) {          if (c == null)             break;          functions.addElement(c);          ct++;          c = getParameter("Function" + ct);       }       if (functions.size() == 0)          functions.addElement( " abs( " + xVar.getName() + ") ^ " + xVar.getName() );       double[] d = getNumericParam("FunctionCount");       if (d == null || d.length == 0 || d[0] <= 0.5)          functionCt = functions.size();       else {          functionCt = (int)Math.round(d[0]);          if (functionCt < functions.size()) { // use number of functions specified as functionCt             functionCt = functions.size();          }          else {  // make extra empty functions to bring total up to functionCt             int extra = functionCt - functions.size();             for (int i = 0; i < extra; i++)                functions.addElement("");          }       }       return functions;    }        private Panel makeFunctionInput(Vector functions, int funcNum) {              // make input box for specified function            // also adds the input box to the inputs[] array       Graph1D graph = new Graph1D();       graph.setColor(graphColors[funcNum % graphColors.length]);       ExprIn in = new ExprIn((String)functions.elementAt(funcNum),parser,graph,xVar);       in.setOnUserAction(mainController);       JCMPanel p = new JCMPanel();       p.add(in,BorderLayout.CENTER);       String name;       if (functions.size() > 1)          name = " " + getParameter("FunctionName","f") + (funcNum+1) + "(" + xVar.getName() + ") = ";       else          name = " " + getParameter("FunctionName","f") +  "(" + xVar.getName() + ") = ";       p.add(new Label(name), BorderLayout.WEST);       if (graphColors.length > 1 && functions.size() > 1)          p.add(new ColorPatch( graphColors[funcNum % graphColors.length] ), BorderLayout.EAST);       inputs[funcNum] = in;       return p;    }    protected void setUpBottomPanel() {         // Overridden to create an appropriate input panel       // Create a panel holding all the function inputs and       // sliders, with a display label for each slider to show its value.              boolean funcInput = "yes".equalsIgnoreCase(getParameter("UseFunctionInput","yes"));              if ( funcInput && "yes".equalsIgnoreCase(getParameter("UseComputeButton", "yes")) ) { // make the compute button          String cname = getParameter("ComputeButtonName", "New Functions");          computeButton = new Button(cname);          computeButton.addActionListener(this);       }       Panel firstPanel = null;  // To help find a place for the compute button             getColors();       Vector functions = getFunctions();       if (!funcInput && sliders.size() == 0)  // nothing to put in the input panel          return;       JCMPanel panel = new JCMPanel();       if (! "no".equalsIgnoreCase(getParameter("TwoInputColumns","no")))          panel.setLayout(new GridLayout(0,2,12,3));       else           panel.setLayout(new GridLayout(0,1,3,3));       panel.setBackground(getColorParam("PanelBackground", Color.lightGray));       if (funcInput) { // make an input box for each function and add it to the panel          inputs = new ExprIn[functions.size()];          for (int i = 0; i < functions.size(); i++) {             Panel p = makeFunctionInput(functions,i);             if (firstPanel == null)                firstPanel = p;             panel.add(p);          }       }       else {  // just make graphs from the function definition strings.          graphs = new Graph1D[functions.size()];          for (int i = 0; i < functions.size(); i++) {             graphs[i] = new Graph1D();             graphs[i].setColor(graphColors[ i % graphColors.length ]);             String def = ((String)functions.elementAt(i)).trim();             if (def.length() > 0) {  // if the definition string is empty, leave graph's function undefined                 Function f = new SimpleFunction( parser.parse(def), xVar );                 graphs[i].setFunction(f);             }          }       }       for (int i = 0; i < sliders.size(); i++) {  // add sliders to the input panel          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);          panel.add(p);          slide.setOnUserAction(mainController);       }              if (computeButton != null) {  // find a place for the compute button!          if (functions.size() == 1)             firstPanel.add(computeButton, BorderLayout.EAST);          else if (limitsPanel == null) {             Panel p = new Panel();             p.add(computeButton);             panel.add(p);            }          // otherwise, add it at the end of setUpLimitPanel();       }              mainPanel.add(panel, BorderLayout.SOUTH);           } // end setUpBottomPanel()    protected void setUpLimitsPanel() { // add compute button if it hasn't been put somewhere else       super.setUpLimitsPanel();       if (limitsPanel != null && computeButton != null && functionCt != 1)          limitsPanel.addComponent(computeButton);    }    protected void setUpCanvas() { // Overridden to add the graph to the canvas.       super.setUpCanvas();  // Do the default setup.       // set up bottom panel has already been defined       // add the graphs to the canvas              if (graphs != null) {          for (int i = 0; i < graphs.length; i++)             canvas.add(graphs[i]);       }       else {          for (int i = 0; i < inputs.length; i++)             canvas.add(inputs[i].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 must          // be followed by a semicolon and list of zero or more numbers.          // Then there is another semicolon and one or more function definitions,          // separated by semicolons.  You can have as many function          // definitions as you have functions in your applet setup.          // (Note that having the numbers before the          // functions is different from the format of examples in all the          // other configurable applets.  This is to allow more than one function.)  Note that even if you leave          // out the numbers, you still need two semicolons.  The list of numbers has the following meaning:          // 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. Each group give the maximum, minimum, and value of a parameters that was defined          // with the "Parameter", "Parameter1", ... applet params.                 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(0,pos);          example = example.substring(pos+1);          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.       StringTokenizer toks = new StringTokenizer(example,";");       int funcNum = 0;       while (funcNum < functionCt) {          if (toks.hasMoreElements()) {  // define the function using definition from example text              String def = toks.nextToken();              if (graphs != null) {                 try {                     graphs[funcNum].setFunction(new SimpleFunction( parser.parse(def), xVar ));                  }                  catch (ParseError e) {                     graphs[funcNum].setFunction(null);                   }              }              else                 inputs[funcNum].setText(def);          }          else {  // function is undefined             if (graphs != null)                graphs[funcNum].setFunction(null);             else                inputs[funcNum].setText("");          }          funcNum++;       }       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 MultiGraph();          app.init();                    f.getContentPane().add (app);          f.pack();          f.setSize (new Dimension (500, 500));          f.setVisible(true);       }        } // end class MultiGraph                     jcm1-source.zip( 532 k)