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/            *
*                                                                        *
*************************************************************************/
// An applet belonging to the class AnimatedGraph displays a graph
// of a function that can depend on a parameter.  The value of the 
// parameter can be "animated" so that it ranges from one value ot
// another over a sequence of frames.
import java.awt.*;
import java.applet.Applet;
import java.util.*;
import edu.hws.jcm.draw.*;
import edu.hws.jcm.data.*;
import edu.hws.jcm.functions.*;
import edu.hws.jcm.awt.*;
public class AnimatedGraph extends GenericGraphApplet {
   // Declare some private variables that are created in one method in
   // this class and used in a second method.
   private Function func;   // The function that is graphed.
   private Graph1D graph;   // The graph of the function.
   
   private Animator animator; // Animates the graph
   private Variable kVar;   // The parameter variable
   
   private VariableInput kMin, kMax, kIntervals;  // min, max, and number of intervals for the animator.  Might be null.
   protected void setUpParser() {  // Override this to create the animator and add its variable to the parser.
   
      int options = Animator.START_STOP_BUTTON | Animator.PAUSE_BUTTON  | Animator.LOOP_CHOICE;
      if  ( ! "no".equalsIgnoreCase(getParameter("UseNextAndPrev","yes")) )
         options |=  Animator.PREV_BUTTON | Animator.NEXT_BUTTON;
      animator = new Animator(options);
      kVar = animator.getValueAsVariable( getParameter("Parameter","k") );   
      parser.add(kVar);
      
      super.setUpParser();
      parameterDefaults = new Hashtable();
      String defaultFunction = xVar.getName() + " / (" + kVar.getName() + " - " + xVar.getName() + "^2)";
      parameterDefaults.put("Function",defaultFunction);
      if (! "no".equalsIgnoreCase(getParameter("UseAnimatorInputs")))
         parameterDefaults.put("TwoLimitsColumns","yes"); // change default if we need space for animator inputs
      
   } // end setUpParser()
   
   protected void setUpBottomPanel() {  // Overridden to add the sliders at the bottom of the applet.
      super.setUpBottomPanel();  // Do the default setup.
      
      // If there is a functionInput box, then the SOUTH position of the mainPanel already contains
      // the inputPanel that contains that box.  If so, add the animator to the SOUTH position of
      // the inputPanel.  (This is a good place, in general, to put extra input objects.)
      // If there is no inputPanel, then the SOUTH position of the mainPanel is empty, so put
      // the animator there.
      
      if (inputPanel != null)
         inputPanel.add(animator, BorderLayout.SOUTH);
      else
         mainPanel.add(animator, BorderLayout.SOUTH);
   } // end setUpBottomPanel()
   protected void setUpCanvas() { // Overridden to add the graph to the canvas and do other chores.
      super.setUpCanvas();  // Do the default setup.
      // When setUpCanvas() is called, the functionInput already exists, if one is
      // to be used, since it is created in setUpBopttomPanel(), which is called
      // before setUpCanvas.  If functionInput exists, add a graph of the function
      // from functionInput to the canvas.  If not, create a graph of the function
      // specified by the parameter named "Function" (or use sin(k*x) if none is specified).
      if (functionInput != null)
         func = functionInput.getFunction(xVar);
      else {
         String def = getParameter("Function");  // default value is set in setUpParser()
         func = new SimpleFunction( parser.parse(def), xVar );
      }
      // Create a graph of the function and add it to the canvas.
      
      graph = new Graph1D(func);
      graph.setColor(getColorParam("GraphColor", Color.magenta));
      canvas.add(graph);
      
      // Set up the min, max, and intervals property of the animator
      
      if  (! "no".equalsIgnoreCase(getParameter("UseAnimatorInputs"))) {
         kMin = new VariableInput(kVar.getName() + "Start",getParameter("ParameterMin","-2"));
         kMax = new VariableInput(kVar.getName() + "End",getParameter("ParameterMax","2"));
         kIntervals = new VariableInput("Intervals", getParameter("Intervals","25"));
         kIntervals.setInputStyle(VariableInput.INTEGER);
         kIntervals.setMin(1);
         kIntervals.setMax(1000);
         kMin.setOnUserAction(mainController);
         kMax.setOnUserAction(mainController);
         kIntervals.setOnUserAction(mainController);
         animator.setMin(kMin);
         animator.setMax(kMax);
         animator.setIntervals(kIntervals);
         if (limitsPanel != null) {
               // componets will be added to limitsPanel in setUpLimitsPanel()
            mainController.add(kMin);  // This is not done automatically, since they are in a limits panel  
            mainController.add(kMax);
            mainController.add(kIntervals);
         }
         else {
            JCMPanel ap = new JCMPanel(9,0);
            ap.setBackground(getColorParam("PanelBackground", Color.lightGray));
            ap.add(new Label(kMin.getName()));
            ap.add(kMin);
            ap.add(new Label());
            ap.add(new Label(kMax.getName()));
            ap.add(kMax);
            ap.add(new Label());
            ap.add(new Label(kIntervals.getName()));
            ap.add(kIntervals);
            ap.add(new Label());
            mainPanel.add(ap,BorderLayout.EAST);
         }
      }
      else {
         try {
            animator.setMin( (new Double(getParameter("ParameterMin","-2"))).doubleValue() );
            animator.setMax( (new Double(getParameter("ParameterMax","2"))).doubleValue() );
            animator.setIntervals( (int)Math.round((new Double(getParameter("Intervals","25"))).doubleValue()) );
         }
         catch (NumberFormatException e) {
         }
      }
      animator.setOnChange(mainController);
      // Add a DrawString to show the current value of the parameter
      if ( ! "no".equalsIgnoreCase(getParameter("ShowParameter","yes")) ) {
         DrawString param = new DrawString(kVar.getName() + " = #", DrawString.BOTTOM_LEFT, new Value[] { kVar });
         param.setBackgroundColor(canvas.getBackground());
         Color c = getColorParam("ParameterColor",Color.black);
         param.setColor(c);
         canvas.add(param);
      }
   } // end setUpCanvas
   protected void setUpLimitsPanel() {
     super.setUpLimitsPanel();
     if (limitsPanel != null && kMin != null) {  // add animator inputs to limits panel
         limitsPanel.addComponentPair(kMin,kMax);
         limitsPanel.addComponent(kIntervals);
     }
   }
 
   protected void doLoadExample(String example) {
         // This method is called when the user loads an example from the 
         // example menu (if there is one).  It overrides an empty method
         // in GenericGraphApplet.
         //   For the AnimatedGraph applet, the example string should contain
         // an expression that defines the function to be graphed.  This can optionally
         // be followed by a semicolon and a list of four to nine numbers.
         // The first four numbers give the x- and y-limits to be used for the
         // example.  If they are not present, then -5,5,-5,5 is used.  The
         // next three numbers specify the minimum value for the parameter, the
         // maximum number, and the number of intervals in the animation.
         // The eigth number, if present, specifies the starting loop style
         // for the animation with the following code:  0 for once-through,
         // 1 for loop, and 2 for back-and-forth.  The ninth number, if
         // present, tells whether to start the animation immediately upon
         // loading.  If it is 1, the animation is started.  If it is
         // not specified or is any value other than 1, the animation is not started.
         
      animator.stop();
         
      int pos = example.indexOf(";");
      boolean startAnimation = false;
      double[] limits = { -5,5,-5,5 }; // x- and y-limits to use
      if (pos > 0) { 
               // Get limits from example text.
         String nums = example.substring(pos+1);
         example = example.substring(0,pos);
         StringTokenizer toks = new StringTokenizer(nums, " ,");
         if (toks.countTokens() >= 4) {
            for (int i = 0; i < 4; i++) {
               try {
                   Double d = new Double(toks.nextToken());
                   limits[i] = d.doubleValue();
               }
               catch (NumberFormatException e) {
               }
            }
         }
         if (toks.hasMoreTokens()) {
            try {
               double d = (new Double(toks.nextToken())).doubleValue();
               if (kMin == null)
                  animator.setMin(d);
               else
                  kMin.setVal(d);
            }
            catch (NumberFormatException e) {
            }
         }
         if (toks.hasMoreTokens()) {
            try {
               double d = (new Double(toks.nextToken())).doubleValue();
               if (kMax == null)
                  animator.setMax(d);
               else
                  kMax.setVal(d);
            }
            catch (NumberFormatException e) {
            }
         }
         if (toks.hasMoreTokens()) {
            try {
               int d = (int)Math.round((new Double(toks.nextToken())).doubleValue());
               if (kIntervals == null)
                  animator.setIntervals(d);
               else
                  kIntervals.setVal(d);
            }
            catch (NumberFormatException e) {
            }
         }
         if (toks.hasMoreTokens()) {
            try {
               int d = (int)Math.round((new Double(toks.nextToken())).doubleValue());
               animator.setLoopStyle(d);
            }
            catch (NumberFormatException e) {
            }
         }
         if (toks.hasMoreTokens()) {
            try {
               int d = (int)Math.round((new Double(toks.nextToken())).doubleValue());
               startAnimation = (d == 1);
            }
            catch (NumberFormatException e) {
            }
         }
      }
      
      // Set up the example data and recompute everything.
      if (functionInput != null) {
            // If there is a function input box, put the example text in it.
         functionInput.setText(example);
      }
      else { 
           // If there is no user input, set the function in the graph directly.
         try {
            func = new SimpleFunction( parser.parse(example), xVar );
            graph.setFunction(func);
         }
         catch (ParseError e) {  
             // There should't be parse error's in the Web-page
             // author's examples!  If there are, the function
             // just won't change.
         }
      }
      CoordinateRect coords = canvas.getCoordinateRect(0);
      coords.setLimits(limits);
      coords.setRestoreBuffer();
      mainController.compute();
      if (startAnimation) {
         try {  // insert a small delay before animation starts
            synchronized(this) {
               wait(250);
            }
         }
         catch (InterruptedException e) {
         }
         animator.start();
      }
      
   } // end doLoadExample()
   
   public void stop() {  // stop animator when applet is stopped
      animator.stop();
      super.stop();
   }
 
      public static void main(String[] a){
         javax.swing.JFrame f = new javax.swing.JFrame();
         Applet app = new AnimatedGraph();
         app.init();
         
         f.getContentPane().add (app);
         f.pack();
         f.setSize (new Dimension (500, 500));
         f.setVisible(true);
      }   
   
} // end class FamiliesOfGraphs
           
       
jcm1-source.zip( 532 k)