/*
* Author: tdanford
* Date: Sep 16, 2008
*/
package org.seqcode.viz.eye;
import java.awt.*;
import java.lang.reflect.*;
import java.util.*;
import org.seqcode.genome.location.ScoredPoint;
import org.seqcode.gseutils.Pair;
import org.seqcode.gseutils.models.Model;
import org.seqcode.gseutils.models.ModelFieldAnalysis;
import org.seqcode.viz.paintable.*;
public class ModelLocatedDifferenceValues extends AbstractModelPaintable {
public static final String boundsKey = "bounds";
public static final String scaleKey = "scale";
public static final String radiusKey = "radius";
public static final String y1ColorKey = "y1color";
public static final String y2ColorKey = "y2color";
public static final String strokeKey = "stroke";
public static final String axisColorKey = "axis-color";
private String xFieldName, y1FieldName, y2FieldName;
private Vector<DiffModel> points;
private Vector<Model> models;
public ModelLocatedDifferenceValues() {
xFieldName = "location";
y1FieldName = "value1";
y2FieldName = "value2";
points = new Vector<DiffModel>();
models = new Vector<Model>();
initProperty(new PropertyValueWrapper<Integer[]>(boundsKey, new Integer[] { 0, 1 }));
initProperty(new PropertyValueWrapper<PaintableScale>(scaleKey, new PaintableScale(0.0, 1.0)));
initProperty(new PropertyValueWrapper<Integer>(radiusKey, 3));
initProperty(new PropertyValueWrapper<Color>(y1ColorKey, Color.red));
initProperty(new PropertyValueWrapper<Color>(y2ColorKey, Color.green));
initProperty(new PropertyValueWrapper<Float>(strokeKey, (float)3.0));
startDrawingPoints();
}
public ModelLocatedDifferenceValues(String xfield, String y1, String y2) {
this();
xFieldName = xfield;
y1FieldName = y1;
y2FieldName = y2;
}
public void setBounds(int start, int end) {
PropertyValueWrapper<Integer[]> wrapper = getProperty(boundsKey);
wrapper.setValue(new Integer[] { start, end });
}
public void addModel(Model m) {
Class modelClass = m.getClass();
ModelFieldAnalysis analysis = new ModelFieldAnalysis(modelClass);
Field xfield = analysis.findField(xFieldName);
Field y1field = analysis.findField(y1FieldName);
Field y2field = analysis.findField(y2FieldName);
if(xfield != null && y1field != null && y2field != null) {
try {
Object xvalue = xfield.get(m);
Object y1 = y1field.get(m);
Object y2 = y2field.get(m);
if(xvalue != null && y1 != null && y2 != null) {
Class xclass = xvalue.getClass();
Class y1class = y1.getClass();
Class y2class = y2.getClass();
if(!Model.isSubclass(xclass, Integer.class)) {
throw new IllegalArgumentException("Location value must be an Integer");
}
if(!Model.isSubclass(y1class, Number.class)) {
throw new IllegalArgumentException("Value must be a Number");
}
if(!Model.isSubclass(y2class, Number.class)) {
throw new IllegalArgumentException("Value must be a Number");
}
Integer xnumber = (Integer)xvalue;
Number y1num = (Number)y1;
Number y2num = (Number)y2;
int x = xnumber.intValue();
double v1 = y1num.doubleValue();
double v2 = y2num.doubleValue();
models.add(m);
addLocatedValue(x, v1, v2, m);
} else {
throw new IllegalArgumentException("location or value was null");
}
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("location or value field was inaccessible", e);
}
} else {
String msg = "No Fields:";
if(xfield == null) {
msg += String.format(" %s", xFieldName);
}
if(y1field == null) {
msg += String.format(" %s", y1FieldName);
}
if(y2field == null) {
msg += String.format(" %s", y2FieldName);
}
throw new IllegalArgumentException(msg);
}
}
private void addLocatedValue(int x, double y1, double y2, Model m) {
PaintableScale yScale = getPropertyValue(scaleKey);
ModelPaintableProperty boundsProp = getProperty(boundsKey);
Integer[] bounds = (Integer[])boundsProp.getValue();
points.add(new DiffModel(x, y1, y2, m));
if(x < bounds[0] || x > bounds[1]) {
bounds[0] = Math.min(x, bounds[0]);
bounds[1] = Math.max(x, bounds[1]);
setProperty(new PropertyValueWrapper<Integer[]>(boundsKey, bounds));
}
yScale.updateScale(y1);
yScale.updateScale(y2);
dispatchChangedEvent();
}
public void addModels(Iterator<? extends Model> itr) {
while(itr.hasNext()) {
addModel(itr.next());
}
}
public void clearModels() {
points.clear();
models.clear();
dispatchChangedEvent();
}
public void paintItem(Graphics g, int x1, int y1, int x2, int y2) {
Integer[] bounds = getPropertyValue(boundsKey);
PaintableScale yScale = getPropertyValue(scaleKey);
int radius = getPropertyValue(radiusKey, 3);
Color y1color = getPropertyValue(y1ColorKey, Color.red);
Color y2color = getPropertyValue(y2ColorKey, Color.green);
float stroke = getPropertyValue(strokeKey, (float)1.0);
int strokeWidth = Math.max(1, (int)Math.floor(stroke));
Color axisColor = getPropertyValue(axisColorKey, Color.black);
int length = Math.max(1, bounds[1] - bounds[0]);
int w = x2-x1, h = y2-y1;
int numPoints = 0;
for(DiffModel p : points) {
if(p.location >= bounds[0] && p.location <= bounds[1]) {
numPoints += 1;
}
}
// Basically, we don't want to draw the lines so thick that they overlap too much.
int maxRadius = Math.max(1, (int)Math.floor((double)w / (double)Math.max(1, numPoints/2)));
radius = Math.min(radius, maxRadius);
int diam = radius*2;
Graphics2D g2 = (Graphics2D)g;
Stroke oldStroke = g2.getStroke();
g2.setStroke(new BasicStroke(stroke));
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
clearDrawnPoints();
/** Painting Code **/
// Axes
g2.setColor(axisColor);
g2.drawRect(x1, y1, w-1, h-1);
int wArea = w - strokeWidth*2;
// Points
for(DiffModel p : points) {
int location = p.location;
//System.out.println(String.format("=> %d, %.2f", location, value));
double v1 = p.value1, v2 = p.value2;
Color color = p.diff >= 0.0 ? y1color : y2color;
double xf = (double)(location-bounds[0]) / (double)length;
double y1f = yScale.fractionalOffset(v1);
double y2f = yScale.fractionalOffset(v2);
int px = x1 + (int)Math.round(xf * (double)wArea) + strokeWidth;
int py1 = y2 - (int)Math.round(y1f * (double)h);
int py2 = y2 - (int)Math.round(y2f * (double)h);
g2.setColor(color);
//g2.drawLine(px, py1, px, py2);
g2.fillRoundRect(px-radius, Math.min(py1, py2)-radius, diam, Math.abs(py2-py1)+diam, diam, diam);
//g2.setColor(Color.lightGray);
//g2.drawOval(px-radius, py1-radius, diam, diam);
//g2.drawOval(px-radius, py2-radius, diam, diam);
/*
g2.setColor(Color.white);
g2.fillOval(px-radius, py-radius, diam, diam);
g2.setColor(color);
g2.drawOval(px-radius, py-radius, diam, diam);
*/
if(p.model != null) {
drawPoint(new Point(px, Math.min(py1, py2)), p.model);
}
}
g2.setStroke(oldStroke);
}
public static class DiffModel extends Model {
public Integer location;
public Double value1, value2, diff;
public Model model;
public DiffModel(int l, double v1, double v2, Model m) {
location = l;
value1 = v1; value2 = v2;
diff = value1 - value2;
model = m;
}
}
}