/* * Author: tdanford * Date: Sep 16, 2008 */ package org.seqcode.viz.eye; import java.awt.*; import java.lang.reflect.*; import java.util.*; import org.seqcode.gseutils.models.Model; import org.seqcode.gseutils.models.ModelFieldAnalysis; import org.seqcode.viz.paintable.*; public class ModelHistogram extends AbstractModelPaintable { public static final String xScaleKey = "x-scale"; public static final String yScaleKey = "y-scale"; public static final String colorKey = "color"; public static final String strokeKey = "stroke"; public static final String axisColorKey = "axis-color"; public static final String barOutlineColorKey = "bar-outline-color"; public static final String binsKey = "bins"; private String valueFieldName; private int maxBin; private Vector<Double> points; private Vector<Integer[]> binCounts; private Vector<Double[]> binBounds; private Vector<Model> models; public ModelHistogram() { valueFieldName = "value"; points = new Vector<Double>(); models = new Vector<Model>(); binCounts = new Vector<Integer[]>(); binBounds = new Vector<Double[]>(); maxBin = 0; initProperty(new PropertyValueWrapper<Integer>(binsKey, 10)); initProperty(new PropertyValueWrapper<PaintableScale>(xScaleKey, new PaintableScale(0.0, 1.0))); initProperty(new PropertyValueWrapper<PaintableScale>(yScaleKey, new PaintableScale(0.0, 1.0))); initProperty(new PropertyValueWrapper<Color>(colorKey, Color.red)); initProperty(new PropertyValueWrapper<Float>(strokeKey, (float)3.0)); rebin(); } public ModelHistogram(String vfield) { this(); valueFieldName = vfield; } public void rebin() { PaintableScale scale = getPropertyValue(xScaleKey); Integer bins = getPropertyValue(binsKey); System.out.println(String.format("Rebinning: %d, %.3f-%.3f", bins, scale.getMin(), scale.getMax())); maxBin = 0; binCounts.clear(); binBounds.clear(); double lower = scale.getMin(), upper = scale.getMax(); double binWidth = (upper-lower)/(double)bins; double start = lower, end = start+binWidth; for(int i = 0; i < bins; i++, start += binWidth, end += binWidth) { binBounds.add(new Double[] { start, end }); System.out.println(String.format("\t%.3f-%.3f", start, end)); binCounts.add(new Integer[] { 0 }); } for(Double p : points) { binPoint(p); } PaintableScale yscale = getPropertyValue(yScaleKey); //yscale.updateScale((double)maxBin); yscale.setScale(0.0, (double)maxBin); } private void binPoint(double value) { PaintableScale scale = getPropertyValue(xScaleKey); Integer bins = getPropertyValue(binsKey); double lower = scale.getMin(), upper = scale.getMax(); scale.updateScale(value); if(scale.getMin() < lower || scale.getMax() > upper) { rebin(); lower = scale.getMin(); upper = scale.getMax(); } double binWidth = (upper-lower)/(double)bins; int bin = Math.min((int)Math.floor((value-lower) / binWidth), bins-1); int count = (binCounts.get(bin)[0] += 1); maxBin = Math.max(maxBin, count); } public void addModel(Model m) { Class modelClass = m.getClass(); ModelFieldAnalysis analysis = new ModelFieldAnalysis(modelClass); Field vfield = analysis.findField(valueFieldName); if(vfield != null) { try { Object vvalue = vfield.get(m); if(vvalue != null) { Class vclass = vvalue.getClass(); if(!Model.isSubclass(vclass, Number.class)) { throw new IllegalArgumentException("Value must be a Number"); } Number vnumber = (Number)vvalue; double value = vnumber.doubleValue(); PaintableScale xScale = getPropertyValue(xScaleKey); xScale.updateScale(value); points.add(value); models.add(m); binPoint(value); dispatchChangedEvent(); } else { throw new IllegalArgumentException("Value was null"); } } catch (IllegalAccessException e) { throw new IllegalArgumentException("Value field was inaccessible", e); } } else { String msg = String.format("No Fields %s", valueFieldName); throw new IllegalArgumentException(msg); } } public void addModels(Iterator<? extends Model> itr) { while(itr.hasNext()) { addModel(itr.next()); } } public ModelPaintable setProperty(ModelPaintableProperty p) { super.setProperty(p); if(p.getKey().equals(binsKey)) { rebin(); dispatchChangedEvent(); } else if (p.getKey().equals(xScaleKey)) { rebin(); dispatchChangedEvent(); } return this; } public void clearModels() { points.clear(); models.clear(); // this will automatically call rebin() -- see the setProperty method, above. setProperty(new PropertyValueWrapper<PaintableScale>(xScaleKey, new PaintableScale(0.0, 1.0))); setProperty(new PropertyValueWrapper<PaintableScale>(yScaleKey, new PaintableScale(0.0, 1.0))); dispatchChangedEvent(); } public void paintItem(Graphics g, int x1, int y1, int x2, int y2) { PaintableScale xScale = getPropertyValue(xScaleKey); PaintableScale yScale = getPropertyValue(yScaleKey); Color barColor = getPropertyValue(colorKey, Color.red); Color barOutlineColor = getPropertyValue(barOutlineColorKey, Color.white); float stroke = getPropertyValue(strokeKey, (float)1.0); Color axisColor = getPropertyValue(axisColorKey, Color.black); int w = x2-x1, h = y2-y1; Graphics2D g2 = (Graphics2D)g; Stroke oldStroke = g2.getStroke(); g2.setStroke(new BasicStroke(stroke)); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); /** Painting Code **/ int strokeInt = Math.max(1, (int)Math.floor(stroke)); HorizontalScalePainter hsp = new HorizontalScalePainter(xScale); // Axes g2.setColor(axisColor); g2.drawRect(x1, y1, w-1, h-1); for(int i = 0; i < binCounts.size(); i++) { Double[] bounds = binBounds.get(i); int count = binCounts.get(i)[0]; if(count > 0) { // if count > 0, then maxBin > 0 //double countf = (double)count / (double)maxBin; double countf = yScale.fractionalOffset((double)count); int yheight = (int)Math.round(countf * (double)(h-strokeInt*2)); double bx1f = xScale.fractionalOffset(bounds[0]); double bx2f = xScale.fractionalOffset(bounds[1]); int bx1 = x1 + (int)Math.round(bx1f * (double)(w-strokeInt*2)) + strokeInt + 1; int bx2 = x1 + (int)Math.round(bx2f * (double)(w-strokeInt*2)) + strokeInt; if(barOutlineColor != null) { g.setColor(barOutlineColor); g.fillRect(bx1, y2-yheight-strokeInt, bx2-bx1, yheight); } g.setColor(barColor); g.fillRect(bx1+1, y2-yheight-strokeInt+1, bx2-bx1, yheight); } } hsp.paintItem(g, x1, y2-10, x2, y2); /** End Painting Code **/ g2.setStroke(oldStroke); } }