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