/* * Author: tdanford * Date: Jan 4, 2009 */ package org.seqcode.viz.paintable; import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.util.*; public class ArrowPainter extends AbstractPaintable { public static void main(String[] args) { new InteractiveArrowFrame(); } private static class InteractiveArrowFrame extends JFrame { public InteractiveArrowFrame() { super("Arrow"); InteractiveArrowPanel p = new InteractiveArrowPanel(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Container c = (Container)getContentPane(); c.setLayout(new BorderLayout()); c.add(p, BorderLayout.CENTER); setVisible(true); pack(); } } private static class InteractiveArrowPanel extends JPanel { private Point p; private ArrowPainter painter; private int thickness; public InteractiveArrowPanel() { thickness = 20; painter = new ArrowPainter(Color.blue); setPreferredSize(new Dimension(400, 400)); p = null; addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { if(e.getButton() == MouseEvent.BUTTON1) { p = e.getPoint(); repaint(); } } }); addMouseMotionListener(new MouseMotionListener() { public void mouseDragged(MouseEvent e) { p = e.getPoint(); repaint(); } public void mouseMoved(MouseEvent e) { } }); } protected void paintComponent(Graphics g) { super.paintComponent(g); int w = getWidth(), h = getHeight(); Graphics2D g2 = (Graphics2D)g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Point c = new Point(w/2, h/2); if(p != null) { painter.paintArrow(g2, c, p, thickness); } } } private Color color; private String label; private boolean direction; public ArrowPainter() { color = Color.blue; direction = true; label = null; } public ArrowPainter(Color c) { this(); color = c; } public ArrowPainter(Color c, boolean dir) { this(c); direction = dir; } public ArrowPainter(boolean dir) { this(Color.blue, dir); } public void setFillColor(Color c) { color = c; } public void setLabel(String l) { label = l; } public void setDirection(boolean dir) { direction = dir; } /** * Some experimental code for painting arbitrary arrows from point-to-point. * */ public void paintArrow(Graphics2D g2, Point p1, Point p2, int thickness) { int dx = p2.x - p1.x; int dy = p2.y - p1.y; double dx2 = (double)(dx * dx); double dy2 = (double)(dy * dy); int dist = (int)Math.round(Math.sqrt(dx2 + dy2)); double rot = 0.0; if(dx == 0 && dy == 0) { rot = 0.0; } else { if(dy < 0) { rot = Math.acos((double)dx / (double)dist); } else { rot = 2.0 * Math.PI - Math.acos((double)dx / (double)dist); } } g2.translate(p1.x, p1.y); g2.rotate(-rot); paintItem(g2, 0, -thickness/2, dist, +thickness/2); g2.rotate(rot); g2.translate(-p1.x, -p1.y); } public void paintItem(Graphics g, int x1, int y1, int x2, int y2) { paintArrow((Graphics2D)g, x1, y1, x2, y2); } public void paintArrow(Graphics2D g2, int x1, int y1, int x2, int y2) { Font font = g2.getFont(); int w = x2-x1, h = y2-y1; int h2 = h/2, h3 = h/3; int w4 = w/4; int arrowDepth = Math.min(h, w); // width of the "head" of the arrow int ya = y1+h3; // ya is the y of the top of the arrow base. int yb = y1+2*h3; // yb is the y of the bottom of the arrow base int yc = y1+h2; // yc is the y of the middle (the point) of the arrow. int xb = direction ? x1 : x2; // xb is the beginning of the base. int xc = direction ? x2 : x1; // xc is the x of the point of the arrow head. // xa is the x of where the arrow base meets the head int xa = direction ? xc - arrowDepth : xc + arrowDepth; int bw = w-arrowDepth+1; // width of the "base" of the arrow int bh = h3; // height of the "base" of the arrow int[] hx = new int[] { xa, xc, xa }; // coordinates (hx and hy) for the arrow head. int[] hy = new int[] { y1, yc, y2 }; g2.setColor(color); g2.fillPolygon(hx, hy, 3); // fills in the head of the arrow g2.fillRect((direction ? xb : xa), ya, bw, bh); // fills in the base of the arrow g2.setColor(Color.black); Stroke stroke = g2.getStroke(); g2.setStroke(new BasicStroke((float)3.0)); g2.drawLine(xb, ya, xa, ya); // draws the top line of the arrow body g2.drawLine(xb, ya, xb, yb); // draws the back line of the arrow body g2.drawLine(xb, yb, xa, yb); // draws the bottom line of the arrow body // outlines the arrow head. g2.drawLine(xa, ya, xa, y1); g2.drawLine(xa, y1, xc, yc); g2.drawLine(xa, yb, xa, y2); g2.drawLine(xa, y2, xc, yc); g2.setStroke(stroke); if(label != null) { int fontSize = font.getSize(); String fontFamily = font.getFamily(); int fontStyle = font.getStyle(); FontMetrics metrics = g2.getFontMetrics(); int lblWidth = metrics.charsWidth(label.toCharArray(), 0, label.length()); int lblHeight = metrics.getAscent() + metrics.getDescent(); while(fontSize > 7 && (lblWidth >= bw || lblHeight >= bh)) { fontSize -= 1; Font newFont = new Font(fontFamily, fontStyle, fontSize); g2.setFont(newFont); metrics = g2.getFontMetrics(); lblWidth = metrics.charsWidth(label.toCharArray(), 0, label.length()); lblHeight = metrics.getAscent() + metrics.getDescent(); } if(lblWidth <= bw && lblHeight <= bh) { if(direction) { g2.drawString(label, xb+bw-lblWidth, ya+bh-metrics.getDescent()); } else { g2.drawString(label, xa+2, ya+bh-metrics.getDescent()); } } } g2.setFont(font); } }