JCCKit logo

2.1 Example: Animated Chart

The applet AnimatedChart (see the Examples page) presents a chart in an animated way. By clicking the button "animate" all bars are deleted first (more precisely their height is set to zero). Now each bar grows with a constant speed up to its value.

The basic structure of the applet reads (click here to see the complete code):

public class AnimatedChart extends Applet {
  private double[] _data = new double[] {55.5, 34.2, 47.4, 53.1, 69.9, 68.7, 81.1};
  private DataPlot _dataPlot;

  public void init() {
    GraphicsPlotCanvas plotCanvas = createPlotCanvas();

    _dataPlot = new DataPlot();
    _dataPlot.addElement(new DataCurve(""));
    plotCanvas.connect(_dataPlot);

    setLayout(new BorderLayout());
    add(plotCanvas.getGraphicsCanvas(), BorderLayout.CENTER);
    add(createControlPanel(), BorderLayout.SOUTH);
  }

  private GraphicsPlotCanvas createPlotCanvas() { ... }

  private Panel createControlPanel() { ... }
}
Highlighted code uses JCCKit.

The init() method (which is always invoked when the applet is started) does the following:

  1. First, a GraphicsPlotCanvas is created. The actual creation is delegated to a private method. Below we will see how it is created.
  2. Next, a DataPlot with an empty DataCurve is created. Hence, an empty plot with no bars will be seen after the applet has been started. The actual chart data are stored in _data. They will be shown after the animate button has been hit.
  3. The DataPlot instance is connected with the GraphicsPlotCanvas. This establishes a connection between the DataPlot instance and the wrapped Plot object. Due to this connection any change in the data of _dataPlot will automatically updated in the view of the GraphicsPlotCanvas.
  4. The view of the GraphicsPlotCanvas will be added to the applet panel. Note, that getGraphicsCanvas() returns an instance of java.awt.Canvas. In the case of Swing it is better to use getGraphicsJPanel() which returns a JPanel wrapper.
  5. Finally, a control panel with the Button labeled animate will be added. Again the creation is delegated to a private method.

Creating a GraphicsPlotCanvas is mainly a configuration task concerning

  • coordinate system,
  • curve appearance, and
  • legend.
A ConfigParameters object is needed to create a new instance of GraphicsPlotCanvas. This is easily done with the help of a java.util.Properties object which is either loaded from a file or programmatically filled as in our case (another way is shown in the second example):
private GraphicsPlotCanvas createPlotCanvas() {
  Properties props = new Properties();
  ConfigParameters config = new ConfigParameters(new PropertiesBasedConfigData(props));
  props.put("plot/legendVisible", "false");
  props.put("plot/coordinateSystem/xAxis/minimum", "-0.5");
  props.put("plot/coordinateSystem/xAxis/maximum", "6.5");
  props.put("plot/coordinateSystem/xAxis/axisLabel", "");
  props.put("plot/coordinateSystem/xAxis/ticLabelFormat/className",
            "jcckit.plot.TicLabelMap");
  props.put("plot/coordinateSystem/xAxis/ticLabelFormat/map",
            "0=Mo;1=Tu;2=We;3=Th;4=Fr;5=Sa;6=Su");
  props.put("plot/coordinateSystem/yAxis/axisLabel", "growth rate");
  props.put("plot/coordinateSystem/yAxis/maximum", "100");
  props.put("plot/coordinateSystem/yAxis/ticLabelFormat", "%d%%");
  props.put("plot/curveFactory/definitions", "curve");
  props.put("plot/curveFactory/curve/withLine", "false");
  props.put("plot/curveFactory/curve/symbolFactory/className", "jcckit.plot.BarFactory");
  props.put("plot/curveFactory/curve/symbolFactory/attributes/className",
            "jcckit.graphic.ShapeAttributes");
  props.put("plot/curveFactory/curve/symbolFactory/attributes/fillColor", "0xfe8000");
  props.put("plot/curveFactory/curve/symbolFactory/attributes/lineColor", "0");
  props.put("plot/curveFactory/curve/symbolFactory/size", "0.08");
  props.put("plot/initialHintForNextCurve/className", "jcckit.plot.PositionHint");
  props.put("plot/initialHintForNextCurve/position", "0 0.1");

  return new GraphicsPlotCanvas(config);
}
Here is not the place to explain all parameters. Please, consult the API documentation especially the constructors of PlotCanvas, Plot, CartesianCoordinateSystem, and SimpleCurveFactory. See also Chapter 4.

The animation is started after the animate button has been hit. This is done in an extra Thread which is created inside the ActionListener registrated at the button:

private Panel createControlPanel() {
  Panel controlPanel = new Panel();
  Button startButton = new Button("animate");
  startButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                  new Thread() {
                          public void run() {
                            animate();
                          }
                        }.start();
                }
              });
  controlPanel.add(startButton);

  return controlPanel;
}
The actual animation is delegated to the private method animate() of the applet:
private void animate() {
  DataCurve curve = new DataCurve("");
  for (int i = 0; i < _data.length; i++) {
    curve.addElement(new DataPoint(FIRST_YEAR + i, 0));
  }
  _dataPlot.replaceElementAt(0, curve);

  for (int i = 0; i < _data.length; i++) {
    double x = i;
    double y = 0;
    while (y < _data[i]) {
      try {
        Thread.sleep(50);
      } catch (InterruptedException e) {}
      y = Math.min(_data[i], y + 5);
      curve.replaceElementAt(i, new DataPoint(x, y));
    }
  }
}

In a first step the heights of all bars are set to zero. This is done by creating a new DataCurve with DataPoints where the y-coordinate is zero. The new curve replaces the current one. Note, that no explicit update of the view is necessary. This is done automatically for all Plot instances connected with the DataPlot instance where such data operations like curve replacements take place.

Now the animation starts: For each bar the y-value is increased by 5 until its final value is reached. Between each increase the animation is paused by 50 milliseconds. After an increase the corresponding curve point is replaced by a new instance. Note, that DataPoint is an immutable class. Thus, we can not change coordinates of a data point directly. Again, the view will be updated automatically.