[ 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
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



[ prev | main ]