Animated Graph
/*************************************************************************
* *
* 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)Related examples in the same category