[ prev | main | next ]


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
WCM components. This one gets pretty complicated! Features that
appeared in previous examples are not commented:

import java.awt.BorderLayout;
import java.awt.Color;

import javax.swing.JLabel;
import javax.swing.JApplet;

import net.sourceforge.webcompmath.awt.Computable;
import net.sourceforge.webcompmath.awt.ComputeButton;
import net.sourceforge.webcompmath.awt.Controller;
import net.sourceforge.webcompmath.awt.ExpressionInput;
import net.sourceforge.webcompmath.awt.Tie;
import net.sourceforge.webcompmath.awt.Tieable;
import net.sourceforge.webcompmath.awt.WcmPanel;
import net.sourceforge.webcompmath.data.Expression;
import net.sourceforge.webcompmath.data.Function;
import net.sourceforge.webcompmath.data.Parser;
import net.sourceforge.webcompmath.data.Value;
import net.sourceforge.webcompmath.data.ValueMath;
import net.sourceforge.webcompmath.data.Variable;
import net.sourceforge.webcompmath.draw.Crosshair;
import net.sourceforge.webcompmath.draw.DisplayCanvas;
import net.sourceforge.webcompmath.draw.DrawBorder;
import net.sourceforge.webcompmath.draw.DrawString;
import net.sourceforge.webcompmath.draw.Graph1D;
import net.sourceforge.webcompmath.draw.LimitControlPanel;
import net.sourceforge.webcompmath.draw.MouseTracker;
import net.sourceforge.webcompmath.draw.TangentLine;
import net.sourceforge.webcompmath.draw.WcmAxes;

* This is an example WCM applet
public class TangentsApplet extends JApplet {

private static final long serialVersionUID = -4457550735230739341L;

DisplayCanvas canvas;

* Overrides init() in JApplet
* @see java.applet.Applet#init()
public void init() {
* By default, a canvas contains just one CoordinateRect. However, it
* can contain 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 = new DisplayCanvas();
canvas.addNewCoordinateRect(0, 0.5, 0, 1);
canvas.addNewCoordinateRect(0.5, 1, 0, 1);

* When an object is added to the canvas, you can specify which
* coordinate rect it is being added to. CoordinateRects in the canvas
* are numbered starting from zero.
canvas.add(new WcmAxes(), 0);
canvas.add(new WcmAxes(), 1);

* Previously, I added canvasses 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.
LimitControlPanel limits = new LimitControlPanel();
limits.addCoords(canvas.getCoordinateRect(1)); // to

Parser p = new Parser();
Variable x = new Variable("x");
ExpressionInput input = new ExpressionInput("sin(x)*cos(2*x) + x/3", p);
Function func = input.getFunction(x);

* 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.
Function deriv = func.derivative(1);
// Graph of the function.
Graph1D graph1 = new Graph1D(func);
// Graph of the derivative function.
Graph1D graph2 = new Graph1D(deriv);
canvas.add(graph1, 0); // Add each graph to a separate CoordinateRect.
canvas.add(graph2, 1);

* A MouseTracker can be added to a 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.)
MouseTracker m1 = new MouseTracker();
MouseTracker m2 = new MouseTracker();

* There are two variables associated with a MouseTracker. They
* represent the x- and y-coordinates of the mouse in the coordinate
* system defined by the CoordinateRect.
Variable xMouse1 = m1.getXVar();
Variable xMouse2 = m2.getXVar();

// Tangent line for first graph.
TangentLine tangent = new TangentLine(xMouse1, func);
// Crosshair for second graph. Positions are determined from
// MouseTrackers.
Crosshair cross = new Crosshair(xMouse2, deriv);

// Add the mouse trackers to the two CoordinateRects.
canvas.add(m1, 0);
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);

// Add a border around each coordinate rect.
canvas.add(new DrawBorder(), 0);
canvas.add(new DrawBorder(), 1);

ComputeButton button = new ComputeButton("Draw graph");

WcmPanel funcPanel = new WcmPanel(3);
funcPanel.add(new JLabel(" f(x) = "), BorderLayout.WEST);
funcPanel.add(input, BorderLayout.CENTER);
funcPanel.add(button, BorderLayout.EAST);

WcmPanel bottom = new WcmPanel(2, 1);
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.

WcmPanel main = new WcmPanel(3);
main.add(bottom, BorderLayout.SOUTH);
main.add(canvas, BorderLayout.CENTER);
main.add(limits, BorderLayout.EAST);

setLayout(new BorderLayout(3, 3));
add(main, BorderLayout.CENTER);

Controller c = main.getController();

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.

* 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 JLabel implements Computable {

private static final long serialVersionUID = 2638507674457461374L;

* 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;

* @see net.sourceforge.webcompmath.awt.Computable#compute()
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 ]