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 ParametricCurve applet is a configurable applet that displays a parametric
  17. // curve defined by two functions of a parameter t.  There can be a "Tracer" button
  18. // on the applet.  When the user clicks this button, a crosshair is moved along
  19. // the curve from beginning to end.
  20.  
  21. import java.awt.*;
  22. import java.applet.Applet;
  23. import java.util.StringTokenizer;
  24. import java.util.Hashtable;
  25. import edu.hws.jcm.draw.*;
  26. import edu.hws.jcm.data.*;
  27. import edu.hws.jcm.functions.*;
  28. import edu.hws.jcm.awt.*;
  29.  
  30.  
  31.  
  32. public class Parametric extends GenericGraphApplet {
  33.  
  34.    // Declare some private variables that are created in one method in
  35.    // this class and used in a second method.
  36.  
  37.    private Function xFunc,yFunc;    // The functions that are graphed.
  38.    private ParametricCurve graph;   // The graph of the function.
  39.    private Animator tracer;         // for tracing the curve by moving a crosshair along it
  40.    private Crosshair crosshair;     // Crosshair used for tracing the graph
  41.    private VariableInput tMin,tMax; // for inputting limits on t
  42.    private VariableInput tIntervals; // for inutting the number of intervals into which the t-range is divided
  43.    private ExpressionInput functionInput2;  // for inputting yFunc; xFunc is input in functionInput
  44.    
  45.    protected void setUpParameterDefaults() {
  46.       parameterDefaults = new Hashtable();
  47.       parameterDefaults.put("TwoLimitsColumns", "yes");
  48.       parameterDefaults.put("Variable","t");
  49.       parameterDefaults.put("XName","x");  // we need this so that xmin and xmax boxes are labeled correctly;
  50.                                            // without it, the name would come from the variable name, t, instead of x
  51.       parameterDefaults.put("FunctionLabel", "  " + getParameter("XName") + "(" + getParameter("Variable") + ") = ");
  52.       parameterDefaults.put("FunctionLabel2", "  " + getParameter("YName","y") + "(" + getParameter("Variable") + ") = ");
  53.    }
  54.  
  55.    protected void setUpCanvas() {  // Override this to add more stuff to the canvas.
  56.    
  57.       super.setUpCanvas();  // Do the common setup: Add the axes and
  58.  
  59.       // When setUpCanvas is called, the function inputs already exist, if they are
  60.       // to be used, since they are created in setUpBopttomPanel(), which is called
  61.       // before setUpCanvas().  If functionInput exists, add a graph of the functions
  62.       // from functionInput and functionInput2 to the canvas.  If not, create a graph
  63.       // of the functions specified by the parameters named "Function" and "Function2".
  64.      
  65.       if (functionInput != null) {
  66.          xFunc = functionInput.getFunction(xVar);
  67.          yFunc = functionInput2.getFunction(xVar);
  68.       }
  69.       else {
  70.          String xFuncDef = " cos(" + xVar.getName() + ") + cos(3*" + xVar.getName() + ")";
  71.          String yFuncDef = " sin(4*" + xVar.getName() + ") - sin(2*" + xVar.getName() + ")";
  72.          xFuncDef = getParameter("Function", xFuncDef);
  73.          yFuncDef = getParameter("Function2", yFuncDef);
  74.          Function f = new SimpleFunction( parser.parse(xFuncDef), xVar );
  75.          xFunc = new WrapperFunction(f);
  76.          f = new SimpleFunction( parser.parse(yFuncDef), xVar );
  77.          yFunc = new WrapperFunction(f);
  78.       }
  79.       graph = new ParametricCurve(xFunc,yFunc);
  80.       Color color = getColorParam("CurveColor");
  81.       if (color != null)
  82.          graph.setColor(color);
  83.          
  84.       // if inputs are desired to control the parameter on the curve, set them up here
  85.          
  86.       if  ("no".equalsIgnoreCase(getParameter("UseParamInputs","yes"))) {
  87.          tMin = new VariableInput(xVar.getName() + "Start",getParameter("ParameterMin","-2"));
  88.          tMax = new VariableInput(xVar.getName() + "End",getParameter("ParameterMax","2"));
  89.          tIntervals = new VariableInput("Intervals", getParameter("Intervals","200"));
  90.          tIntervals.setInputStyle(VariableInput.INTEGER);
  91.          tIntervals.setMin(1);
  92.          tIntervals.setMax(5000);
  93.          tMin.setOnUserAction(mainController);
  94.          tMax.setOnUserAction(mainController);
  95.          tIntervals.setOnUserAction(mainController);
  96.          graph.setTMin(tMin);
  97.          graph.setTMax(tMax);
  98.          graph.setIntervals(tIntervals);
  99.          if (limitsPanel != null) {
  100.                // componets will be added to limitsPanel in setUpLimitsPanel()
  101.             mainController.add(tMin);  // This is not done automatically, since they are in a limits panel  
  102.             mainController.add(tMax);
  103.             mainController.add(tIntervals);
  104.          }
  105.          else {
  106.             JCMPanel ap = new JCMPanel(9,0);
  107.             ap.setBackground(getColorParam("PanelBackground", Color.lightGray));
  108.             ap.add(new Label(tMin.getName()));
  109.             ap.add(tMin);
  110.             ap.add(new Label());
  111.             ap.add(new Label(tMax.getName()));
  112.             ap.add(tMax);
  113.             ap.add(new Label());
  114.             ap.add(new Label(tIntervals.getName()));
  115.             ap.add(tIntervals);
  116.             ap.add(new Label());
  117.             mainPanel.add(ap,BorderLayout.EAST);
  118.          }
  119.       }
  120.       else {
  121.          try {
  122.             graph.setTMin( new Constant((new Double(getParameter("ParameterMin","-2"))).doubleValue()) );
  123.             graph.setTMax( new Constant((new Double(getParameter("ParameterMax","2"))).doubleValue()) );
  124.             graph.setIntervals( new Constant((new Double(getParameter("Intervals","25"))).doubleValue()) );
  125.          }
  126.          catch (NumberFormatException e) {
  127.          }
  128.       }
  129.  
  130.       // If the applet is configured to have a tracer button, create it and add the crosshair to the canvas
  131.  
  132.       if (! "no".equalsIgnoreCase( getParameter("UseTracer","yes") ) ) {
  133.          tracer = new Animator();
  134.          tracer.setMin(graph.getTMin());
  135.          tracer.setMax(graph.getTMax());
  136.          tracer.setUndefinedWhenNotRunning(true);
  137.          tracer.setStartButtonName("Trace Curve!");
  138.          double[] d = getNumericParam("TracerIntervals");
  139.          int ints;
  140.          if (d == null || d.length != 1)
  141.             ints = 100;
  142.          else
  143.             ints = (int)Math.round(d[0]);
  144.          if (ints <= 0)
  145.             tracer.setIntervals(graph.getIntervals());
  146.          else
  147.             tracer.setIntervals(ints);
  148.          Variable v = tracer.getValueAsVariable();
  149.          crosshair = new Crosshair( new ValueMath(xFunc,v), new ValueMath(yFunc,v) );
  150.          crosshair.setLineWidth(3);
  151.          crosshair.setColor(getColorParam("CrosshairColor",Color.gray));
  152.          canvas.add(crosshair);
  153.            
  154.          if (inputPanel != null) {
  155.             inputPanel.add(tracer,BorderLayout.WEST);
  156.          }
  157.          else if (limitsPanel == null) {
  158.             Panel p = new Panel();
  159.             p.add(tracer);
  160.             mainPanel.add(p,BorderLayout.SOUTH);
  161.          }
  162.          // if inputPanel is null but limitsPanel is not null, the tracer will be
  163.          //    added to the limit control panel in setUpLimitsPanel()
  164.       }
  165.  
  166.       canvas.add(graph);  // Finally, add the graph to the canvas.
  167.  
  168.    } // end setUpCanvas()
  169.    
  170.  
  171.    protected void setUpLimitsPanel() {
  172.       super.setUpLimitsPanel();
  173.       if (limitsPanel != null && tMin != null) {  // add parameter inputs to limits panel
  174.           limitsPanel.addComponentPair(tMin,tMax);
  175.           limitsPanel.addComponent(tIntervals);
  176.       }
  177.       if (inputPanel == null && tracer != null && limitsPanel != null)  {
  178.           limitsPanel.addComponent(tracer);
  179.       }
  180.    }
  181.  
  182.    
  183.    protected void setUpBottomPanel() { // override this to make a panel containing inputs for two functions
  184.       if ( ! "no".equalsIgnoreCase(getParameter("UseFunctionInput", "yes")) ) {
  185.      
  186.          inputPanel = new JCMPanel();
  187.          inputPanel.setBackground( getColorParam("PanelBackground", Color.lightGray) );
  188.          Panel in = new JCMPanel(2,1);
  189.          inputPanel.add(in,BorderLayout.CENTER);
  190.        
  191.          if ( ! "no".equalsIgnoreCase(getParameter("UseComputeButton", "yes")) ) {
  192.             String cname = getParameter("ComputeButtonName", "New Functions");
  193.             computeButton = new Button(cname);
  194.             inputPanel.add(computeButton, BorderLayout.EAST);
  195.             computeButton.addActionListener(this);
  196.          }
  197.  
  198.          String varName = getParameter("Variable");
  199.          String def = getParameter("Function");
  200.          if (def == null)
  201.             def = "cos(" + varName + ") + cos(3*" + varName + ")";
  202.          functionInput = new ExpressionInput(def,parser);
  203.          String label = getParameter("FunctionLabel");
  204.          if ("none".equalsIgnoreCase(label))
  205.             in.add(functionInput);
  206.          else {
  207.             Panel p = new JCMPanel();
  208.             p.add(functionInput,BorderLayout.CENTER);
  209.             p.add( new Label(label), BorderLayout.WEST );
  210.             in.add(p);
  211.          }
  212.          
  213.          def = getParameter("Function2");
  214.          if (def == null)
  215.             def = "sin(4*" + varName + ") - sin(2*" + varName + ")";
  216.          functionInput2 = new ExpressionInput(def,parser);
  217.          label = getParameter("FunctionLabel2");
  218.          if ("none".equalsIgnoreCase(label))
  219.             in.add(functionInput2);
  220.          else {
  221.             Panel p = new JCMPanel();
  222.             p.add(functionInput2,BorderLayout.CENTER);
  223.             p.add( new Label(label), BorderLayout.WEST );
  224.             in.add(p);
  225.          }
  226.          
  227.          mainPanel.add(inputPanel, BorderLayout.SOUTH);
  228.          functionInput.setOnUserAction(mainController);
  229.          functionInput2.setOnUserAction(mainController);
  230.       }
  231.    }
  232.      
  233.  
  234.    protected void setUpMainPanel() { // Override to set up controller for tracer, if there is one
  235.    
  236.       super.setUpMainPanel(); // Do the common setup
  237.  
  238.       if ( tracer == null ) {
  239.          return;  // If the applet is not configured to use a trace button, there is nothing to do.
  240.       }
  241.  
  242.       Controller traceController = new Controller();  // A controler that will only recompute the crosshair position
  243.       traceController.add(tracer);
  244.       traceController.add(crosshair);
  245.       tracer.setOnChange(traceController);
  246.  
  247.    } // end setUpMainPanel()
  248.    
  249.    protected void doLoadExample(String example) {
  250.          // This method is called when the user loads an example from the
  251.          // example menu (if there is one).  It overrides an empty method
  252.          // in GenericGraphApplet.
  253.          //   For the Parametric applet, the example string should contain
  254.          // two expression that defines the curve to be graphed, separated
  255.          // by a semicolon.  This can optionally
  256.          // be followed by another semicolon and a list of four to seven numbers.
  257.          // The first four numbers give the x- and y-limits to be used for the
  258.          // example.  If they are not present, then -5,5,-5,5 is used.  The
  259.          // next three numbers specify the minimum value for the parameter, the
  260.          // maximum value, and the number of intervals into which the range of
  261.          // parameter values is divided.
  262.          
  263.       if (tracer != null)
  264.          tracer.stop();
  265.          
  266.       int pos = example.indexOf(";");
  267.       if (pos == -1)
  268.          return; // illegal example -- must have two functions
  269.       String example2 = example.substring(pos+1);
  270.       example = example.substring(0,pos);
  271.       pos = example2.indexOf(";");  
  272.      
  273.          
  274.       double[] limits = { -5,5,-5,5 }; // x- and y-limits to use
  275.  
  276.       if (pos > 0) {
  277.                // Get limits from example2 text.
  278.          String nums = example2.substring(pos+1);
  279.          example2 = example2.substring(0,pos);
  280.          StringTokenizer toks = new StringTokenizer(nums, " ,");
  281.          if (toks.countTokens() >= 4) {
  282.             for (int i = 0; i < 4; i++) {
  283.                try {
  284.                    Double d = new Double(toks.nextToken());
  285.                    limits[i] = d.doubleValue();
  286.                }
  287.                catch (NumberFormatException e) {
  288.                }
  289.             }
  290.          }
  291.          if (toks.hasMoreTokens()) {
  292.             try {
  293.                double d = (new Double(toks.nextToken())).doubleValue();
  294.                if (tMin == null) {
  295.                   graph.setTMin(new Constant(d));
  296.                   if (tracer != null)
  297.                      tracer.setMin(d);
  298.                }
  299.                else
  300.                   tMin.setVal(d);
  301.             }
  302.             catch (NumberFormatException e) {
  303.             }
  304.          }
  305.          if (toks.hasMoreTokens()) {
  306.             try {
  307.                double d = (new Double(toks.nextToken())).doubleValue();
  308.                if (tMax == null) {
  309.                   graph.setTMax(new Constant(d));
  310.                   if (tracer != null)
  311.                      tracer.setMax(d);
  312.                }
  313.                else
  314.                   tMax.setVal(d);
  315.             }
  316.             catch (NumberFormatException e) {
  317.             }
  318.          }
  319.          if (toks.hasMoreTokens()) {
  320.             try {
  321.                int d = (int)Math.round((new Double(toks.nextToken())).doubleValue());
  322.                if (tIntervals == null) {
  323.                   if (tracer != null && tracer.getIntervals() == graph.getIntervals())
  324.                      tracer.setIntervals(d);
  325.                   graph.setIntervals(new Constant(d));
  326.                }
  327.                else
  328.                   tIntervals.setVal(d);
  329.             }
  330.             catch (NumberFormatException e) {
  331.             }
  332.          }
  333.       }
  334.      
  335.       // Set up the example data and recompute everything.
  336.  
  337.       if (functionInput != null) {
  338.             // If there is a function input box, put the example text in it.
  339.          functionInput.setText(example);
  340.          functionInput2.setText(example2);
  341.       }
  342.       else {
  343.            // If there is no user input, set the function in the graph directly.
  344.          try {
  345.             Function f = new SimpleFunction( parser.parse(example), xVar );
  346.             ((WrapperFunction)xFunc).setFunction(f);
  347.             Function g = new SimpleFunction( parser.parse(example2), xVar );
  348.             ((WrapperFunction)yFunc).setFunction(g);
  349.          }
  350.          catch (ParseError e) {  
  351.              // There should't be parse error's in the Web-page
  352.              // author's examples!  If there are, the function
  353.              // just won't change.
  354.          }
  355.       }
  356.       CoordinateRect coords = canvas.getCoordinateRect(0);
  357.       coords.setLimits(limits);
  358.       coords.setRestoreBuffer();
  359.       mainController.compute();
  360.      
  361.    } // end doLoadExample()
  362.    
  363.  
  364.    public void stop() {  // stop animator when applet is stopped
  365.       if (tracer != null)
  366.          tracer.stop();
  367.       super.stop();
  368.    }
  369.  
  370.  
  371.  
  372.  
  373. } // end class Parametric
  374.