/* Copyright 2014 by Sean Luke and George Mason University Licensed under the Academic Free License version 3.0 See the file "LICENSE" for more information */ package sim.util.media.chart; import java.awt.*; import java.util.*; import java.awt.event.*; import javax.swing.*; import sim.util.gui.*; // From JFreeChart import org.jfree.chart.*; import org.jfree.chart.plot.*; import org.jfree.data.general.*; import org.jfree.data.statistics.*; import org.jfree.data.xy.*; import org.jfree.chart.renderer.xy.*; import org.jfree.util.*; // from iText (www.lowagie.com/iText/) import com.lowagie.text.*; import com.lowagie.text.pdf.*; /* // looks like we'll have to move to these soon import com.itextpdf.text.*; import com.itextpdf.text.pdf.*; */ public class BubbleChartGenerator extends XYChartGenerator { public void removeSeries(int index) { super.removeSeries(index); update(); } public void moveSeries(int index, boolean up) { super.moveSeries(index, up); update(); } protected void buildChart() { DefaultXYZDataset dataset = new DefaultXYZDataset(); chart = ChartFactory.createBubbleChart("Untitled Chart","Untitled X Axis","Untitled Y Axis",dataset, PlotOrientation.VERTICAL, false, true, false); chart.setAntiAlias(true); chartPanel = buildChartPanel(chart); setChartPanel(chartPanel); // most irritating: you can't change the scale type once you've // constructed the renderer. :-( chart.getXYPlot().setRenderer(new XYBubbleRenderer(XYBubbleRenderer.SCALE_ON_DOMAIN_AXIS)); // this must come last because the chart must exist for us to set its dataset setSeriesDataset(dataset); } protected void update() { // we'll rebuild the plot from scratch SeriesAttributes[] sa = getSeriesAttributes(); //XYPlot xyplot = (XYPlot)(chart.getPlot()); DefaultXYZDataset dataset = new DefaultXYZDataset(); for(int i=0; i < sa.length; i++) { BubbleChartSeriesAttributes attributes = (BubbleChartSeriesAttributes)(sa[i]); double scale = attributes.getScale(); // copy over values, and square-root the z-value. // A bug in JFreeChart means that z-values are not shown by // area but rather by (ugh) diameter. // Also we'll take advantage of this situation to allow // for user-defined scaling of the bubbles on a per-series basis. double[][] values = attributes.getValues(); double[][] v2 = new double[values.length][values[0].length]; for(int k = 0; k < v2.length; k++) for(int j = 0; j < v2[k].length; j++) v2[k][j] = values[k][j]; for(int j = 0; j < v2[2].length; j++) v2[2][j] = Math.sqrt(scale * v2[2][j]); dataset.addSeries(new UniqueString(attributes.getSeriesName()), v2); } setSeriesDataset(dataset); } public SeriesAttributes addSeries(double[][] values, String name, final org.jfree.data.general.SeriesChangeListener stopper) { DefaultXYZDataset dataset = (DefaultXYZDataset)(getSeriesDataset()); int i = dataset.getSeriesCount(); dataset.addSeries(new UniqueString(name), values); // need to have added the dataset BEFORE calling this since it'll try to change the name of the series BubbleChartSeriesAttributes csa = new BubbleChartSeriesAttributes(this, name, i, values, stopper); seriesAttributes.add(csa, name); revalidate(); update(); // won't update properly unless I force it here by letting all the existing scheduled events to go through. Dumb design. :-( SwingUtilities.invokeLater(new Runnable() { public void run() { update(); } }); return csa; } public void updateSeries(int index, double[][] vals) { if (index < 0) // this happens when we're a dead chart but the inspector doesn't know return; if (index >= getNumSeriesAttributes()) // this can happen when we close a window if we use the Histogram in a display return; BubbleChartSeriesAttributes series = (BubbleChartSeriesAttributes)(getSeriesAttribute(index)); series.setValues(vals); } }