001: import java.awt.Font; 002: import java.awt.Graphics2D; 003: import java.awt.Stroke; 004: import java.awt.font.FontRenderContext; 005: import java.awt.geom.GeneralPath; 006: import java.awt.geom.Line2D; 007: import java.awt.geom.Point2D; 008: import java.awt.geom.Rectangle2D; 009: import java.util.ArrayList; 010: 011: /** 012: An edge that is composed of multiple line segments 013: */ 014: public abstract class SegmentedLineEdge extends GeneralPathEdge 015: { 016: /** 017: Costructs an edge with no adornments. 018: */ 019: public SegmentedLineEdge() 020: { 021: lineStyle = LineStyle.SOLID; 022: startArrowHead = ArrowHead.NONE; 023: endArrowHead = ArrowHead.NONE; 024: startLabel = ""; 025: middleLabel = ""; 026: endLabel = ""; 027: } 028: 029: /** 030: Sets the line style property. 031: @param newValue the new value 032: */ 033: public void setLineStyle(LineStyle newValue) { lineStyle = newValue; } 034: 035: /** 036: Gets the line style property. 037: @return the line style 038: */ 039: public LineStyle getLineStyle() { return lineStyle; } 040: 041: /** 042: Sets the start arrow head property 043: @param newValue the new value 044: */ 045: public void setStartArrowHead(ArrowHead newValue) { startArrowHead = newValue; } 046: 047: /** 048: Gets the start arrow head property 049: @return the start arrow head style 050: */ 051: public ArrowHead getStartArrowHead() { return startArrowHead; } 052: 053: /** 054: Sets the end arrow head property 055: @param newValue the new value 056: */ 057: public void setEndArrowHead(ArrowHead newValue) { endArrowHead = newValue; } 058: 059: /** 060: Gets the end arrow head property 061: @return the end arrow head style 062: */ 063: public ArrowHead getEndArrowHead() { return endArrowHead; } 064: 065: /** 066: Sets the start label property 067: @param newValue the new value 068: */ 069: public void setStartLabel(String newValue) { startLabel = newValue; } 070: 071: /** 072: Gets the start label property 073: @return the label at the start of the edge 074: */ 075: public String getStartLabel() { return startLabel; } 076: 077: /** 078: Sets the middle label property 079: @param newValue the new value 080: */ 081: public void setMiddleLabel(String newValue) { middleLabel = newValue; } 082: 083: /** 084: Gets the middle label property 085: @return the label at the middle of the edge 086: */ 087: public String getMiddleLabel() { return middleLabel; } 088: 089: /** 090: Sets the end label property 091: @param newValue the new value 092: */ 093: public void setEndLabel(String newValue) { endLabel = newValue; } 094: 095: /** 096: Gets the end label property 097: @return the label at the end of the edge 098: */ 099: public String getEndLabel() { return endLabel; } 100: 101: /** 102: Draws the edge. 103: @param g2 the graphics context 104: */ 105: public void draw(Graphics2D g2) 106: { 107: Stroke oldStroke = g2.getStroke(); 108: g2.setStroke(lineStyle.getStroke()); 109: g2.draw(getPath()); 110: g2.setStroke(oldStroke); 111: ArrayList points = getPoints(); 112: startArrowHead.draw(g2, (Point2D) points.get(1), 113: (Point2D) points.get(0)); 114: endArrowHead.draw(g2, (Point2D) points.get(points.size() - 2), 115: (Point2D) points.get(points.size() - 1)); 116: 117: drawString(g2, (Point2D) points.get(1), (Point2D) points.get(0), 118: startLabel, false); 119: drawString(g2, (Point2D) points.get(points.size() / 2 - 1), 120: (Point2D) points.get(points.size() / 2), 121: middleLabel, true); 122: drawString(g2, (Point2D) points.get(points.size() - 2), 123: (Point2D) points.get(points.size() - 1), 124: endLabel, false); 125: } 126: 127: /** 128: Draws a string. 129: @param g2 the graphics context 130: @param p an endpoint of the segment along which to 131: draw the string 132: @param q the other endpoint of the segment along which to 133: draw the string 134: @param s the string to draw 135: @param center true if the string should be centered 136: along the segment 137: */ 138: 139: public static void drawString(Graphics2D g2, 140: Point2D p, Point2D q, String s, boolean center) 141: { 142: Point2D a = getAttachmentPoint(g2, p, q, s, center); 143: g2.drawString(s, (float) a.getX(), (float) a.getY()); 144: } 145: 146: /** 147: Computes the attachment point for drawing a string. 148: @param g2 the graphics context 149: @param p an endpoint of the segment along which to 150: draw the string 151: @param q the other endpoint of the segment along which to 152: draw the string 153: @param s the string to draw 154: @param center true if the string should be centered 155: along the segment 156: @return the point at which to draw the string 157: */ 158: private static Point2D getAttachmentPoint(Graphics2D g2, 159: Point2D p, Point2D q, String s, boolean center) 160: { 161: if (s == null) s = ""; 162: Font font = g2.getFont(); 163: FontRenderContext frc = g2.getFontRenderContext(); 164: Rectangle2D b = font.getStringBounds(s, frc); 165: final int GAP = 3; 166: double xoff = GAP; 167: double yoff = -GAP; 168: Point2D attach = q; 169: if (center) 170: { 171: if (p.getX() > q.getX()) 172: { 173: return getAttachmentPoint(g2, q, p, s, center); 174: } 175: attach = new Point2D.Double((p.getX() + q.getX()) / 2, 176: (p.getY() + q.getY()) / 2); 177: if (p.getY() < q.getY()) 178: yoff = b.getY() + b.getHeight() - GAP; 179: else if (p.getY() == q.getY()) 180: xoff = -b.getWidth() / 2; 181: else 182: yoff = -b.getY() + GAP; 183: } 184: else 185: { 186: if (p.getX() < q.getX()) 187: { 188: xoff = -GAP - b.getWidth(); 189: } 190: if (p.getY() > q.getY()) 191: { 192: yoff = -b.getY() + GAP; 193: } 194: } 195: return new Point2D.Double(attach.getX() + xoff, attach.getY() + yoff); 196: } 197: 198: /** 199: Computes the extent of a string that is drawn along a line segment. 200: @param g2 the graphics context 201: @param p an endpoint of the segment along which to 202: draw the string 203: @param q the other endpoint of the segment along which to 204: draw the string 205: @param s the string to draw 206: @param center true if the string should be centered 207: along the segment 208: @return the rectangle enclosing the string 209: */ 210: public static Rectangle2D getStringBounds(Graphics2D g2, 211: Point2D p, Point2D q, String s, boolean center) 212: { 213: if (g2 == null) return new Rectangle2D.Double(); 214: Font font = g2.getFont(); 215: FontRenderContext frc = g2.getFontRenderContext(); 216: Rectangle2D b = font.getStringBounds(s, frc); 217: Point2D a = getAttachmentPoint(g2, p, q, s, center); 218: return new Rectangle2D.Double(b.getX() + a.getX(), 219: b.getY() + a.getY(), b.getWidth(), b.getHeight()); 220: } 221: 222: public Rectangle2D getBounds(Graphics2D g2) 223: { 224: ArrayList points = getPoints(); 225: Rectangle2D r = super.getBounds(g2); 226: r.add(getStringBounds(g2, 227: (Point2D) points.get(1), (Point2D) points.get(0), 228: startLabel, false)); 229: r.add(getStringBounds(g2, 230: (Point2D) points.get(points.size() / 2 - 1), 231: (Point2D) points.get(points.size() / 2), 232: middleLabel, true)); 233: r.add(getStringBounds(g2, 234: (Point2D) points.get(points.size() - 2), 235: (Point2D) points.get(points.size() - 1), 236: endLabel, false)); 237: return r; 238: } 239: 240: public GeneralPath getPath() 241: { 242: ArrayList points = getPoints(); 243: GeneralPath path = new GeneralPath(); 244: Point2D p = (Point2D) points.get(points.size() - 1); 245: path.moveTo((float) p.getX(), (float) p.getY()); 246: for (int i = points.size() - 2; i >= 0; i--) 247: { 248: p = (Point2D) points.get(i); 249: path.lineTo((float) p.getX(), (float) p.getY()); 250: } 251: return path; 252: } 253: 254: public Line2D getConnectionPoints() 255: { 256: ArrayList points = getPoints(); 257: return new Line2D.Double((Point2D) points.get(0), 258: (Point2D) points.get(points.size() - 1)); 259: } 260: 261: /** 262: Gets the corner points of this segmented line edge 263: @return an array list of Point2D objects, containing 264: the corner points 265: */ 266: public abstract ArrayList getPoints(); 267: 268: private LineStyle lineStyle; 269: private ArrowHead startArrowHead; 270: private ArrowHead endArrowHead; 271: private String startLabel; 272: private String middleLabel; 273: private String endLabel; 274: }