Graph Applet 2
This applet that draws the graph of a function and marks
a point on the graph with a crosshair. The position of the
point is controlled by a slider at the bottom of the applet.
Enter the function in box at the bottom of the applet and press
return. You can click on the graph to zoom in on a point.
Shift-click to zoom out from a point. Or drag the mouse to draw
a rectangle that is then zoomed to fill the whole graph area.
Or right-click-and-drag to slide the graph around.This applet is an improvement over the previous GraphApplet1.
You should probably look at that applet first.
The source code for the applet shows how it was assembled
from WCM components:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.Label;
import javax.swing.BorderFactory;
import javax.swing.JApplet;
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.VariableInput;
import net.sourceforge.webcompmath.awt.VariableJSlider;
import net.sourceforge.webcompmath.awt.WcmPanel;
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.WcmAxes;
import net.sourceforge.webcompmath.draw.CoordinateRect;
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.Panner;
/**
* This applet is an example of how to build a math applet using WCM.
*/
public class GraphApplet2 extends JApplet {
private static final long serialVersionUID = -3013460300602876330L;
private DisplayCanvas canvas;
/**
* This method overrides init() in JApplet.
*
* @see java.applet.Applet#init()
*/
public void init() {
Parser parser = new Parser();
// Create the parser and the variable, x.
Variable x = new Variable("x");
parser.add(x);
canvas = new DisplayCanvas();
// Create the canvas, and set it to do zooming.
canvas.setHandleMouseZooms(true);
canvas.add(new Panner());
/*
* Adding a panner lets the user right-click-and-drag on the canvas to
* slide the graph around.
*/
CoordinateRect coords = canvas.getCoordinateRect();
/*
* Behind the scene, a canvas has a CoordinateRect, which actually keeps
* track of the x- and y-limits (and does some of the other work of the
* canvas). For some purposes, you need the CoordinateRect. You can
* obtain it by calling the DisplayCanvas's getCoordinateRect() method.
*/
LimitControlPanel limits = new LimitControlPanel(
LimitControlPanel.SET_LIMITS | LimitControlPanel.RESTORE, false);
/*
* In this case, I am using a LimitControlPanel that contains two
* buttons. the usual "Set Limits" button and a "Restore Limits" button.
* The second button button will restore the original limits on the
* canvas. The second parameter to this constructor can be set to true
* if you would like the components in the LimitControlPanel to be
* arranged into two columns instead of one. Buttons can also be added
* after the constructor is called by calling the addButtons() method
* from class LimitControlPanel.
*/
limits.addCoords(canvas);
ExpressionInput input = new ExpressionInput("sin(x)+2*cos(3*x)", parser);
// For user input
Function func = input.getFunction(x);
// The function that will be graphed.
Graph1D graph = new Graph1D(func); // The graph itself.
VariableInput xInput = new VariableInput();
/*
* An input box where the user can specify the x-coordinate of the point
* on the graph that is marked by the crosshair.
*/
VariableJSlider xSlider = new VariableJSlider(coords
.getValueObject(CoordinateRect.XMIN), coords
.getValueObject(CoordinateRect.XMAX));
/*
* A VariableJSlider is a slider that the user can adjust as a means of
* inputting a value. The parameters to the constructor specify the
* minimum and maximum of this value. In this case, the minimum value is
* coords.getValueObject(CoordinateRect.XMIN). This says that the
* minimum value on the slider is given by the minimum x-value on the
* canvas's CoordinateRect. This minimum is adjusted automatically when
* the limits on the CoordinateRect change. The maximum value is
* similar. This VariableJSlider is actually used as a second way of
* inputting the x-coordinate of the point where the crosshair is shown.
* Later, the value of the slider will be "tied" to the value in the
* VariableInput. You should check that when you change one, the other
* is also changed. (To change the value associated with the
* VariableInput box, you have to press return in that box.)
*/
DrawString info = new DrawString("x = #\nf(x) = #",
DrawString.TOP_LEFT, new Value[] { xSlider,
new ValueMath(func, xSlider) });
/*
* A DrawString draws a string on a DisplayCanvas. The string can have
* line breaks, indicated by '\n', and can contain embedded Values,
* indicated by '#'. The position of the string is DrawString.TOP_LEFT.
* That is, it is in the top-left corner of the canvas. The third
* parameter is an array of Value objects whose values are substituted
* for the #'s in the string.
*/
info.setFont(new Font("SansSerif", Font.BOLD, 12));
// Set properties of the DrawString.
info.setColor(new Color(0, 100, 0));
info.setOffset(10);
ComputeButton graphIt = new ComputeButton("Graph It!");
/*
* A ComputeButton is just a button that can be registered with a
* Controller, so that clicking on the Button causes the Controller to
* compute. In this case, this is a redundant input, since pressing
* return in the ExpressionInput box will accomplish the same thing.
* However, the button gives the user a more obvious way to change the
* function that is graphed.
*/
WcmPanel main = new WcmPanel();
// The interface is constructed of WcmPanels.
WcmPanel top = new WcmPanel();
WcmPanel bottom = new WcmPanel();
main.add(canvas, BorderLayout.CENTER);
main.add(limits, BorderLayout.EAST);
main.add(bottom, BorderLayout.SOUTH);
main.add(top, BorderLayout.NORTH);
main.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
top.add(input, BorderLayout.CENTER);
top.add(new Label(" f(x) = "), BorderLayout.WEST);
top.add(graphIt, BorderLayout.EAST);
bottom.add(xSlider, BorderLayout.CENTER);
bottom.add(xInput, BorderLayout.EAST);
bottom.add(new Label(" x = "), BorderLayout.WEST);
setLayout(new BorderLayout()); // Set up for the Applet itself.
add(main, BorderLayout.CENTER);
setBackground(Color.lightGray);
canvas.add(new WcmAxes()); // Add a set of axes to the DisplayCanvas.
canvas.add(graph);
// Add the graph of the function to the DisplayCanvas.
canvas.add(new Crosshair(xSlider, func));
/*
* Add a CrossHair to the canvas. The crosshair is on the graph of the
* function, func, at the point whose xCoordinate is given by the value
* on the slider, xSlider.
*/
canvas.add(info); // Add the DrawString to the canvas.
canvas.add(new DrawBorder(Color.darkGray, 2));
// Add a 2-pixel dark gray border around edges of the canvas.
main.gatherInputs();
/*
* The Controller for the main panel. must be set to respond to user
* actions on the input objects. The gatherInputs() method is an easy
* way to do this. This calls the setOnUserAction() method of the four
* input objects: input, graphIt, xInput and xSlider.
*/
Controller controller = main.getController();
/*
* Get the controller from the main panel. We still need it for a few
* things...
*/
graphIt.setOnUserAction(controller);
/*
* ComputeButtons aren't handled automatically by main.gatherInput(). It
* must be set to notify the controller when it is clicked, in order for
* the applet to be recomputed when the button is clicked.
*/
coords.setOnChange(controller);
/*
* Because the VariableSlider depends on the limits on the
* CoordinateRect, the controller must also listen for changes in these
* limits. The gatherInputs() doesn't "gather" in the coordinate rect.
*/
controller.add(new Tie(xSlider, xInput));
/*
* This is the thing that synchronizes the values on the VariableSlider
* and the VariableSlider. After checking all the inputs in the applet,
* the Controller "synchronizes" the two objects named in the "Tie".
*/
/*
* I forgot to set an errorReporter for this applet! You can check what
* happens when the input in the applet contains an error. (The error
* message is printed to standard output. In Netscape, for example, this
* means that it goes to the Java Console, where it will do the user no
* good at all.)
*/
} // end init()
} // end class SimpleGraph