[ prev | main | next ]

TangentsApplet



This applet draws the graph of a function and its derivative.
A tangent line is drawn on the function, and a crosshair marks the
corresponding point on the graph of the tangent. You can move the
tangent line by clicking-and-dragging on either graph. Aside from
this, the canvas does not respond to mouse actions. A formula for
the derivative is shown at the bottom of the applet:

The following source code shows how the applet is built from
JCM components. This one gets pretty complicated! Features that
appeared in previous examples are not commented:




import java.awt.*;
import java.applet.Applet;
import edu.hws.jcm.awt.*;
import edu.hws.jcm.draw.*;
import edu.hws.jcm.data.*;

public class TangentsApplet extends Applet {

   DisplayCanvas canvas;
   
   public void stop() {
      canvas.releaseResources();
   }
   
   public void init() {
   
      canvas = new DisplayCanvas();            // By default, a canvas contains just one
      canvas.addNewCoordinateRect(0,0.5,0,1);  //   CoordinateRect.  However, it can contain
      canvas.addNewCoordinateRect(0.5,1,0,1);  //   more than one.  Here, I add two 
                                               //   CoordinateRects to the canvas.  The
                                               //   parameters specify the portion of the
                                               //   canvas occupied by the coords, where
                                               //   0,1,0,1 would mean the entire canvas,
                                               //   both horizontally and vertically.  The
                                               //   first coords occupies the left half
                                               //   of the canvas, and the second occupies 
                                               //   the right.
      
      canvas.add( new Axes(), 0);   // When an object is added to the canvas, you can
      canvas.add( new Axes(), 1);   //   specify which coordinate rect it is being
                                    //   added to.  CoordinateRects in the canvas
                                    //   are numbered starting from zero.
      
      LimitControlPanel limits = new LimitControlPanel(); 
      limits.addCoords(canvas.getCoordinateRect(0));  // Previously, I added canvasses
      limits.addCoords(canvas.getCoordinateRect(1));  //   to LimitControlPanels, but
                                                      //   really its the CoordinateRects
                                                      //   that are added.  Here, I explicitely
                                                      //   add both CoordinateRects to the
                                                      //   LimitControlPanel.  This has the
                                                      //   effect of synchronizing the
                                                      //   limits on the two coords with each
                                                      //   other as well as with the limits panel.
      limits.setErrorReporter(canvas);
      
      Parser p = new Parser();
      Variable x = new Variable("x");
      p.add(x);
      ExpressionInput input = new ExpressionInput("sin(x)*cos(2*x) + x/3",p);
      Function func = input.getFunction(x);
      
      Function deriv = func.derivative(1);  // Every function object has one or
                                            //   more derivatives.  func.derivative(1)
                                            //   is the Function object that represents
                                            //   the derivative of func with respect to
                                            //   its first (and only) parameter.
                                            //   This derivative is computed symbolically
                                            //   from the expression that defines
                                            //   func, that is, the expression entered
                                            //   by the user.  The derivative function
                                            //   changes along with the function when
                                            //   the user's input changes.
      
      Graph1D graph1 = new Graph1D(func);   // Graph of the function.
      Graph1D graph2 = new Graph1D(deriv);  // Graph of the derivative function.
      canvas.add(graph1, 0);   // Add each graph to a separate CoordinateRect.
      canvas.add(graph2, 1);
      
      MouseTracker m1 = new MouseTracker();  // A MouseTracker can be added to a
      MouseTracker m2 = new MouseTracker();  //   CoordinateRect (or DisplayCanvas) to
                                             //   respond to user clicks-and-drags of
                                             //   the mouse.  (To make them work, I had
                                             //   to leave the zoom feature of the
                                             //   DisplayCanvas turned off in this
                                             //   applet.)
      
      Variable xMouse1 = m1.getXVar();   // There are two variables associated with
      xMouse1.setVal(0);                 //    a MouseTracker.  They represent the
      Variable xMouse2 = m2.getXVar();   //    x- and y-coordinates of the mouse
      xMouse2.setVal(0);                 //    in the coordinate system defined by
                                         //    the CoordinateRect.
      
      TangentLine tangent = new TangentLine(xMouse1,func);  // Tangent line for first graph.
      Crosshair cross = new Crosshair(xMouse2,deriv); // Crosshair for second graph.
                                                      // Positions are determined from MouseTrackers.

      canvas.add(m1,0);  // Add the mouse trackers to the two CoordinateRects.
      canvas.add(m2,1);
      canvas.add(tangent, 0);  // Add tangent line to the first rect.
      canvas.add(cross, 1);    // Add the Crosshair to the second rect.
      
      DrawString values =  new DrawString("x = #\nf(x) = #\nf'(x) = #", DrawString.BOTTOM_LEFT, 
                    new Value[] { xMouse1, new ValueMath(func,xMouse1), new ValueMath(deriv,xMouse1) } );
      values.setNumSize(6);  // Limit the size of numbers in the DrawString.
      values.setColor(new Color(0,100,0));
      canvas.add( values, 1 );                      // Add some DrawStrings to the canvas.
      canvas.add( new DrawString("y = f(x)"), 0);
      canvas.add( new DrawString("y = f'(x)"), 1);
      
      canvas.add( new DrawBorder(), 0);  // Add a border around each coordinate rect.
      canvas.add( new DrawBorder(), 1);

      ComputeButton button = new ComputeButton("Draw graph");
      
      JCMPanel funcPanel = new JCMPanel(3);
      funcPanel.add(new Label(" f(x) = "), BorderLayout.WEST);
      funcPanel.add(input, BorderLayout.CENTER);
      funcPanel.add(button, BorderLayout.EAST);
      
      JCMPanel bottom = new JCMPanel(2,1);
      bottom.add(funcPanel);
      bottom.add( new FuncLabel(input.getExpression().derivative(x)) );
           // A FuncLabel is a custom "Computable", defined below, for
           //   displaying the formula for an expression.  In this case,
           //   the expression that is displayed is the derivative of
           //   the user's input with respect to x.
      
      JCMPanel main = new JCMPanel(3);
      main.setInsetGap(3);
      main.add(bottom,BorderLayout.SOUTH);
      main.add(canvas,BorderLayout.CENTER);
      main.add(limits,BorderLayout.EAST);
      
      setBackground(Color.lightGray);
      setLayout(new BorderLayout(3,3));
      add(main,BorderLayout.CENTER);

      Controller c = main.getController();
      c.setErrorReporter(canvas);
      
      c.add(new Tie( (Tieable)xMouse1, (Tieable)xMouse2 ));
           // An interesting point in this applet is that the x-coordinates
           // of the tangent line and crosshair are synchronized.  This is done
           // by "Tieing" the x-variables associated with the two MouseTrackers.
           // When the user changes one of the values, the other is changed
           // to have the same value.  Note that xMouse1 and xMouse2 are 
           // declared to be of type Variable, but they also implement the
           // Tieable interface so that they can be synchronized with other
           // MouseTracker variables or with VariableSliders or VariableInputs.

      button.setOnUserAction(c);  // Make controller respond to button.

      main.gatherInputs(); // Must respond to input objects!
                           // This applet would actually be more efficient if
                           // Were built of regular Panels and set up the
                           // the controllers by hand, as in GraphApplet3
      
   }  // end init();
   
   
   static class FuncLabel extends Label implements Computable {
         // Objects that are meant to be recomputed by Controllers implement
         // the Computable interface.  There is no standard JCM component
         // for displaying an expression, but it's easy enough to make
         // one.  A FuncLabel, when properly added to a Controller, will
         // display the String representation of an expression.
      Expression exp;
      FuncLabel(Expression e) {
         super(" f'(x) = " + e);
         exp = e;
      }
      public void compute() {
           // Recompute the display.  This is meant to be called whenever
           // the expression might have changed.
         setText(" f'(x) = " + exp);
      }
   } // end nested class FuncLabel

} // end class TangentsApplet


[ prev | main | next ]