[ prev | main ]

Series Grapher Applet

This applet displays the graph of a sum of the form "sum for
k = N to M of f(x,k)", where the lower and upper limits N and M
are specifed by sliders at the bottom of the applet. One of the
terms in the series is displayed in a separate graph. The term
that is displayed is specified by a third slider. The sample
function is the series for sin(x).

The following source code shows how the applet is built from
WCM components. This is a complicated applet, and not one whose
programming would be undertaken lightly.

// This one is rather tricky. To get the graph of the series, I had
// to write a custom Function, defined by a nested class at the end
// of this class. When this function is evaluated, the value of
// the index variable (kVar in the program) is changed as the summation
// is computed. Unfortunately, this means that kVar better not be the
// variable associated with the VariableSlider, term, since then setting
// its value would also change the value displayed on the slider. So,
// to make it work, kVar is a separater variable from the slider, and
// there is a custom Computable that copies the value of the slider to
// the value of kVar before the graph of one term in the series is drawn.
// This example also uses two Controllers, and here the two Controllers
// are pretty necessary since I don't want to recompute the summation
// graph unless there is no choice. The summation requires enough computation
// to require a noticable amount of time, even on a fast computer.

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Insets;

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

import net.sourceforge.webcompmath.awt.Computable;
import net.sourceforge.webcompmath.awt.ComputeButton;
import net.sourceforge.webcompmath.awt.Controller;
import net.sourceforge.webcompmath.awt.DisplayLabel;
import net.sourceforge.webcompmath.awt.ExpressionInput;
import net.sourceforge.webcompmath.awt.VariableSlider;
import net.sourceforge.webcompmath.data.Cases;
import net.sourceforge.webcompmath.data.Constant;
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.Variable;
import net.sourceforge.webcompmath.draw.CoordinateRect;
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.WcmAxes;

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

private static final long serialVersionUID = 8644112419670416093L;

private DisplayCanvas termCanvas, seriesCanvas;

* Overrides init() in JApplet
* @see java.applet.Applet#init()
public void init() {

* Separate canvasses for the term graph and the summation graph. In
* order to create these canvasses with non-standard x- and y-limits, I
* provide a CoordinateRect for the canvas as an argument to the
* constructor.
termCanvas = new DisplayCanvas(new CoordinateRect(-15, 15, -3, 3));
seriesCanvas = new DisplayCanvas(new CoordinateRect(-15, 15, -3, 3));

* Setting the second parameter in the constructor for LimitControlPanel
* to true causes the components in the panel to be laid out in two
* columns instead of one.
LimitControlPanel limits = new LimitControlPanel(
LimitControlPanel.SET_LIMITS, true);

* The LimitControlPanel controls both canvasses, so the coords on the
* canvasses are synchronized.

VariableSlider lower = new VariableSlider(new Constant(0),
new Constant(10));
// These sliders will only have integer values.

VariableSlider upper = new VariableSlider(lower, new Constant(20));
* This variable is declared final so that it can be used in the custom
* Computable class that is declared below.
final VariableSlider term = new VariableSlider(lower, upper);

* You can specify a number of different options for a parser. Here,
* I've turned on factorials, which are not allowed by default.
Parser parser = new Parser(Parser.DEFAULT_OPTIONS | Parser.FACTORIAL);

* This variable is declared final so that it can be used in the custom
* Computable class that is declared below.
final Variable kVar = new Variable("k");

Variable xVar = new Variable("x");

ExpressionInput input = new ExpressionInput(
"(-1)^k * x^(2*k+1) / (2*k+1)!", parser);

ComputeButton button = new ComputeButton("New");

DrawString seriesString = new DrawString("Sum from k = # to #",
DrawString.TOP_LEFT, new Value[] { lower, upper });
seriesString.setColor(new Color(0, 120, 0));
seriesString.setFont(new Font("SansSerif", Font.BOLD, 10));

DrawString termString = new DrawString("Term with k = #",
DrawString.TOP_LEFT, new Value[] { term });
termString.setColor(new Color(0, 120, 0));
termString.setFont(new Font("SansSerif", Font.BOLD, 10));

seriesCanvas.add(new WcmAxes());
seriesCanvas.add(new Graph1D(new SeriesFunc(input.getExpression(),
xVar, kVar, lower, upper)));
seriesCanvas.add(new DrawBorder());

termCanvas.add(new WcmAxes());
termCanvas.add(new Graph1D(input.getFunction(xVar)));
termCanvas.add(new DrawBorder());

* A controller that will redraw the term graph when necessary.
Controller termController = new Controller();

* This custom Computable object copies the value specified on the term
* slider to the variable, kVar, which is used in the user's expression.
termController.add(new Computable() {
public void compute() {

* Add a DisplayCanvas to a controller is the same as adding everything
* that the canvas contains.
* termController repsonds to changes in the value of the term slider.
* This controller responds to any other change.
Controller graphController = new Controller();

* termController is a "sub-controller" of graphController


setLayout(new BorderLayout(3, 3));
JPanel top = new JPanel();
top.setLayout(new GridLayout(2, 1, 3, 3));
add(top, BorderLayout.CENTER);
JPanel bot = new JPanel();
bot.setLayout(new GridLayout(1, 2, 3, 3));
JPanel botLeft = new JPanel();
botLeft.setLayout(new GridLayout(5, 1, 3, 3));
add(bot, BorderLayout.SOUTH);

botLeft.add(new JLabel("Enter the formula for a term, f(x,k): "));
JPanel inputPanel = new JPanel();
inputPanel.setLayout(new BorderLayout(3, 3));
inputPanel.add(input, BorderLayout.CENTER);
inputPanel.add(button, BorderLayout.EAST);
botLeft.add(slidePanel("Lower limit k = #", lower, graphController));
botLeft.add(slidePanel("Upper limit k = #", upper, graphController));
botLeft.add(slidePanel("Show term for k = #", term, termController));


} // end doInit()

JPanel slidePanel(String s, VariableSlider v, Controller c) {
JPanel p = new JPanel();
p.setLayout(new GridLayout(1, 2, 3, 3));
DisplayLabel dl = new DisplayLabel(s, new Value[] { v });
return p;

* @see java.awt.Container#getInsets()
public Insets getInsets() {
// Leave a border of three pixels around the edges of the applet.
return new Insets(3, 3, 3, 3);

private static class SeriesFunc implements Function {

* This class defines a Function that represents the summation of a
* function of two variables, x and n, for n between a specified lower
* limit and upper limit.

private static final long serialVersionUID = 321811273393720768L;

Expression func; // The function of two variables.

Variable variable, index; // The two variables.

Value start, stop; // The upper and lower limit.

SeriesFunc(Expression exp, Variable x, Variable n, Value low, Value high) {
func = exp;
variable = x;
index = n;
start = low;
stop = high;

* @see net.sourceforge.webcompmath.data.Function#getArity()
public int getArity() {
// The summation is a function of one variable.
return 1;

* @see net.sourceforge.webcompmath.data.Function#getValueWithCases(double[],
* net.sourceforge.webcompmath.data.Cases)
public double getValueWithCases(double[] x, Cases cases) {
* Evaluate the summati0n at x[0]. (Information used in continutity
* checks is stored in cases.)
double sum = 0;
int a = (int) Math.round(start.getVal());
int b = (int) Math.round(stop.getVal());
double save = index.getVal();
for (int i = a; i <= b; i++) {
sum += func.getValueWithCases(cases);
return sum;

* @see net.sourceforge.webcompmath.data.Function#getVal(double[])
public double getVal(double[] arguments) {
return getValueWithCases(arguments, null);

* The remaining methods are reuired by the Function interface, but they
* are not used in this applet, so I have not bothered to define them
* corretly (as I would have in a class meant for general use.

* @see net.sourceforge.webcompmath.data.Function#derivative(int)
public Function derivative(int wrt) {
return null;

* @see net.sourceforge.webcompmath.data.Function#derivative(net.sourceforge.webcompmath.data.Variable)
public Function derivative(Variable x) {
return null;

* @see net.sourceforge.webcompmath.data.Function#dependsOn(net.sourceforge.webcompmath.data.Variable)
public boolean dependsOn(Variable x) {
return false;

* @return no name
public String getName() {
return null;

* @param s
public void setName(String s) {


} // end class SeriesGrapherApplet

[ prev | main ]