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 SecantTangent applet shows the graph of a function with two points
  17. // marked on the function.  The tangent to the graph is drawn at the first
  18. // of these points.  A second line is drawn between the two points.
  19. // Both of the points can be dragged by the user.  Alternatively, the
  20. // x-coords of the points can be typed into input boxes.
  21.  
  22. import java.awt.*;
  23. import java.applet.Applet;
  24. import java.util.StringTokenizer;
  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. public class SecantTangent extends GenericGraphApplet {
  32.  
  33.  
  34.    private Function func;   // The function that is graphed.
  35.  
  36.    VariableInput x1Input = new VariableInput(); // x-coord where tangent is drawn.
  37.    VariableInput x2Input = new VariableInput(); // x-coord for other point of secant.
  38.  
  39.    protected void setUpParameterDefaults() {
  40.       parameterDefaults = new java.util.Hashtable();
  41.       String varName = getParameter("Variable","x");
  42.       parameterDefaults.put("Function", " e ^ " + varName);
  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");
  59.          Function f = new SimpleFunction( parser.parse(def), xVar );
  60.          func = new WrapperFunction(f);
  61.       }
  62.       Graph1D graph = new Graph1D(func);
  63.       Color color = getColorParam("GraphColor",Color.black);
  64.       graph.setColor(color);
  65.          
  66.       // Create two DraggablePoint objects, which will be the points on the canvas
  67.       // that the user can drag.  The x-coordinate of drag1 will be tied later to
  68.       // x1Input, so that either drag1 or x1Input can be used for setting the
  69.       // values of the point.  Same for drag2 and x2Input.
  70.      
  71.       Color tangentColor = getColorParam("TangentColor", Color.red);
  72.       Color secantColor = getColorParam("SecantColor",new Color(0,200,0));
  73.      
  74.       DraggablePoint drag1 = new DraggablePoint();  // point where tangent is drawn
  75.       DraggablePoint drag2 = new DraggablePoint();  // other point on secant line
  76.      
  77.       drag1.clampY(func);   // Both points are clamped to move along the function.
  78.       drag2.clampY(func);
  79.      
  80.       drag1.setColor(tangentColor);  
  81.       drag1.setGhostColor(lighten(tangentColor));
  82.      
  83.       drag2.setColor(secantColor);    
  84.       drag2.setGhostColor(lighten(secantColor));
  85.  
  86.  
  87.       // Create the tangent line and the secant line.  
  88.  
  89.       DrawGeometric secant = new DrawGeometric(DrawGeometric.INFINITE_LINE_ABSOLUTE,
  90.                                       drag1.getXVar(), drag1.getYVar(),
  91.                                       drag2.getXVar(), drag2.getYVar());      
  92.       secant.setColor(secantColor);
  93.                                      
  94.       TangentLine tangent = new TangentLine(drag1.getXVar(), func);
  95.       tangent.setColor(tangentColor);      
  96.      
  97.       canvas.add(drag1);
  98.       canvas.add(drag2);
  99.       canvas.add(tangent);
  100.       canvas.add(secant);
  101.       canvas.add(graph);
  102.      
  103.       // Create a DrawString to display the slopes of the tangent and secant.
  104.      
  105.       Value tangentSlope = new ValueMath(func.derivative(1), drag1.getXVar());
  106.       Value secantSlope = new ValueMath( new ValueMath(drag2.getYVar(), drag1.getYVar(), '-'),
  107.                                          new ValueMath(drag2.getXVar(), drag1.getXVar(), '-'),
  108.                                          '/');
  109.      
  110.       DrawString info;
  111.       if ( "no".equalsIgnoreCase(getParameter("ShowTangentSlope","yes")) ) {        
  112.          info = new DrawString( "Secant Slope = #",
  113.                               DrawString.TOP_LEFT,
  114.                               new Value[] { secantSlope } );
  115.       }
  116.       else {
  117.          info = new DrawString( "Secant Slope = #\nTangent Slope = #",
  118.                               DrawString.TOP_LEFT,
  119.                               new Value[] { secantSlope, tangentSlope } );
  120.       }
  121.       info.setFont(new Font("Monospaced",Font.PLAIN,10));
  122.       info.setNumSize(7);
  123.       info.setColor(getColorParam("SlopeTextColor", Color.black));
  124.       info.setBackgroundColor(getColorParam("SlopeTextBackground",Color.white));
  125.       info.setFrameWidth(1);
  126.       canvas.add(info);
  127.      
  128.       // Create input boxes and add them to the bottom of the applet
  129.      
  130.       Panel xIn = new Panel();  // not a JCMPanel, since I don't want the input boxes to be controlled
  131.                                 // by the main controller anyway.
  132.       xIn.setBackground(getColorParam("PanelColor",Color.lightGray));
  133.       xIn.setLayout(new GridLayout(1,4,3,3));
  134.       xIn.add(new Label("Tangent at " + xVar.getName() + " = ", Label.RIGHT));
  135.       xIn.add(x1Input);
  136.       xIn.add(new Label("Secant to  " + xVar.getName() + " = ", Label.RIGHT));
  137.       xIn.add(x2Input);
  138.      
  139.       // Put the inputs at the bottom of the inputPanel, if there is one, otherwise
  140.       // at the bottom of the mainPanel
  141.       if (inputPanel == null)
  142.          mainPanel.add(xIn, BorderLayout.SOUTH);
  143.       else
  144.          inputPanel.add(xIn, BorderLayout.SOUTH);
  145.      
  146.  
  147.       // Set up controllers.  I want to arrange things so that the controls that position the
  148.       // two points on the graph do not cause the graph to be recomputted when they are changed.
  149.  
  150.       Controller dragControl = new Controller();  // A controller to respond to dragging
  151.       mainController.remove(canvas);
  152.       mainController.add(graph);
  153.       mainController.add(dragControl);  // Things in dragController should be recomputed when graph is changed
  154.  
  155.       dragControl.add(x1Input);  // dragControl checks the contents of the x-inputs
  156.       dragControl.add(x2Input);  //    and recomputes everything except the graph.
  157.       dragControl.add(drag1);
  158.       dragControl.add(drag2);
  159.       dragControl.add(tangent);
  160.       dragControl.add(secant);
  161.       dragControl.add(info);
  162.  
  163.       drag1.setOnUserAction(dragControl);     // dragControl.compute() is called when the
  164.       drag2.setOnUserAction(dragControl);     //    user drags one of the points or types
  165.       x1Input.setOnTextChange(dragControl);   //    in one of the x-input boxes.
  166.       x2Input.setOnTextChange(dragControl);
  167.  
  168.      
  169.       // By adding Tie's to dragControll, we make sure that the positions of the
  170.       // draggable points are synchronized with the contents of the x-input boxes.
  171.  
  172.       dragControl.add(new Tie((Tieable)drag1.getXVar(), x1Input));
  173.       dragControl.add(new Tie((Tieable)drag2.getXVar(), x2Input));
  174.      
  175.       // Get initial values for draggable points
  176.      
  177.       double[] d1 = getNumericParam("X1");
  178.       double x1 = (d1 != null && d1.length == 1)? d1[0] : 0;
  179.       x1Input.setVal(x1);
  180.       drag1.setLocation(x1,0);  // y-value will be changed to make the point lie on the curve
  181.       double[] d2 = getNumericParam("X2");
  182.       double x2 = (d2 != null && d2.length == 1)? d2[0] : 1;
  183.       x2Input.setVal(x2);
  184.       drag2.setLocation(x2,0);
  185.  
  186.    } // end setUpCanvas()
  187.    
  188.  
  189.    private Color lighten(Color c) { // for making "Ghost" color of draggable point
  190.       int r = c.getRed();
  191.       int g = c.getGreen();
  192.       int b = c.getBlue();
  193.       int nr, ng, nb;
  194.       if (r <= 200 || g <= 200 || b <= 200) {
  195.          nb = 255 - (255 - b) / 3;
  196.          ng = 255 - (255 - g) / 3;
  197.          nr = 255 - (255 - r) / 3;
  198.       }
  199.       else {
  200.          nb = b / 2;
  201.          ng = g / 2;
  202.          nr = r / 2;
  203.       }
  204.       return new Color(nr,ng,nb);
  205.    }
  206.  
  207.  
  208.  
  209.    protected void doLoadExample(String example) {
  210.          // This method is called when the user loads an example from the
  211.          // example menu (if there is one).  It overrides an empty method
  212.          // in GenericGraphApplet.
  213.          //   For the SecantTangent applet, the example string should contain
  214.          // an expression that defines the function to be graphed.  This can optionally
  215.          // be followed by a semicoloon and a list of fourto six numbers.
  216.          // The first four numbers give the x- and y-limits to be used for the
  217.          // example.  If they are not present, then -5,5,-5,5 is used.  The
  218.          // fifth number, if present, gives the x-coord where the tangent line
  219.          // is drawn.  The sixth number gives the x-coord of the second point
  220.          // on the secant line.
  221.    
  222.       int pos = example.indexOf(";");
  223.  
  224.       double[] limits = { -5,5,-5,5 };  // x- and y-limits to use
  225.      
  226.       if (pos > 0) { // get limits from example text
  227.          String limitsText = example.substring(pos+1);
  228.          example = example.substring(0,pos);
  229.          StringTokenizer toks = new StringTokenizer(limitsText, " ,");
  230.          if (toks.countTokens() >= 4) {
  231.             for (int i = 0; i < 4; i++) {
  232.                try {
  233.                    Double d = new Double(toks.nextToken());
  234.                    limits[i] = d.doubleValue();
  235.                }
  236.                catch (NumberFormatException e) {
  237.                }
  238.             }
  239.             if (toks.countTokens() > 0) { // Get point for tangent line
  240.                try {
  241.                    Double d = new Double(toks.nextToken());
  242.                    x1Input.setVal( d.doubleValue() );
  243.                }
  244.                catch (NumberFormatException e) {
  245.                }
  246.             }
  247.             if (toks.countTokens() > 0) { // Get other point for secant line
  248.                try {
  249.                    Double d = new Double(toks.nextToken());
  250.                    x2Input.setVal( d.doubleValue() );
  251.                }
  252.                catch (NumberFormatException e) {
  253.                }
  254.             }
  255.          }
  256.       }
  257.      
  258.       // Set up the example data and recompute everything.
  259.  
  260.       if (functionInput != null) {
  261.             // If there is a function input box, put the example text in it.
  262.          functionInput.setText(example);
  263.       }
  264.       else {
  265.            // If there is no user input, set the function in the graph directly.
  266.            // Also, in this case, func is a "WrapperFunction".  Set the
  267.            // definition of that WrapperFunction to be the same as f
  268.          try {
  269.             Function f = new SimpleFunction( parser.parse(example), xVar );
  270.             ((WrapperFunction)func).setFunction(f);
  271.          }
  272.          catch (ParseError e) {  
  273.              // There should't be parse error's in the Web-page
  274.              // author's examples!  If there are, the function
  275.              // just won't change.
  276.          }
  277.       }
  278.       CoordinateRect coords = canvas.getCoordinateRect(0);
  279.       coords.setLimits(limits);
  280.       coords.setRestoreBuffer();
  281.       mainController.compute();
  282.      
  283.    } // end doLoadExample()
  284.    
  285.  
  286.  
  287. } // end class SimpleGraph
  288.