Search code examples
javamultithreadingjfreechart

JFreeChart chart is lagging


This program is supposed to graph and animate a sine wave by using a thread to "move over" the points in the XYSeries that draws the wave. After a few seconds, the chart starts to flash and zooms in and out randomly. I don't know if this is a problem with my code or with my computer (probably the code).

import java.awt.geom.Point2D;
import static java.lang.Math.PI;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

public class WaveGraph extends JFrame
{
    double numOfWaves = 2;
    double waveAmp = 2;
    double waveLength = 10;
    double waveRes = 20;
    double median = ((waveLength*numOfWaves)/2);
    int numOfPoints = (int)(numOfWaves*waveRes);
    boolean going = true;
    boolean left = true;
    XYSeriesCollection dataset = new XYSeriesCollection();
    Point2D.Double[] points;

WaveGraph()
{
    XYSeries buoys = new XYSeries("Buoys");
    XYSeries series = new XYSeries("Wave");
    points = new Point2D.Double[numOfPoints];
    for(int i=0; i<numOfPoints; i++)
    {
        Point2D.Double temp = new Point2D.Double();
        temp.x = i*(waveLength/waveRes);
        temp.y = waveAmp*Math.sin((2*PI)/waveLength*temp.x);
        points[i] = temp;
        series.add(points[i].x, points[i].y);
        if(i==(numOfPoints/2)-1)
            buoys.add(median, points[i].y);
    }

    buoys.add(median, 0);
    dataset.addSeries(series);
    dataset.addSeries(buoys);
    JFreeChart chart = ChartFactory.createXYLineChart(
    "Sine Wave", // chart title
    "Wavelength(meters)", // x axis label
    "Amplitude (meters)", dataset, // data
    PlotOrientation.VERTICAL,
    false, // include legend
    false, // tooltips
    false // urls
    );
    XYPlot plot = (XYPlot)chart.getPlot();
    XYLineAndShapeRenderer renderer
    = (XYLineAndShapeRenderer) plot.getRenderer();
    renderer.setSeriesShapesFilled(1, true);
    renderer.setSeriesShapesVisible(1, true);
    NumberAxis domain = (NumberAxis)plot.getDomainAxis();
    domain.setRange(0.00, waveLength*numOfWaves);
    NumberAxis range = (NumberAxis)plot.getRangeAxis();
    range.setRange(-waveAmp*1.3, waveAmp*1.3);
    ChartPanel cp = new ChartPanel(chart);
    cp.setPreferredSize(new java.awt.Dimension(500, 270));
    setContentPane(cp);
    setDefaultCloseOperation(EXIT_ON_CLOSE);         
    pack();
    setLocationRelativeTo(null);
    setVisible(true);
    WaveRunner run = new WaveRunner();
}

class WaveRunner extends Thread
{
    WaveRunner()
    {
        this.start();
    }

    public void run()
    {
        double[] temp = new double[numOfPoints];
        XYSeries temp2, temp3;
        while(going)
        {
            temp2 = dataset.getSeries(0);
            temp3 = dataset.getSeries(1);
            temp2.delete(0, numOfPoints-1);
            temp3.delete(0, 1);
            for(int i=0; i<numOfPoints; i++)
            {
                if(left){
                try{
                    temp[i] = points[i+1].y;
                }
                catch(java.lang.ArrayIndexOutOfBoundsException exc){
                    temp[numOfPoints-1] = points[0].y;
                }}
                else{
                try{
                    temp[i] = points[i-1].y;
                }
                catch(java.lang.ArrayIndexOutOfBoundsException exc){
                    temp[0] = points[numOfPoints-1].y;
                }}
            }
            for(int i=0; i<numOfPoints; i++)
            {
                points[i].y = temp[i];
                temp2.add(points[i].x, points[i].y);
                if(i==(numOfPoints/2))
                {    
                    temp3.add(median, points[i].y);
                    temp3.add(median, 0);
                }
            }
            try
            {
                Thread.sleep(50);
            }
            catch(InterruptedException exc)
            {}
        }    
    }
}

public static void main(String[] args)
{
    WaveGraph test = new WaveGraph();
}
}

Solution

  • Your example is incorrectly synchronized in that it updates the chart's dataset from WaveRunner. The chart's model is displayed in a ChartPanel, so it should be updated only on the event dispatch thread. Instead, pace the animation using Swing Timer, as shown here and here, or SwingWorker, as shown here.