/*
* 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 ModelLocatedValues extends AbstractModelPaintable {
public static final String boundsKey = "bounds";
public static final String scaleKey = "scale";
public static final String radiusKey = "radius";
public static final String colorKey = "color";
public static final String strokeKey = "stroke";
public static final String axisColorKey = "axis-color";
public static final String stemKey = "stems";
public static final String connectedKey = "connected?";
private String xFieldName, yFieldName;
private Vector<PointModel> points;
private Vector<Model> models;
public ModelLocatedValues() {
xFieldName = "location";
yFieldName = "value";
points = new Vector<PointModel>();
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>(colorKey, Color.red));
initProperty(new PropertyValueWrapper<Float>(strokeKey, (float)2.0));
initProperty(new PropertyValueWrapper<Boolean>(stemKey, (Boolean)true));
initProperty(new PropertyValueWrapper<Boolean>(connectedKey, (Boolean)false));
startDrawingPoints();
}
public ModelLocatedValues(String xfield, String yfield) {
this();
xFieldName = xfield;
yFieldName = yfield;
}
public void setBounds(int start, int end) {
PropertyValueWrapper<Integer[]> wrapper = getProperty(boundsKey);
wrapper.setValue(new Integer[] { start, end });
}
public void addValue(Object value) {
if (value instanceof ScoredPoint) {
ScoredPoint p = (ScoredPoint)value;
int location = p.getLocation();
double val = p.getScore();
Color c = getPropertyValue(colorKey);
models.add(new PointModel(location, val, null, c));
addLocatedValue(location, val, null, c);
} else {
super.addValue(value);
}
}
public void addModel(Model m) {
Class modelClass = m.getClass();
ModelFieldAnalysis analysis = new ModelFieldAnalysis(modelClass);
Field xfield = analysis.findField(xFieldName);
Field yfield = analysis.findField(yFieldName);
if(xfield != null && yfield != null) {
try {
Object xvalue = xfield.get(m);
Object yvalue = yfield.get(m);
if(xvalue != null && yvalue != null) {
Class xclass = xvalue.getClass();
Class yclass = yvalue.getClass();
if(!Model.isSubclass(xclass, Integer.class)) {
throw new IllegalArgumentException("Location value must be an Integer");
}
if(!Model.isSubclass(yclass, Number.class)) {
throw new IllegalArgumentException("Value must be a Number");
}
Integer xnumber = (Integer)xvalue;
Number ynumber = (Number)yvalue;
int x = xnumber.intValue();
double y = ynumber.doubleValue();
Color color = getPropertyValue(colorKey);
Field colorfield = analysis.findField("color");
if(colorfield != null && Model.isSubclass(colorfield.getType(), Color.class)) {
Color c = (Color)colorfield.get(m);
if(c != null) {
color = c;
}
}
models.add(m);
addLocatedValue(x, y, m, color);
} 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(yfield == null) {
msg += String.format(" %s", yFieldName);
}
throw new IllegalArgumentException(msg);
}
}
private void addLocatedValue(int x, double y, Model m, Color c) {
PaintableScale yScale = getPropertyValue(scaleKey);
ModelPaintableProperty boundsProp = getProperty(boundsKey);
Integer[] bounds = (Integer[])boundsProp.getValue();
PointModel pm = new PointModel(x, y, m, c);
points.add(pm);
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(y);
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 color = getPropertyValue(colorKey, Color.red);
float stroke = getPropertyValue(strokeKey, (float)1.0);
int strokeWidth = Math.max(1, (int)Math.floor(stroke));
Color axisColor = getPropertyValue(axisColorKey, Color.black);
Boolean stems = getPropertyValue(stemKey, true);
Boolean connected = getPropertyValue(connectedKey, true);
int diam = radius*2;
int length = Math.max(1, bounds[1] - bounds[0]);
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);
clearDrawnPoints();
/** Painting Code **/
// Axes
//g2.setColor(axisColor);
//g2.drawRect(x1, y1, w-1, h-1);
int wArea = w - strokeWidth*2;
if(connected) {
int ppx = -1, ppy = -1;
g2.setColor(color);
for(PointModel p : points) {
int location = p.location;
double value = p.value;
double xf = (double)(location-bounds[0]) / (double)length;
double yf = yScale.fractionalOffset(value);
int px = x1 + (int)Math.round(xf * (double)wArea) + strokeWidth;
int py = y2 - (int)Math.round(yf * (double)h);
if(ppx != -1 && ppy != -1) {
g.setColor(p.color);
g.drawLine(ppx, ppy, px, py);
}
ppx = px; ppy = py;
}
}
// Points
for(PointModel p : points) {
int location = p.location;
double value = p.value;
//System.out.println(String.format("=> %d, %.2f", location, value));
double xf = (double)(location-bounds[0]) / (double)length;
double yf = yScale.fractionalOffset(value);
boolean outOfBounds = value < yScale.getMin() || value > yScale.getMax();
int px = x1 + (int)Math.round(xf * (double)wArea) + strokeWidth;
int py = y2 - (int)Math.round(yf * (double)h);
if(!outOfBounds) {
g2.setColor(p.color);
if(stems) {
g2.drawLine(px, py, px, y2);
}
//g2.setColor(Color.white);
//g2.fillOval(px-radius, py-radius, diam, diam);
g2.setColor(p.color);
g2.drawOval(px-radius, py-radius, diam, diam);
if(p.model != null) {
drawPoint(new Point(px, py), p.model);
}
}
}
g2.setStroke(oldStroke);
}
public static class PointModel extends Model {
public Integer location;
public Double value;
public Model model;
public Color color;
public PointModel(int l, double v, Model m, Color c) {
location = l; value = v;
model = m;
color = c;
}
}
}