1 package prefuse.action.layout; 2 3 import java.awt.geom.Rectangle2D ; 4 import java.util.Arrays ; 5 import java.util.Iterator ; 6 7 import prefuse.Constants; 8 import prefuse.data.Table; 9 import prefuse.data.query.NumberRangeModel; 10 import prefuse.util.ArrayLib; 11 import prefuse.util.MathLib; 12 import prefuse.util.PrefuseLib; 13 import prefuse.util.ui.ValuedRangeModel; 14 import prefuse.visual.VisualItem; 15 16 22 public class StackedAreaChart extends Layout { 23 24 private String m_field; 25 private String m_start; 26 private String m_end; 27 28 private String [] columns; 29 private double[] baseline; 30 private double[] peaks; 31 private float[] poly; 32 private double m_padding = 0.05; 33 private float m_threshold; 34 private Rectangle2D bounds; 35 36 private int m_orientation = Constants.ORIENT_BOTTOM_TOP; 37 private boolean m_horiz = false; 38 private boolean m_top = false; 39 40 private boolean m_norm = false; 41 private NumberRangeModel m_model; 42 43 50 public StackedAreaChart(String group, String field, String [] columns) { 51 this(group, field, columns, 1.0); 52 } 53 54 63 public StackedAreaChart(String group, String field, String [] columns, 64 double threshold) 65 { 66 super(group); 67 this.columns = columns; 68 baseline = new double[columns.length]; 69 peaks = new double[columns.length]; 70 poly = new float[4*columns.length]; 71 72 m_field = field; 73 m_start = PrefuseLib.getStartField(field); 74 m_end = PrefuseLib.getEndField(field); 75 setThreshold(threshold); 76 77 m_model = new NumberRangeModel(0,1,0,1); 78 } 79 80 82 87 public void setColumns(String [] cols) { 88 columns = cols; 89 } 90 91 96 public void setNormalized(boolean b) { 97 m_norm = b; 98 } 99 100 105 public boolean isNormalized() { 106 return m_norm; 107 } 108 109 114 public double getPaddingPercentage() { 115 return m_padding; 116 } 117 118 123 public void setPaddingPercentage(double p) { 124 if ( p < 0 || p > 1 ) 125 throw new IllegalArgumentException ( 126 "Illegal padding percentage: " + p); 127 m_padding = p; 128 } 129 130 135 public double getThreshold() { 136 return m_threshold; 137 } 138 139 144 public void setThreshold(double threshold) { 145 m_threshold = (float)threshold; 146 } 147 148 153 public ValuedRangeModel getRangeModel() { 154 return m_model; 155 } 156 157 165 public int getOrientation() { 166 return m_orientation; 167 } 168 169 179 public void setOrientation(int orient) { 180 if ( orient != Constants.ORIENT_TOP_BOTTOM && 181 orient != Constants.ORIENT_BOTTOM_TOP && 182 orient != Constants.ORIENT_LEFT_RIGHT && 183 orient != Constants.ORIENT_RIGHT_LEFT) { 184 throw new IllegalArgumentException ( 185 "Invalid orientation value: "+orient); 186 } 187 m_orientation = orient; 188 m_horiz = (m_orientation == Constants.ORIENT_LEFT_RIGHT || 189 m_orientation == Constants.ORIENT_RIGHT_LEFT); 190 m_top = (m_orientation == Constants.ORIENT_TOP_BOTTOM || 191 m_orientation == Constants.ORIENT_LEFT_RIGHT); 192 } 193 194 199 201 204 public void run(double frac) { 205 bounds = getLayoutBounds(); 206 Arrays.fill(baseline, 0); 207 208 float min = (float)(m_horiz?bounds.getMaxY() :bounds.getMinX()); 210 float hgt = (float)(m_horiz?bounds.getWidth():bounds.getHeight()); 211 int xbias = (m_horiz ? 1 : 0); 212 int ybias = (m_horiz ? 0 : 1); 213 int mult = m_top ? 1 : -1; 214 float inc = (float) (m_horiz ? (bounds.getMinY()-bounds.getMaxY()) 215 : (bounds.getMaxX()-bounds.getMinX())); 216 inc /= columns.length-1; 217 int len = columns.length; 218 219 double maxValue = getPeaks(); 221 float b = (float)(m_horiz ? (m_top?bounds.getMinX():bounds.getMaxX()) 222 : (m_top?bounds.getMinY():bounds.getMaxY())); 223 Arrays.fill(baseline, b); 224 225 m_model.setValueRange(0, maxValue, 0, maxValue); 226 227 Table t = (Table)m_vis.getGroup(m_group); 229 Iterator iter = t.tuplesReversed(); 230 while ( iter.hasNext() ) { 231 VisualItem item = (VisualItem)iter.next(); 232 if ( !item.isVisible() ) continue; 233 234 float height = 0; 235 236 for ( int i=len; --i >= 0; ) { 237 poly[2*(len-1-i)+xbias] = min + i*inc; 238 poly[2*(len-1-i)+ybias] = (float)baseline[i]; 239 } 240 for ( int i=0; i<columns.length; ++i ) { 241 int base = 2*(len+i); 242 double value = item.getDouble(columns[i]); 243 baseline[i] += mult * hgt * 244 MathLib.linearInterp(value,0,peaks[i]); 245 poly[base+xbias] = min + i*inc; 246 poly[base+ybias] = (float)baseline[i]; 247 height = Math.max(height, 248 Math.abs(poly[2*(len-1-i)+ybias]-poly[base+ybias])); 249 } 250 if ( height < m_threshold ) { 251 item.setVisible(false); 252 } 253 254 setX(item, null, 0); 255 setY(item, null, 0); 256 setPolygon(item, poly); 257 } 258 } 259 260 private double getPeaks() { 261 double sum = 0; 262 263 Arrays.fill(peaks, 0); 265 Iterator iter = m_vis.visibleItems(m_group); 266 while ( iter.hasNext() ) { 267 VisualItem item = (VisualItem)iter.next(); 268 for ( int i=0; i<columns.length; ++i ) { 269 double val = item.getDouble(columns[i]); 270 peaks[i] += val; 271 sum += val; 272 } 273 } 274 double max = ArrayLib.max(peaks); 275 276 if ( !m_norm ) { 278 Arrays.fill(peaks, max); 279 } 280 281 if ( !m_norm ) { 283 for ( int i=0; i<peaks.length; ++i ) { 284 peaks[i] += m_padding * peaks[i]; 285 } 286 max += m_padding*max; 287 } 288 289 if ( m_norm ) { 291 max = 1.0; 292 } 293 if ( Double.isNaN(max) ) 294 max = 0; 295 return max; 296 } 297 298 301 private void setPolygon(VisualItem item, float[] poly) { 302 float[] a = getPolygon(item, m_field); 303 float[] s = getPolygon(item, m_start); 304 float[] e = getPolygon(item, m_end); 305 System.arraycopy(a, 0, s, 0, a.length); 306 System.arraycopy(poly, 0, a, 0, poly.length); 307 System.arraycopy(poly, 0, e, 0, poly.length); 308 item.setValidated(false); 309 } 310 311 314 private float[] getPolygon(VisualItem item, String field) { 315 float[] poly = (float[])item.get(field); 316 if ( poly == null || poly.length < 4*columns.length ) { 317 int len = columns.length; 319 float inc = (float) (m_horiz?(bounds.getMinY()-bounds.getMaxY()) 320 :(bounds.getMaxX()-bounds.getMinX())); 321 inc /= len-1; 322 float max = (float) 323 (m_horiz ? (m_top?bounds.getMaxX():bounds.getMinX()) 324 : (m_top?bounds.getMinY():bounds.getMaxY())); 325 float min = (float)(m_horiz?bounds.getMaxY():bounds.getMinX()); 326 int bias = (m_horiz ? 1 : 0); 327 328 poly = new float[4*len]; 330 Arrays.fill(poly, max); 331 for ( int i=0; i<len; ++i ) { 332 float x = i*inc + min; 333 poly[2*(len+i) +bias] = x; 334 poly[2*(len-1-i)+bias] = x; 335 } 336 item.set(field, poly); 337 } 338 return poly; 339 } 340 341 } | Popular Tags |