1

I have a scatterplot which plots positions of agents. These positions change. I was wondering how can I repaint/redraw the scatterplot with the new positions

my drawing method. I need to redraw in the updatePositions function. Is there any way to implement any listener for ScatterPlot?

private ChartPanel createPanel() {
    JFreeChart jfreechart = ChartFactory.createScatterPlot(
            title, "", "", initPositions(),PlotOrientation.VERTICAL, true, true, false);
    XYPlot xyPlot = (XYPlot) jfreechart.getPlot();
    xyPlot.setDomainCrosshairVisible(true);
    xyPlot.setRangeCrosshairVisible(true);
    XYItemRenderer renderer = xyPlot.getRenderer();
    renderer.setSeriesPaint(0, Color.blue);
    adjustAxis((NumberAxis) xyPlot.getDomainAxis(), true);
    adjustAxis((NumberAxis) xyPlot.getRangeAxis(), false);
    xyPlot.setBackgroundPaint(Color.white);
    return new ChartPanel(jfreechart);
}

private void adjustAxis(NumberAxis axis, boolean vertical) {
    axis.setRange(-1, lattice+1);
    axis.setTickUnit(new NumberTickUnit(1));
    axis.setVerticalTickLabels(vertical);
}

private XYDataset initPositions() {
    XYSeriesCollection xySeriesCollection = new XYSeriesCollection();

    for (int i = 0; i < populationSize; i++) {
        if(population.get(i).status==1){
            healthy.add(population.get(i).position[0], population.get(i).position[1]);
        }else if(population.get(i).status==2){
            infected.add(population.get(i).position[0], population.get(i).position[1]);
        }else if(population.get(i).status==3){
            recovered.add(population.get(i).position[0], population.get(i).position[1]);
        }
    }

    xySeriesCollection.addSeries(healthy);
    xySeriesCollection.addSeries(infected);
    xySeriesCollection.addSeries(recovered);
    return xySeriesCollection;
}

public void clear(){
    healthy.clear();
    infected.clear();
    recovered.clear();
}
public void updatePositions(ArrayList<Person> pop ){
    population = pop;

    for (int i = 0; i < populationSize; i++) {
        if(population.get(i).status==1){
            healthy.addOrUpdate(population.get(i).position[0], population.get(i).position[1]);
        }else if(population.get(i).status==2){
            infected.addOrUpdate(population.get(i).position[0], population.get(i).position[1]);
        }else if(population.get(i).status==3){
            recovered.addOrUpdate(population.get(i).position[0], population.get(i).position[1]);
        }
    }
}

this is the method in the main class. The update of the positions is done at the "move" function

    public static void main(String [] args){
    createPopulation(populationSize);
    initInfection(infectRatio);

    EventQueue.invokeLater(new Runnable() {             
        @Override           
        public void run() {         
            DrawArea demo = new DrawArea("Demo", lattice, populationSize,population);               
            demo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);                
            demo.pack();                
            demo.setLocationRelativeTo(null);               
            demo.setVisible(true);
            for(int i =0;i<1000;i++){

                for(int j=0; j<populationSize; j++){
                    population.get(j).move(0.8);
                }
                demo.clear();
                demo.updatePositions(population);
            }
        }          
    });
}
MayTheSchwartzBeWithYou
  • 1,181
  • 1
  • 16
  • 32

1 Answers1

1

As shown below, the chart (view) listens to its dataset (model) and updates itself accordingly. When the Move button is pressed, each XYDataItem in the series in modified in update() to reflect its new position.

image

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.util.*;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import org.jfree.chart.*;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.axis.NumberTickUnit;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.xy.XYDataItem;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

/**
 * @see http://stackoverflow.com/a/19749344/230513
 * @see http://stackoverflow.com/a/7208723/230513
 */
public class ScatterMove extends JFrame {

    private static final int N = 16;
    private static final String title = "Scatter Move Demo";
    private static final Random rand = new Random();
    private XYSeries moved = new XYSeries("Population");

    public ScatterMove(String s) {
        super(s);
        update();
        final ChartPanel chartPanel = createDemoPanel();
        this.add(chartPanel, BorderLayout.CENTER);
        JPanel control = new JPanel();
        control.add(new JButton(new AbstractAction("Move") {
            @Override
            public void actionPerformed(ActionEvent e) {
                moved.clear();
                update();
            }
        }));
        this.add(control, BorderLayout.SOUTH);
    }

    private void update() {
        for (int i = 0; i < N; i++) {
            moved.add(new XYDataItem(rand.nextGaussian(), rand.nextGaussian()));
        }
    }

    private ChartPanel createDemoPanel() {
        JFreeChart jfreechart = ChartFactory.createScatterPlot(
            title, "X", "Y", createSampleData(),
            PlotOrientation.VERTICAL, true, true, false);
        XYPlot xyPlot = (XYPlot) jfreechart.getPlot();
        XYItemRenderer renderer = xyPlot.getRenderer();
        NumberAxis domain = (NumberAxis) xyPlot.getDomainAxis();
        domain.setRange(-3.0, 3.0);
        domain.setTickUnit(new NumberTickUnit(1));
        NumberAxis range = (NumberAxis) xyPlot.getRangeAxis();
        range.setRange(-3.0, 3.0);
        range.setTickUnit(new NumberTickUnit(1));
        return new ChartPanel(jfreechart){

            @Override
            public Dimension getPreferredSize() {
                return new Dimension(640, 480);
            }
        };
    }

    private XYDataset createSampleData() {
        XYSeriesCollection xySeriesCollection = new XYSeriesCollection();
        xySeriesCollection.addSeries(moved);
        return xySeriesCollection;
    }

    public static void main(String args[]) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                ScatterMove demo = new ScatterMove(title);
                demo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                demo.pack();
                demo.setLocationRelativeTo(null);
                demo.setVisible(true);
            }
        });
    }
}
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • well there goes my sleep xD. Thank you I will try it. It seems it will work with heavy modification(the points change in another class so I have to update/pass-to-the-plot-class the points everytime) – MayTheSchwartzBeWithYou Nov 03 '13 at 03:37
  • 1
    Instead of clearing and adding, you might want to look and extending an abstract class that implements `XYDataset`, as shown [here](http://stackoverflow.com/a/12481509/230513). – trashgod Nov 03 '13 at 10:35