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
JCM 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.*; import edu.hws.jcm.data.*; import edu.hws.jcm.draw.*; import edu.hws.jcm.awt.*; public class SeriesGrapherApplet extends java.applet.Applet { private DisplayCanvas termCanvas, seriesCanvas; public void stop() { termCanvas.releaseResources(); seriesCanvas.releaseResources(); } public void init() { termCanvas = new DisplayCanvas(new CoordinateRect(-15,15,-3,3)); seriesCanvas = new DisplayCanvas(new CoordinateRect(-15,15,-3,3)); // 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. LimitControlPanel limits = new LimitControlPanel(LimitControlPanel.SET_LIMITS,true); // 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. limits.addCoords(termCanvas); // The LimitControlPanel controls both canvasses, limits.addCoords(seriesCanvas); // so the coords on the canvasses are synchronized. VariableSlider lower = new VariableSlider(new Constant(0), new Constant(10)); lower.setIntegerValued(true); // These sliders will only have integer values. lower.setVal(0); VariableSlider upper = new VariableSlider(lower, new Constant(20)); upper.setIntegerValued(true); final VariableSlider term = new VariableSlider(lower,upper); // This variable is declared final so that it can be used int // the custom Computable class that is declared below. term.setIntegerValued(true); term.setVal(0); Parser parser = new Parser(Parser.DEFAULT_OPTIONS | Parser.FACTORIAL); // You can specify a number of different options for a parser. // Here, I've turned on factorials, which are not allowed by default. final Variable kVar = new Variable("k"); // This variable is declared final so that it can be used int // the custom Computable class that is declared below. Variable xVar = new Variable("x"); parser.add(kVar); parser.add(xVar); 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 Axes() ); seriesCanvas.add( new Graph1D( new SeriesFunc(input.getExpression(),xVar,kVar,lower,upper) ) ); seriesCanvas.add( new DrawBorder() ); seriesCanvas.add( seriesString ); termCanvas.add( new Axes() ); termCanvas.add( new Graph1D( input.getFunction(xVar) ) ); termCanvas.add( new DrawBorder() ); termCanvas.add( termString ); Controller termController = new Controller(); // A controller that will redraw // the term graph when necessary. termController.add(new Computable() { public void compute() { // This custom Computable object copies kVar.setVal(term.getVal()); // the value specified on the term slider } // to the variable, kVar, which is used }); // in the user's expression. termController.add(term); termController.add(termCanvas); // Add a DisplayCanvas to a controller is the // same as adding everything that the canvas // contains. term.setOnUserAction(termController); // termController repsonds to changes // in the value of the term slider. Controller graphController = new Controller(); // This controller responds to any other change. graphController.add(lower); graphController.add(upper); graphController.add(seriesCanvas); graphController.add(input); graphController.add(termController); // termController is a "sub-controller" of graphController lower.setOnUserAction(graphController); upper.setOnUserAction(graphController); input.setOnUserAction(graphController); button.setOnUserAction(graphController); setBackground(Color.lightGray); setLayout(new BorderLayout(3,3)); Panel top = new Panel(); top.setLayout(new GridLayout(2,1,3,3)); top.add(seriesCanvas); top.add(termCanvas); add(top, BorderLayout.CENTER); Panel bot = new Panel(); bot.setLayout(new GridLayout(1,2,3,3)); Panel botLeft = new Panel(); botLeft.setLayout(new GridLayout(5,1,3,3)); bot.add(botLeft); bot.add(limits); add(bot, BorderLayout.SOUTH); botLeft.add(new Label("Enter the formula for a term, f(x,k): ")); Panel inputPanel = new Panel(); inputPanel.setLayout(new BorderLayout(3,3)); inputPanel.add(input,BorderLayout.CENTER); inputPanel.add(button,BorderLayout.EAST); botLeft.add(inputPanel); 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 )); graphController.setErrorReporter(termCanvas); limits.setErrorReporter(termCanvas); } // end doInit() Panel slidePanel(String s, VariableSlider v, Controller c) { Panel p = new Panel(); p.setLayout(new GridLayout(1,2,3,3)); DisplayLabel dl = new DisplayLabel(s, new Value[] { v }); p.add(v); p.add(dl); c.add(dl); return p; } 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. 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; } public int getArity() { // The summation is a function of one variable. return 1; } 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(); variable.setVal(x[0]); for (int i = a; i <= b; i++) { index.setVal(i); sum += func.getValueWithCases(cases); } index.setVal(save); return sum; } 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. public Function derivative(int wrt) { return null; } public Function derivative(Variable x) { return null; } public boolean dependsOn(Variable x) { return false; } public String getName() { return null; } public void setName(String s) { } } } // end class SeriesGrapherApplet