/*
Copyright 2006 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.chart.renderer.xy.*;
// 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.*;
*/
/**
HistogramGenerator is a ChartGenerator which displays a histogram using the JFreeChart library.
The generator uses the HistoramDataset as its dataset, which holds histogram elements consisting of
a name, an array of doubles (the samples), and an integer (the number of bins).
representing a time series displayed on the chart. You add series to the generator with the <tt>addSeries</tt>
method.
<p>HistogramGenerator creates attributes components in the form of HistogramSeriesAttributes, which work with
the generator to properly update the chart to reflect changes the user has made to its display.
*/
public class HistogramGenerator extends XYChartGenerator
{
HistogramType histogramType = HistogramType.FREQUENCY;
public void removeSeries(int index)
{
super.removeSeries(index);
update();
}
public void moveSeries(int index, boolean up)
{
super.moveSeries(index, up);
update();
}
protected void buildChart()
{
HistogramDataset dataset = new HistogramDataset();
dataset.setType(HistogramType.FREQUENCY); // when buildChart() is called, histogramType hasn't been set yet.
chart = ChartFactory.createHistogram("Untitled Chart","Untitled X Axis","Untitled Y Axis",dataset,
PlotOrientation.VERTICAL, false, true, false);
chart.setAntiAlias(true);
//chartPanel = new ScrollableChartPanel(chart, true);
chartPanel = buildChartPanel(chart);
setChartPanel(chartPanel);
//chartHolder.getViewport().setView(chartPanel);
((XYBarRenderer)(chart.getXYPlot().getRenderer())).setShadowVisible(false);
((XYBarRenderer)(chart.getXYPlot().getRenderer())).setBarPainter(new StandardXYBarPainter());
// this must come last because the chart must exist for us to set its dataset
setSeriesDataset(dataset);
}
protected void update()
{
// We have to rebuild the dataset from scratch (deleting and replacing it) because JFreeChart's
// histogram facility doesn't have a way to remove or move elements. Stupid stupid stupid.
SeriesAttributes[] sa = getSeriesAttributes();
HistogramDataset dataset = new HistogramDataset();
dataset.setType(histogramType);
for(int i=0; i < sa.length; i++)
{
HistogramSeriesAttributes attributes = (HistogramSeriesAttributes)(sa[i]);
dataset.addSeries(new UniqueString(attributes.getSeriesName()), attributes.getValues(), attributes.getNumBins());
}
setSeriesDataset(dataset);
}
public HistogramGenerator()
{
// buildChart is called by super() first
LabelledList list = new LabelledList("Show Histogram...");
DisclosurePanel pan1 = new DisclosurePanel("Show Histogram...", list);
final HistogramType[] styles = new HistogramType[]
{ HistogramType.FREQUENCY, HistogramType.RELATIVE_FREQUENCY, HistogramType.SCALE_AREA_TO_1 };
final JComboBox style = new JComboBox(new String[] {"By Frequency", "By Relative Frequency", "With Area = 1.0"});
style.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
histogramType = styles[style.getSelectedIndex()];
HistogramDataset dataset = (HistogramDataset)(getSeriesDataset());
dataset.setType(histogramType);
}
});
list.add(style);
addGlobalAttribute(pan1);
}
/** Adds a series, plus a (possibly null) SeriesChangeListener which will receive a <i>single</i>
event if/when the series is deleted from the chart by the user. Returns the series attributes. */
public SeriesAttributes addSeries(double[] vals, int bins, String name, SeriesChangeListener stopper)
{
if (vals == null || vals.length == 0) vals = new double[] { 0 }; // ya gotta have at least one val
HistogramDataset dataset = (HistogramDataset)(getSeriesDataset());
int i = dataset.getSeriesCount();
dataset.setType(histogramType); // It looks like the histograms reset
dataset.addSeries(new UniqueString(name), vals, bins);
// need to have added the dataset BEFORE calling this since it'll try to change the name of the series
HistogramSeriesAttributes csa = new HistogramSeriesAttributes(this, name, i, vals, bins, stopper);
seriesAttributes.add(csa);
revalidate(); // display the new series panel
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;
if (vals == null || vals.length == 0) vals = new double[] { 0 }; // ya gotta have at least one val
HistogramSeriesAttributes hsa = (HistogramSeriesAttributes)(getSeriesAttribute(index));
hsa.setValues(vals);
}
public void setHistogramType(HistogramType type)
{
histogramType = type;
}
public HistogramType getHistogramType()
{
return histogramType;
}
}