/* * Author: tdanford * Date: Sep 16, 2008 */ package org.seqcode.viz.eye; import java.awt.*; import java.lang.reflect.*; import java.util.*; import javax.swing.*; import org.seqcode.gseutils.Pair; import org.seqcode.gseutils.models.Model; import org.seqcode.gseutils.models.ModelFieldAnalysis; import org.seqcode.viz.paintable.*; import java.awt.*; import java.awt.event.*; public class ModelLineGraph extends AbstractModelPaintable { public static final String xScaleKey = "x-scale"; public static final String yScaleKey = "y-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"; private String xFieldName, yFieldName; private Map<Double,LinePoint> points; private Map<Double,Model> models; public ModelLineGraph() { xFieldName = "x"; yFieldName = "y"; points = new TreeMap<Double,LinePoint>(); models = new TreeMap<Double,Model>(); 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<Integer>(radiusKey, 3)); initProperty(new PropertyValueWrapper<Color>(colorKey, Color.red)); initProperty(new PropertyValueWrapper<Float>(strokeKey, (float)3.0)); startDrawingPoints(); } public ModelLineGraph(String xfield, String yfield) { this(); xFieldName = xfield; yFieldName = yfield; } 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, Number.class)) { throw new IllegalArgumentException("X value must be a Number"); } if(!Model.isSubclass(yclass, Number.class)) { throw new IllegalArgumentException("Y value must be a Number"); } Number xnumber = (Number)xvalue; Number ynumber = (Number)yvalue; double x = xnumber.doubleValue(); double y = ynumber.doubleValue(); PaintableScale xScale = getPropertyValue(xScaleKey); PaintableScale yScale = getPropertyValue(yScaleKey); LinePoint sp = new LinePoint(x,y); points.put(x, sp); models.put(x, m); xScale.updateScale(x); yScale.updateScale(y); dispatchChangedEvent(); } else { throw new IllegalArgumentException("x or y value was null"); } } catch (IllegalAccessException e) { throw new IllegalArgumentException("x or y 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); } } public void addModels(Iterator<? extends Model> itr) { while(itr.hasNext()) { addModel(itr.next()); } } public <T> Set<T> findTags(double x1, double y1, double x2, double y2, String tagName) { System.out.println(String.format("findTags(%.2f,%.2f,%.2f,%.2f,%s)", x1,y1, x2,y2, tagName)); Set<T> tags = new HashSet<T>(); Collection<Model> contained = findModels(x1, y1, x2, y2); for(Model m : contained) { try { Field f = m.getClass().getField(tagName); T value = (T)f.get(m); tags.add(value); } catch (NoSuchFieldException e) { // silently fail. } catch (IllegalAccessException e) { e.printStackTrace(); } } return tags; } public Collection<Model> findModels(double x1, double y1, double x2, double y2) { ArrayList<Model> ms = new ArrayList<Model>(); for(int i = 0; i < points.size(); i++) { LinePoint pt = points.get(i); Double x = pt.x, y = pt.y; if(x >= x1 && y >= y1 && x < x2 && y < y2) { ms.add(models.get(i)); } } return ms; } public void clearModels() { points.clear(); models.clear(); 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); int radius = getPropertyValue(radiusKey, 3); Color color = getPropertyValue(colorKey, Color.red); float stroke = getPropertyValue(strokeKey, (float)1.0); Color axisColor = getPropertyValue(axisColorKey, Color.black); int diam = radius*2; 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 **/ g2.setColor(Color.white); g2.fillRect(x1, y1, w, h); // Axes g2.setColor(axisColor); g2.drawRect(x1, y1, w-1, h-1); VerticalScalePainter vsp = new VerticalScalePainter(yScale); HorizontalScalePainter hsp = new HorizontalScalePainter(xScale); vsp.paintItem(g, x1, y1, x2, y2); hsp.paintItem(g, x1, y1, x2, y2); clearDrawnPoints(); Vector<Point> pts = new Vector<Point>(); Vector<Double> keys = new Vector<Double>(); Point previous = null; // Points for(Double key : points.keySet()) { LinePoint p = points.get(key); keys.add(key); double xf = xScale.fractionalOffset(p.x); double yf = yScale.fractionalOffset(p.y); int px = x1 + (int)Math.round(xf * (double)w); int py = y2 - (int)Math.round(yf * (double)h); Point pp = new Point(px, py); pts.add(pp); if(previous != null) { g2.setColor(color); g2.drawLine(previous.x, previous.y, pp.x, pp.y); } previous = pp; } for(int i = 0; i < pts.size(); i++) { Point pt = pts.get(i); Model m = models.get(keys.get(i)); int px = pt.x, py = pt.y; g2.setColor(color); g2.drawOval(px-radius, py-radius, diam, diam); if(m != null) { drawPoint(new Point(px, py), m); } } g2.setStroke(oldStroke); } public static class InteractiveFrame extends JFrame { private InteractivePanel panel; private ModelLineGraph scatter; public InteractiveFrame(ModelLineGraph sc, String tagName) { super(tagName); Container c = (Container)getContentPane(); c.setLayout(new BorderLayout()); scatter = sc; c.add(panel = new InteractivePanel(scatter, tagName), BorderLayout.CENTER); panel.setPreferredSize(new Dimension(300, 300)); setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); SwingUtilities.invokeLater(new Runnable() { public void run() { setVisible(true); pack(); } }); } } public static class InteractivePanel extends PaintablePanel { private ModelLineGraph scatter; private int lastWidth, lastHeight; private String tagName; private Point p1, p2; private Point mouse, models; private Set<Model> mouseModels; public InteractivePanel(ModelLineGraph sc, String tagn) { super(new DoubleBufferedPaintable(sc)); scatter = sc; lastWidth = lastHeight = 1; tagName = tagn; p1 = p2 = null; addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { if(tagName != null) { p1 = e.getPoint(); p2 = p1; repaint(); } } public void mouseReleased(MouseEvent e) { if(p1 != null && p2 != null && tagName !=null) { System.out.println(String.format("Searching %s, %s", p1.toString(), p2.toString())); Set tags = findTags(p1, p2, tagName); System.out.println("Found: " + tags.size()); for(Object t : tags) { System.out.println(t.toString()); } } } public void mouseClicked(MouseEvent e) { p1 = p2 = null; repaint(); } }); addMouseMotionListener(new MouseMotionAdapter() { public void mouseDragged(MouseEvent e) { if(p1 != null) { p2 = e.getPoint(); repaint(); } } public void mouseMoved(MouseEvent e) { if(e.getButton() == MouseEvent.NOBUTTON) { mouse = e.getPoint(); Pair<Point,Set<Model>> nearby = scatter.findNearestDrawnPoint(mouse); models = nearby.getFirst(); mouseModels = nearby.getLast(); repaint(); } else { mouse = models = null; mouseModels = null; } } }); } public <T> Set<T> findTags(Point p1, Point p2, String tagName) { int minX = Math.min(p1.x, p2.x), maxX = Math.max(p1.x, p2.x); int minY = Math.min(p1.y, p2.y), maxY = Math.max(p1.y, p2.y); double x1 = xPixToCoord(minX), x2 = xPixToCoord(maxX); double y1 = yPixToCoord(lastHeight-maxY), y2 = yPixToCoord(lastHeight-minY); return scatter.findTags(x1, y1, x2, y2, tagName); } private double xPixToCoord(int x) { PaintableScale scale = scatter.getPropertyValue(ModelLineGraph.xScaleKey); double f = (double)x / (double)lastWidth; double range = scale.getRange(); return scale.getMin() + f*range; } private double yPixToCoord(int y) { PaintableScale scale = scatter.getPropertyValue(ModelLineGraph.yScaleKey); double f = (double)y / (double)lastHeight; double range = scale.getRange(); return scale.getMin() + f*range; } protected void paintComponent(Graphics g) { super.paintComponent(g); lastWidth = getWidth(); lastHeight = getHeight(); if(p1 != null && p2 != null) { int x1 = Math.min(p1.x, p2.x), x2 = Math.max(p1.x, p2.x); int y1 = Math.min(p1.y, p2.y), y2 = Math.max(p1.y, p2.y); g.setColor(Color.blue); g.drawRect(x1, y1, x2-x1, y2-y1); } if(mouse != null && models != null) { g.setColor(Color.black); g.drawLine(mouse.x, mouse.y, models.x, models.y); g.drawString(mouseModels.toString(), mouse.x, mouse.y); } } } public static class LinePoint extends Model { public Double x, y; public LinePoint(Double _x, Double _y) { x = _x; y = _y; } } }