[ prev | main | next ]

Graph Applet 3

This applet that draws the graph of a function and marks
a point on the graph with a pair of lines. It's a minor
variation on GraphApplet2, but the programming is
different. In this version, I don't use JCMPanel's to build the
applet, so I have to do more setup myself. In this version, I mark
the point on the graph differently:

The following source code is commented only where it differs
from GraphApplet2.

import java.awt.*;
import edu.hws.jcm.data.*;
import edu.hws.jcm.draw.*;
import edu.hws.jcm.awt.*;

public class GraphApplet3 extends java.applet.Applet {

   private DisplayCanvas canvas;
   public void stop() {
   public void init() {
      Parser parser = new Parser();
      Variable x = new Variable("x");

      canvas = new DisplayCanvas();
      canvas.add(new Panner());
      CoordinateRect coords = canvas.getCoordinateRect();
      LimitControlPanel limits =
           new LimitControlPanel( LimitControlPanel.SET_LIMITS | LimitControlPanel.RESTORE, false);
      ExpressionInput input = new ExpressionInput("sin(x)+2*cos(3*x)", parser);
      Function func = input.getFunction(x);
      Graph1D graph = new Graph1D(func);
      VariableInput xInput = new VariableInput();
      VariableSlider xSlider = new VariableSlider( coords.getValueObject(CoordinateRect.XMIN), 
                                                      coords.getValueObject(CoordinateRect.XMAX) );
      Value yValue = new ValueMath(func,xSlider); // A Value object to represent the y-coord of the point.
         // Instead of using a crosshair to mark a point on the graph, it is marked
         //   with two gray lines and a small magenta oval.  These geometric objects
         //   are represented as objects belonging to the class DrawGeometric,
         //   which makes it possible to draw a variety of geometric figures on a
         //   DisplayCanvas.
      DrawGeometric vLine = new DrawGeometric(DrawGeometric.LINE_ABSOLUTE,xSlider,new Constant(0),xSlider,yValue);
      DrawGeometric hLine = new DrawGeometric(DrawGeometric.LINE_ABSOLUTE,new Constant(0),yValue,xSlider,yValue);
      DrawGeometric point = new DrawGeometric(DrawGeometric.OVAL_CENTERED,xSlider,yValue,3,3);

      DrawString info = new DrawString("x = #\nf(x) = #", DrawString.TOP_LEFT,
                                                             new Value[] { xSlider, yValue });
      info.setFont( new Font("SansSerif",Font.BOLD,12) );
      info.setColor( new Color(0,100,0) );

      ComputeButton graphIt = new ComputeButton("Graph It!");
      setLayout(new BorderLayout(3,3));

          // In this version of the applet, I have built the interface from
          //    regular Panels instead of JCMPanels.  This puts responsibility
          //    for a lot more setup in the hands of the programmer.  The gain
          //    is in efficiency.  Here, my object is to avoid recomputing the
          //    graph just because the user adjusts the slider.  To do this,
          //    I have to use two controllers, which listen for different user
          //    actions.  (Of course, computers are so fast now that the extra
          //    computation probably doesn't add a perceptible delay.  In this 
          //    case, the extra design work is probably not worth the trouble.)
      Panel top = new Panel();
      top.setLayout(new BorderLayout(3,3));
      Panel bottom = new Panel();
      bottom.setLayout(new BorderLayout(3,3));
      add(canvas, BorderLayout.CENTER);  // Add components directly to the applet.
      add(limits, BorderLayout.EAST);
      add(bottom, BorderLayout.SOUTH);
      add(top, BorderLayout.NORTH);

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

      canvas.add( new Axes() );
      canvas.add( hLine );
      canvas.add( vLine );
      canvas.add( point );
      canvas.add( graph );
      canvas.add( info );
      canvas.add( new DrawBorder(Color.darkGray, 2) );
      Controller cc = new Controller();  // This controller will listen for changes
      xInput.setOnUserAction(cc);        //   In the VariableSlider or VariableInput,
      xSlider.setOnUserAction(cc);       //   As well as in the limits on the coordinates.

      cc.add( new Tie(xSlider,xInput) ); // Ties the values of the slider and VariableInput.
      cc.add( hLine );    // I have to tell the controller which objects need to be recomputed
      cc.add( vLine );    //    when it sees some kind of change.  This includes all the 
      cc.add( point );    //    objects that depend on the x-coordinate.  Note that is ALSO
      cc.add( info );     //    includes xInput and xSlider, which need to be checked for 
      cc.add( xInput );   //    changes in their values.  The value associated with a
      cc.add( xSlider );  //    VariableSlider or VariableInput doesn't actually change
                          //    until a Controller checks it.  (All this is the part of the
                          //    setup that is done automatically when you build your
                          //    interface from JCMPanels.)
      Controller gc = new Controller();   // This controller will listen for changes
      input.setOnUserAction(gc);          //   in the function definition.
      gc.add(input);   // I have to add the ExpressionInput to a Controller, since the
                       //   function doesn't actually change unless the ExpressionInput
                       //   is checked by a Controller.
      gc.add(graph);   // The graph needs to be recomputed when the function changes.
      gc.add(cc);      // You can add one Controller to another.  Here, gc will call
                       //   on cc to do all its checks and computations, in addition to.
                       //   recomputing the graph.
      gc.setErrorReporter(canvas);      // Set error reporters for the Controller.
                                        //   This error reporter is also used by
                                        //   cc, which has been added as a subcontroller
                                        //   to gc.  So, it's not necessary to set a separate
                                        //   error reporter for cc

      limits.setErrorReporter(canvas);  // Error reporter for the LimitControlPanel.
   } // end init()

} // end class SimpleGraph3

[ prev | main | next ]