package prefuse.action.layout;
import java.awt.geom.Rectangle2D;
import java.util.Arrays;
import java.util.Iterator;
import prefuse.data.Table;
import prefuse.data.query.NumberRangeModel;
import prefuse.util.ArrayLib;
import prefuse.util.MathLib;
import prefuse.util.PrefuseLib;
import prefuse.util.ui.ValuedRangeModel;
import prefuse.visual.VisualItem;
/**
* Layout Action that computes a stacked area chart, in which a series of
* data values are consecutively stacked on top of each other.
*
* @author jeffrey heer
*/
public class StackedAreaChart extends Layout {
private String m_field;
private String m_start;
private String m_end;
private String[] columns;
private double[] baseline;
private float[] poly;
private double m_padding = 0.05;
private float m_threshold;
private Rectangle2D bounds;
private NumberRangeModel m_model;
/**
* Create a new StackedAreaChart.
* @param group the data group to layout
* @param field the data field in which to store computed polygons
* @param columns the various data fields, in sorted order, that
* should be referenced for each consecutive point of a stack layer
*/
public StackedAreaChart(String group, String field, String[] columns) {
this(group, field, columns, 1.0);
}
/**
* Create a new StackedAreaChart.
* @param group the data group to layout
* @param field the data field in which to store computed polygons
* @param columns the various data fields, in sorted order, that
* should be referenced for each consecutive point of a stack layer
* @param threshold height threshold under which stacks should not
* be made visible.
*/
public StackedAreaChart(String group, String field, String[] columns,
double threshold)
{
super(group);
this.columns = columns;
baseline = new double[columns.length];
poly = new float[4*columns.length];
m_field = field;
m_start = PrefuseLib.getStartField(field);
m_end = PrefuseLib.getEndField(field);
setThreshold(threshold);
m_model = new NumberRangeModel(0,1,0,1);
}
// ------------------------------------------------------------------------
/**
* Gets the percentage of the layout bounds that should be reserved for
* empty space at the top of the stack.
* @return the padding percentage
*/
public double getPaddingPercentage() {
return m_padding;
}
/**
* Sets the percentage of the layout bounds that should be reserved for
* empty space at the top of the stack.
* @param p the padding percentage to use
*/
public void setPaddingPercentage(double p) {
if ( p < 0 || p > 1 )
throw new IllegalArgumentException(
"Illegal padding percentage: " + p);
m_padding = p;
}
/**
* Get the minimum height threshold under which stacks should not be
* made visible.
* @return the minimum height threshold for visibility
*/
public double getThreshold() {
return m_threshold;
}
/**
* Set the minimum height threshold under which stacks should not be
* made visible.
* @param threshold the minimum height threshold for visibility to use
*/
public void setThreshold(double threshold) {
m_threshold = (float)threshold;
}
/**
* Get the range model describing the range occupied by the value
* stack.
* @return the stack range model
*/
public ValuedRangeModel getRangeModel() {
return m_model;
}
// TODO: support externally driven range specification (i.e. stack zooming)
// public void setRangeModel(NumberRangeModel model) {
// m_model = model;
// }
// ------------------------------------------------------------------------
/**
* @see prefuse.action.Action#run(double)
*/
public void run(double frac) {
Arrays.fill(baseline, 0);
bounds = getLayoutBounds();
float inc = (float) (bounds.getMaxX()-bounds.getMinX())
/ (columns.length-1);
int len = columns.length;
// first walk
Iterator iter = m_vis.visibleItems(m_group);
while ( iter.hasNext() ) {
VisualItem item = (VisualItem)iter.next();
for ( int i=0; i= 0; ) {
poly[2*(len-1-i)] = (float)bounds.getMinX() + i*inc;
poly[2*(len-1-i)+1] = (float)baseline[i];
}
for ( int i=0; i