Advanced Graphics Java

/*************************************************************************
*                                                                        *
*  This source code file, and compiled classes derived from it, can      *
*  be used and distributed without restriction, including for commercial *
*  use.  (Attribution is not required but is appreciated.)               * 
*                                                                        *
*   David J. Eck                                                         *
*   Department of Mathematics and Computer Science                       *
*   Hobart and William Smith Colleges                                    *
*   Geneva, New York 14456,   USA                                        *
*   Email: eck@hws.edu          WWW: http://math.hws.edu/eck/            *
*                                                                        *
*************************************************************************/
import java.awt.*;
import edu.hws.jcm.data.*;
import edu.hws.jcm.draw.*;
import edu.hws.jcm.awt.*;
public class GraphApplet2 extends java.applet.Applet {
      public static void main(String[] a){
         javax.swing.JFrame f = new javax.swing.JFrame();
         java.applet.Applet app = new GraphApplet2();
         app.init();
         
         f.getContentPane().add (app);
         f.pack();
         f.setSize (new Dimension (500, 500));
         f.setVisible(true);
      }  
   private DisplayCanvas canvas;
   
   public void stop() {
         // To be a little nicer to the system, I have declared the DisplayCanvas
         // outside the init() method, and I use the standard stop() method of
         // the applet to call canvas.releaseResources().  This frees up the
         // substantial amount of memory that is used for the off-screen copy
         // of image displayed on the canvas.  (This off-screen copy is used for
         // "double-buffering".)
      canvas.releaseResources();
   }
   
   public void init() {
   
      Parser parser = new Parser();      // Create the parser and the variable, x.
      Variable x = new Variable("x");
      parser.add(x);
      canvas = new DisplayCanvas();      // Create the canvas, and set it to do zooming.
      canvas.setHandleMouseZooms(true);
      
      canvas.add(new Panner());  // Adding a panner lets the user right-click-and-drag on the
                                 // canvas to slide the graph around.
      
      CoordinateRect coords = canvas.getCoordinateRect();
          // Behind the scene, a canvas has a CoordinateRect, which actually keeps track
          //   of the x- and y-limits (and does some of the other work of the canvas).
          //   For some purposes, you need the CoordinateRect.  You can obtain it by
          //   calling the DisplayCanvas's getCoordinateRect() method.
      
      LimitControlPanel limits =
           new LimitControlPanel( LimitControlPanel.SET_LIMITS | LimitControlPanel.RESTORE, false);
             // In this case, I am using a LimitControlPanel that contains two buttons.
             //   the usual "Set Limits" button and a "Restore Limits" button.  The second button
             //   button will restore the original limits on the canvas.  The second parameter
             //   to this constructor can be set to true if you would like the components in the
             //   LimitControlPanel to be arranged into two columns instead of one.
             //   Buttons can also be added after the constructor is called by calling
             //   the addButtons() method from class LimitControlPanel.
      limits.addCoords(canvas);
      
      ExpressionInput input = new ExpressionInput("sin(x)+2*cos(3*x)", parser);  // For user input
      Function func = input.getFunction(x);  // The function that will be graphed.
      Graph1D graph = new Graph1D(func);     // The graph itself.
      
      VariableInput xInput = new VariableInput();  // An input box where the user can
                                                   //   specify the x-coordinate of the
                                                   //   point on the graph that is marked 
                                                   //   by the crosshair.  
      
      VariableSlider xSlider = new VariableSlider( coords.getValueObject(CoordinateRect.XMIN), 
                                            coords.getValueObject(CoordinateRect.XMAX) );
          // A VariableSlider is a slider (actually a Scrollbar) that the user can
          //   adjust as a means of inputting a value.  The parameters to the constructor
          //   specify the minimum and maximum of this value.  In this case, the minimum
          //   value is coords.getValueObject(CoordinateRect.XMIN).  This says that the
          //   minimum value on the slider is given by the minimum x-value on the canvas's
          //   CoordinateRect.  This minimum is adjusted automatically when the limits
          //   on the CoordinateRect change.  The maximum value is similar.
          // This Variable Slider is actually used as a second way of inputting the
          //   x-coordinate of the point where the crosshair is shown.  Later, the value of
          //   the slider will be "tied" to the value in the VariableInput.  You should check
          //   that when you change one, the other is also changed.  (To change the
          //   value associated with the VariableInput box, you have to press return
          //   in that box.)
      
      DrawString info = new DrawString("x = #\nf(x) = #", DrawString.TOP_LEFT,
                                   new Value[] { xSlider, new ValueMath(func,xSlider) });
          // A DrawString draws a string on a DisplayCanvas.  The string can have line
          //   breaks, indicated by '\n', and can contain embedded Values, indicated
          //   by '#'.  The position of the string is DrawString.TOP_LEFT.  That is,
          //   it is in the top-left corner of the canvas.  The third parameter is
          //   an array of Value objects whose values are substituted for the #'s
          //   in the string.
          
      info.setFont( new Font("SansSerif",Font.BOLD,12) );  // Set properties of the DrawString.
      info.setColor( new Color(0,100,0) );
      info.setOffset(10);
      
      ComputeButton graphIt = new ComputeButton("Graph It!");
          // A ComputeButton is just a button that can be registered with a Controller,
          //   so that clicking on the Button causes the Controller to compute.  In this
          //   case, this is a redundant input, since pressing return in the ExpressionInput
          //   box will accomplish the same thing.  However, the button gives the user
          //   a more obvious way to change the function that is graphed.
      
      JCMPanel main = new JCMPanel();         // The interface is constructed of JCMPanels.
      JCMPanel top = new JCMPanel();          
      JCMPanel bottom = new JCMPanel();
      main.add(canvas, BorderLayout.CENTER);
      main.add(limits, BorderLayout.EAST);
      main.add(bottom, BorderLayout.SOUTH);
      main.add(top, BorderLayout.NORTH);
      main.setInsetGap(3);
      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);
      setLayout(new BorderLayout());   // Set up for the Applet itself.
      add(main, BorderLayout.CENTER);
      setBackground(Color.lightGray);      
      
      canvas.add( new Axes() );  // Add a set of axes to the DisplayCanvas.
      canvas.add(graph);         // Add the graph of the function to the DisplayCanvas.
      canvas.add( new Crosshair(xSlider, func) );  // Add a CrossHair to the canvas.
                                                   //   The crosshair is on the graph of the
                                                   //   function, func, at the point whose xCoordinate
                                                   //   is given by the value on the slider, xSlider.
      canvas.add( info );  // Add the DrawString to the canvas.
      canvas.add( new DrawBorder(Color.darkGray, 2) );  // Add a 2-pixel dark gray border around
                                                        //   edges of the canvas.
      
      main.gatherInputs();  // The Controller for the main panel.
                            //    must be set to respond to user actions
                            //    on the input objects.  The gatherInputs()
                            //    method is an easy way to do this.  This calls
                            //    the setOnUserAction() method of the four
                            //    input objects:  input, graphIt, xInput
                            //    and xSlider.
      
      Controller controller = main.getController();  // Get the controller from the main panel.
                                                     // We still need it for a few things...
                                                     
      graphIt.setOnUserAction(controller);  // ComputeButtons aren't handled automatically
                                            //   by main.gatherInput().  It must be set
                                            //   to notify the controller when it is clicked,
                                            //   in order for the applet to be recomputed
                                            //   when the button is clicked.
      coords.setOnChange(controller);   // Because the VariableSlider depends on the limits
                                        //   on the CoordinateRect, the controller must also
                                        //   listen for changes in these limits.  The gatherInputs()
                                        //   doesn't "gather" in the coordinate rect.      
                                        
      controller.add( new Tie(xSlider,xInput) );  // This is the thing that synchronizes the
                                                  //   values on the VariableSlider and the
                                                  //   VariableSlider.  After checking all the
                                                  //   inputs in the applet, the Controller
                                                  //   "synchronizes" the two objects named
                                                  //   in the "Tie".
      
      // I forgot to set an errorReporter for this applet!
      // You can check what happens when the input in the applet
      // contains an error. (The error message is printed to
      // standard output.  In Netscape, for example, this means
      // that it goes to the Java Console, where it will do the
      // user no good at all.)
 
   }  // end init()
} // end class SimpleGraph
           
       
jcm1-source.zip( 532 k)