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() { canvas.releaseResources(); } public void init() { Parser parser = new Parser(); Variable x = new Variable("x"); parser.add(x); canvas = new DisplayCanvas(); canvas.setHandleMouseZooms(true); canvas.add(new Panner()); CoordinateRect coords = canvas.getCoordinateRect(); LimitControlPanel limits = new LimitControlPanel( LimitControlPanel.SET_LIMITS | LimitControlPanel.RESTORE, false); limits.addCoords(canvas); 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); vLine.setColor(Color.lightGray); hLine.setColor(Color.lightGray); point.setColor(Color.magenta); point.setFillColor(Color.magenta); 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) ); info.setOffset(10); ComputeButton graphIt = new ComputeButton("Graph It!"); setLayout(new BorderLayout(3,3)); setBackground(Color.lightGray); // 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. coords.setOnChange(cc); 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. graphIt.setOnUserAction(gc); 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