/*
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 javax.swing.event.*;
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.*;
import org.jfree.chart.renderer.category.*;
/** A SeriesAttributes used for user control of BoxPlot series created with BoxPlotGenerator.
Unfortunately JFreeChart doesn't have nearly
as well-designed a BoxPlot chart facility as its time series charts. There is no BoxPlotSeries object to
encapsulate a series, and no facilities for deleting or moving series relative to one another.
*/
public class BoxPlotSeriesAttributes extends SeriesAttributes
{
double[][] values;
String[] labels = new String[] { "" };
public void setLabels(String[] labels)
{
if (labels != null) labels = (String[])(labels.clone());
else labels = new String[] { "" };
this.labels = labels;
}
public String[] getLabels() { return labels; }
// public void setLabel(String label) { this.labels = new String[] { label }; }
public double[][] getValues() { return values; }
public void setValues(double[][] vals)
{
if (vals != null)
{
vals = (double[][]) (vals.clone());
for(int i = 0; i < vals.length; i++)
vals[i] = (double[]) (vals[i].clone());
}
values = vals;
}
//public void setValues(double[] vals) { setValues(new double[][] { vals }); }
/** Border thickness */
float thickness;
NumberTextField thicknessField;
/** The color of the BoxPlot bar. */
Color fillColor;
ColorWell fillColorWell;
/** The color of the BoxPlot bar border. */
Color strokeColor;
ColorWell strokeColorWell;
/** The opacity of the BoxPlot 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 BoxPlot bar border. Sadly this must be separate than the color because
Sun doesn't have a proper color selector. */
double lineOpacity;
NumberTextField lineOpacityField;
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()); }
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 BoxPlotSeriesAttributes object with the given generator, series name, series index,
and desire to display margin options. */
public BoxPlotSeriesAttributes(ChartGenerator generator, String name, int index, double[][] values, String[] labels, SeriesChangeListener stoppable)
{
super(generator, name, index, stoppable);
setValues(values);
setLabels(labels);
super.setSeriesName(name); // just set the name, don't update
}
/** Produces a BoxPlotSeriesAttributes object with the given generator, series name, series index,
and desire to display margin options. */
// used privately by BoxPlotGenerator. Maybe we should simplify this
BoxPlotSeriesAttributes(ChartGenerator generator, String name, int index, double[][] values, SeriesChangeListener stoppable)
{
super(generator, name, index, stoppable);
setValues(values);
super.setSeriesName(name); // just set the name, don't update
}
/** Produces a BoxPlotSeriesAttributes object with the given generator, series name, series index,
and desire to display margin options. */
public BoxPlotSeriesAttributes(ChartGenerator generator, String name, int index, double[] values, SeriesChangeListener stoppable)
{
super(generator, name, index, stoppable);
setValues(new double[][]{values});
super.setSeriesName(name); // just set the name, don't update
}
/** It's very expensive to call this function (O(n)) because JFreeChart has no way of changing the
name of a BoxPlot 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
((BoxPlotGenerator)generator).update();
}
public void rebuildGraphicsDefinitions()
{
BoxAndWhiskerRenderer renderer = (BoxAndWhiskerRenderer)getCategoryRenderer();
renderer.setSeriesOutlineStroke(getSeriesIndex(),
new BasicStroke(thickness));
renderer.setSeriesStroke(getSeriesIndex(),
new BasicStroke(thickness));
renderer.setSeriesPaint(getSeriesIndex(),reviseColor(fillColor, fillOpacity));
renderer.setSeriesOutlinePaint(getSeriesIndex(),reviseColor(strokeColor, lineOpacity));
repaint();
}
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;
// NOTES:
// fillColor = (Color)(getCategoryRenderer().getSeriesPaint(getSeriesIndex()));
// this returns null, cause getSeriesPaint returns whatever was set through setSeriesPaint;
// for the default colors, you need "lookupSeriesPaint()".
// fillColor = (Color) (getCategoryRenderer().lookupSeriesPaint(getSeriesIndex()));
// getCategoryRenderer 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) (getCategoryRenderer().getItemPaint(getSeriesIndex(), -1));
// second argument does not matter
fillColor = (Color)(getCategoryRenderer().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);
}
public CategoryItemRenderer getCategoryRenderer()
{
return ((CategoryPlot)(getPlot())).getRenderer();
}
public void setPlotVisible(boolean val)
{
plotVisible = val;
getCategoryRenderer().setSeriesVisible(seriesIndex, Boolean.valueOf(val));
}
}