/* 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 javax.swing.*; import javax.swing.border.*; import java.awt.event.*; import java.awt.geom.*; import java.util.*; import sim.util.gui.*; // From JFreeChart import org.jfree.data.xy.*; import org.jfree.chart.*; import org.jfree.chart.event.*; import org.jfree.chart.plot.*; import org.jfree.data.general.*; import org.jfree.chart.renderer.xy.*; import org.jfree.data.general.*; /** A SeriesAttributes used for user control of histogram series created with HistogramGenerator. Unfortunately JFreeChart doesn't have nearly as well-designed a histogram chart facility as its time series charts. There is no HistogramSeries object to encapsulate a series, and no facilities for deleting or moving series relative to one another. */ public class HistogramSeriesAttributes extends SeriesAttributes { double[] values; public double[] getValues() { return values; } public void setValues(double[] vals) { if (vals != null) vals = (double[])(vals.clone()); values = vals; } /** Border thickness */ float thickness; NumberTextField thicknessField; /** The color of the histogram bar. */ Color fillColor; ColorWell fillColorWell; /** The color of the histogram bar border. */ Color strokeColor; ColorWell strokeColorWell; /** The opacity of the histogram bar. Sadly this must be separate than the color because Sun doesn't have a proper color selector. */ double fillOpacity; NumberTextField fillOpacityField; /** The opacity of the histogram bar border. Sadly this must be separate than the color because Sun doesn't have a proper color selector. */ double lineOpacity; NumberTextField lineOpacityField; NumberTextField numBinsField; public void setFillOpacity(double value) { fillOpacityField.setValue(fillOpacityField.newValue(value)); } public double getFillOpacity() { return fillOpacityField.getValue(); } public void setStrokeOpacity(double value) { lineOpacityField.setValue(lineOpacityField.newValue(value)); } public double getStrokeOpacity() { return lineOpacityField.getValue(); } public void setThickness(double value) { thicknessField.setValue(thicknessField.newValue(value)); } public double getThickness() { return (double)(thicknessField.getValue()); } // we need to store numBins here so update() gets it properly, see later int numBins; public void setNumBins(int value) { numBinsField.setValue(numBinsField.newValue(value)); numBins = (int)(numBinsField.getValue()); } public int getNumBins() { return numBins; } public void setFillColor(Color value) { fillColorWell.setColor(fillColor = value); } public Color getFillColor() { return fillColor; } public void setStrokeColor(Color value) { strokeColorWell.setColor(strokeColor = value); } public Color getStrokeColor() { return strokeColor; } /** Produces a HistogramSeriesAttributes object with the given generator, series name, series index, and desire to display margin options. */ public HistogramSeriesAttributes(ChartGenerator generator, String name, int index, double[] values, int bins, SeriesChangeListener stoppable) // , boolean includeMargin) { super(generator, name, index, stoppable); setValues(values); super.setSeriesName(name); // just set the name, don't update numBins = bins; numBinsField.setValue(bins); // otherwise it'd call newValue(), which would in turn try to update the series, which hasn't been made yet numBinsField.setInitialValue(bins); // make this the default } /** It's very expensive to call this function (O(n)) because JFreeChart has no way of changing the name of a histogram dataset series, and so we must rebuild all of it from scratch. */ public void setSeriesName(String val) { super.setSeriesName(val); // call this first to set it ((HistogramGenerator)generator).update(); } public void rebuildGraphicsDefinitions() { XYBarRenderer renderer = (XYBarRenderer)(((XYPlot)getPlot()).getRenderer()); if (thickness == 0.0) renderer.setDrawBarOutline(false); else { renderer.setSeriesOutlineStroke(getSeriesIndex(), new BasicStroke(thickness)); renderer.setDrawBarOutline(true); } renderer.setSeriesPaint(getSeriesIndex(),reviseColor(fillColor, fillOpacity)); renderer.setSeriesOutlinePaint(getSeriesIndex(),reviseColor(strokeColor, lineOpacity)); repaint(); } static final int DEFAULT_BINS = 8; // to have enough space -- it'll get changed soon public void buildAttributes() { // The following three variables aren't defined until AFTER construction if // you just define them above. So we define them below here instead. thickness = 2.0f; fillOpacity = 1.0; lineOpacity = 1.0; numBinsField = new NumberTextField("", DEFAULT_BINS ,true) { public double newValue(double newValue) { newValue = (int)newValue; if (newValue < 1) newValue = currentValue; // we now set the number of bins directly so update() gets it when it calls getNumBins() numBins = (int)newValue; ((HistogramGenerator)generator).update(); return newValue; } }; addLabelled("Bins",numBinsField); // NOTES: // fillColor = (Color)((((XYPlot)getPlot()).getRenderer()).getSeriesPaint(getSeriesIndex())); // this returns null, cause getSeriesPaint returns whatever was set through setSeriesPaint; // for the default colors, you need "lookupSeriesPaint()". // fillColor = (Color) ((((XYPlot)getPlot()).getRenderer()).lookupSeriesPaint(getSeriesIndex())); // getRenderer returns an object implementing the XYItemRenderer interface. // either you cast that object to AbstractRenderer, and call lookupSeriesPaint() // or you call getItemPaint() on it directly; all getItemPaint does is call lookupSeriesPaint(), // but that looks bad, cause getItemPaint() seems to be meant for category data). // On the other hand, lookupSeriesPaint() does not show up before 1.0.6, so // in the interest of backward compatibility: fillColor = (Color) ((((XYPlot)getPlot()).getRenderer()).getItemPaint(getSeriesIndex(), -1)); // second argument does not matter fillColor = (Color)((((XYPlot)getPlot()).getRenderer()).getSeriesPaint(getSeriesIndex())); fillColorWell = new ColorWell(fillColor) { public Color changeColor(Color c) { fillColor = c; rebuildGraphicsDefinitions(); return c; } }; addLabelled("Fill",fillColorWell); fillOpacityField = new NumberTextField("Opacity ", fillOpacity,1.0,0.125) { public double newValue(double newValue) { if (newValue < 0.0 || newValue > 1.0) newValue = currentValue; fillOpacity = (float)newValue; rebuildGraphicsDefinitions(); return newValue; } }; addLabelled("",fillOpacityField); strokeColor = Color.black; strokeColorWell = new ColorWell(strokeColor) { public Color changeColor(Color c) { strokeColor = c; rebuildGraphicsDefinitions(); return c; } }; addLabelled("Line",strokeColorWell); lineOpacityField = new NumberTextField("Opacity ", lineOpacity,1.0,0.125) { public double newValue(double newValue) { if (newValue < 0.0 || newValue > 1.0) newValue = currentValue; lineOpacity = (float)newValue; rebuildGraphicsDefinitions(); return newValue; } }; addLabelled("",lineOpacityField); thicknessField = new NumberTextField("Width ", thickness,false) { public double newValue(double newValue) { if (newValue < 0.0) newValue = currentValue; thickness = (float)newValue; rebuildGraphicsDefinitions(); return newValue; } }; addLabelled("",thicknessField); } }