Subversion Repositories wimsdev

Rev

Blame | Last modification | View Log | RSS feed

  1. /*************************************************************************
  2. *                                                                        *
  3. *  This source code file, and compiled classes derived from it, can      *
  4. *  be used and distributed without restriction, including for commercial *
  5. *  use.  (Attribution is not required but is appreciated.)               *
  6. *                                                                        *
  7. *   David J. Eck                                                         *
  8. *   Department of Mathematics and Computer Science                       *
  9. *   Hobart and William Smith Colleges                                    *
  10. *   Geneva, New York 14456,   USA                                        *
  11. *   Email: eck@hws.edu          WWW: http://math.hws.edu/eck/            *
  12. *                                                                        *
  13. *************************************************************************/
  14.  
  15.  
  16. // The SimpleGraph applet is a configurable applet that displays the graph of
  17. // a single function of one variable.  Optionally, a point can be marked on
  18. // the graph.  The user can control the location of the point.
  19.  
  20. import java.awt.*;
  21. import java.applet.Applet;
  22. import java.util.StringTokenizer;
  23. import edu.hws.jcm.draw.*;
  24. import edu.hws.jcm.data.*;
  25. import edu.hws.jcm.functions.*;
  26. import edu.hws.jcm.awt.*;
  27.  
  28.  
  29. public class SimpleGraph extends GenericGraphApplet {
  30.  
  31.    // Declare some private variables that are created in one method in
  32.    // this class and used in a second method.
  33.  
  34.    private VariableInput xInput; // Contains the x-coordinate of the marked point.
  35.  
  36.    private Function func;   // The function that is graphed.
  37.    private Graph1D graph;   // The graph of the function.
  38.  
  39.    private DrawGeometric point;  // An oval that marks the selected point on the graph.
  40.    private DrawGeometric vLine;  // A line from the point to the x-axis.
  41.    private DrawGeometric hLine;  // A line from the point to the y-axis.
  42.  
  43.  
  44.  
  45.    protected void setUpCanvas() {  // Override this to add more stuff to the canvas.
  46.    
  47.       super.setUpCanvas();  // Do the common setup: Add the axes and
  48.  
  49.       // When setUpCanvas is called, the functionInput already exists, if one is
  50.       // to be used, since it is created in setUpBopttomPanel(), which is called
  51.       // before setUpCanvas.  If functionInput exists, add a graph of the function
  52.       // from functionInput to the canvas.  If not, create a graph of the function
  53.       // specified by the parameter named "Function".
  54.      
  55.       if (functionInput != null)
  56.          func = functionInput.getFunction(xVar);
  57.       else {
  58.          String def = getParameter("Function", " abs(" + xVar.getName() + ") ^ " + xVar.getName());
  59.          Function f = new SimpleFunction( parser.parse(def), xVar );
  60.          func = new WrapperFunction(f);
  61.       }
  62.       graph = new Graph1D(func);
  63.       Color color = getColorParam("GraphColor");
  64.       if (color != null)
  65.          graph.setColor(color);
  66.          
  67.       // If the applet is configured to mark a point on the graph, create the point and
  68.       // the lines from the point to the x- and y-axes and add them to the canvas before
  69.       // the graph.  The properties of these objects have to be set later, in setUpMainPanel(),
  70.       // because the input objects that they depend on don't exist when this method is
  71.       // called.  However, I want to add them to the canvas here so they will lie behind the
  72.       // graph and behind the border of the canvas (which is added after setUpCanvas() is
  73.       // executed).
  74.  
  75.       if (! "no".equalsIgnoreCase( getParameter("ShowPoint","yes") ) ) {
  76.          vLine = new DrawGeometric();
  77.          hLine = new DrawGeometric();
  78.          point = new DrawGeometric();
  79.          canvas.add(vLine);
  80.          canvas.add(hLine);
  81.          canvas.add(point);
  82.       }
  83.  
  84.       canvas.add(graph);  // Finally, add the graph to the canvas.
  85.  
  86.    } // end setUpCanvas()
  87.    
  88.    
  89.  
  90.    protected void setUpMainPanel() { // Override to handle the point marked on the graph
  91.    
  92.       super.setUpMainPanel(); // Do the common setup
  93.  
  94.       if ( "no".equalsIgnoreCase( getParameter("ShowPoint","yes") ) ) {
  95.          return;  // If the applet is not configured to show a point, there is nothing to do.
  96.       }
  97.      
  98.       // Create two input objects, a VariableInput and a VariableSlider.  The values of
  99.       // the two inputs will be synchronized with each other using a "Tie".  The
  100.       // minimum and maximum values represented on the slider are given by the
  101.       // the minimum and maximum x-coordinates on the CoordinateRect.  This will restrict
  102.       // the x-coodinate of the point that is marked on the graph to the range of
  103.       // x-values actually shown on the screen.
  104.      
  105.       xInput = new VariableInput();   // An input box for the x-coord of the marked point
  106.       xInput.setInputStyle(VariableInput.REAL);   // Allow only real numbers (not constant expressions)
  107.       CoordinateRect coords = canvas.getCoordinateRect();
  108.       VariableSlider xSlider = new VariableSlider( coords.getValueObject(CoordinateRect.XMIN), coords.getValueObject(CoordinateRect.XMAX) );
  109.       Value yValue = new ValueMath(func,xSlider); // Represents the y-value of the marked point.
  110.      
  111.       DisplayLabel yDisplay = new DisplayLabel(" y = #", yValue);  // Shows the y-value of the point
  112.  
  113.       // Create a panel to contain the input objects.
  114.      
  115.       JCMPanel panel = new JCMPanel(1,3);
  116.       panel.setBackground(getColorParam("PanelBackground",Color.lightGray));
  117.       JCMPanel subpanel = new JCMPanel();
  118.       String varName = getParameter("Variable","x");
  119.       subpanel.add(new Label(" " + varName + " = ", Label.CENTER), BorderLayout.WEST);
  120.       subpanel.add(xInput, BorderLayout.CENTER);
  121.       panel.add(xSlider);
  122.       panel.add(subpanel);
  123.       panel.add(yDisplay);
  124.      
  125.       // If there is a functionInput box, then the SOUTH position of the mainPanel already contains
  126.       // the inputPanel that contains that box.  If so, add the new panel to the SOUTH position of
  127.       // the inputPanel.  (This is a good place, in general, to put extra input objects.)
  128.       // If there is no inputPanel, then the SOUTH position of the mainPanel is empty, so put
  129.       // the newly created panel there.  Also, set the background color for the input panel from
  130.       // from the PanelBackground applet param.  (This is already done for inputPanel, if it exists.)
  131.      
  132.       if (inputPanel == null)
  133.          mainPanel.add(panel, BorderLayout.SOUTH);
  134.       else {
  135.          inputPanel.setBackground(getColorParam("PanelBackground",Color.lightGray));
  136.          inputPanel.add(panel, BorderLayout.SOUTH);
  137.       }
  138.  
  139.       // Set up all the data for the point and the lines from the point to the axes.
  140.       // The objects where created in setUpCanvas() and added to the canvas.
  141.  
  142.       hLine.setPoints(new Constant(0),yValue,xSlider,yValue);
  143.       hLine.setPoints(new Constant(0),yValue,xSlider,yValue);
  144.       point.setShape(DrawGeometric.CROSS);
  145.       point.setPoints(xSlider,yValue,5,5);
  146.       point.setLineWidth(3);
  147.       vLine.setPoints(xSlider,new Constant(0),xSlider,yValue);
  148.       Color c = getColorParam("LineColor", Color.lightGray);
  149.       vLine.setColor(c);
  150.       hLine.setColor(c);
  151.       c = getColorParam("DotColor", Color.gray);
  152.       point.setColor(c);
  153.  
  154.       // Now, I have to set a Controller to respond to changes in the input objects.
  155.       // I could just use the mainController, but then the data for the graph would
  156.       // be recomputed whenever the user changes the x-coordinate of the marked point.
  157.       // For effieciency, I will use a separate Controller that only recomputes the
  158.       // data for the point (not the graph) when the inputs change.
  159.      
  160.       Controller cc = new Controller();
  161.  
  162.       xInput.setOnTextChange(cc);   // cc responds when user types in the input box
  163.       xSlider.setOnUserAction(cc);  // cc responds when the user drags the slider
  164.       coords.setOnChange(cc);       // cc responds when the coordinate limits change;
  165.                                     //    this is necessary because the minimum and
  166.                                     //    maximum values on the slider have to be checked.
  167.  
  168.       cc.add( xInput );  // Check whether the values have changed.
  169.       cc.add( xSlider );
  170.  
  171.       cc.add( new Tie(xSlider,xInput) );  // synchronize values of input box and slider
  172.  
  173.       cc.add( hLine );  // Recompute the values for the point and lines.
  174.       cc.add( vLine );
  175.       cc.add( point );
  176.  
  177.       cc.add( yDisplay ); // Recompute the value displayed on the yDisplay label.
  178.  
  179.       mainController.add(cc);  // When the mainController recomputes (because function has
  180.                                //   been changed, all the stuff controlled by cc also has
  181.                                //   to be checked.
  182.      
  183.       mainController.remove(canvas);  // The mainController should not recompute the contents
  184.                                       //   of the canvas (which it would do by default).
  185.       mainController.add(graph);      // But the mainController should recompute the graph.
  186.  
  187.    } // end setUpMainPanel()
  188.    
  189.  
  190.  
  191.    protected void doLoadExample(String example) {
  192.          // This method is called when the user loads an example from the
  193.          // example menu (if there is one).  It overrides an empty method
  194.          // in GenericGraphApplet.
  195.          //   For the SimpleGraph applet, the example string should contain
  196.          // an expression that defines the function to be graphed.  This can optionally
  197.          // be followed by a semicoloon and a list of four or five numbers.
  198.          // The first four numbers give the x- and y-limits to be used for the
  199.          // example.  If they are not present, then -5,5,-5,5 is used.  The
  200.          // fifth number, if present, gives the x-coord of the marked point
  201.          // on the graph.
  202.    
  203.       int pos = example.indexOf(";");
  204.  
  205.       double[] limits = { -5,5,-5,5 };  // x- and y-limits to use
  206.      
  207.       if (pos > 0) { // get limits from example text
  208.          String limitsText = example.substring(pos+1);
  209.          example = example.substring(0,pos);
  210.          StringTokenizer toks = new StringTokenizer(limitsText, " ,");
  211.          if (toks.countTokens() >= 4) {
  212.             for (int i = 0; i < 4; i++) {
  213.                try {
  214.                    Double d = new Double(toks.nextToken());
  215.                    limits[i] = d.doubleValue();
  216.                }
  217.                catch (NumberFormatException e) {
  218.                }
  219.             }
  220.             if (toks.countTokens() > 0 && xInput != null) {
  221.                   // get x-coord of marked point from example text
  222.                try {
  223.                    Double d = new Double(toks.nextToken());
  224.                    xInput.setVal( d.doubleValue() );
  225.                }
  226.                catch (NumberFormatException e) {
  227.                }
  228.             }
  229.          }
  230.       }
  231.      
  232.       // Set up the example data and recompute everything.
  233.  
  234.       if (functionInput != null) {
  235.             // If there is a function input box, put the example text in it.
  236.          functionInput.setText(example);
  237.       }
  238.       else {
  239.            // If there is no user input, set the function in the graph directly.
  240.            // Also, in this case, func is a "WrapperFunction".  Set the
  241.            // definition of that WrapperFunction to be the same as f
  242.          try {
  243.             Function f = new SimpleFunction( parser.parse(example), xVar );
  244.             ((WrapperFunction)func).setFunction(f);
  245.          }
  246.          catch (ParseError e) {  
  247.              // There should't be parse error's in the Web-page
  248.              // author's examples!  If there are, the function
  249.              // just won't change.
  250.          }
  251.       }
  252.       CoordinateRect coords = canvas.getCoordinateRect(0);
  253.       coords.setLimits(limits);
  254.       coords.setRestoreBuffer();
  255.       mainController.compute();
  256.      
  257.    } // end doLoadExample()
  258.    
  259.  
  260.  
  261. } // end class SimpleGraph
  262.