001 package nl.cwi.sen1.gui.plugin.prefusedot; 002 003 import java.awt.Shape; 004 import java.awt.geom.AffineTransform; 005 import java.awt.geom.GeneralPath; 006 import java.awt.geom.Point2D; 007 import java.awt.geom.Rectangle2D; 008 009 import prefuse.Constants; 010 import prefuse.render.EdgeRenderer; 011 import prefuse.util.GraphicsLib; 012 import prefuse.visual.EdgeItem; 013 import prefuse.visual.VisualItem; 014 015 public class DotEdgeRenderer extends EdgeRenderer { 016 protected static Point2D start = new Point2D.Float(); 017 protected static Point2D end = new Point2D.Float(); 018 019 public DotEdgeRenderer(int edgeType, int arrowType) { 020 super(edgeType, arrowType); 021 } 022 023 protected boolean onDotLocation(VisualItem item) { 024 return item.getX() == item.getInt(DotAdapter.DOT_X) 025 && item.getY() == item.getInt(DotAdapter.DOT_Y); 026 } 027 028 protected Shape makeCurvedEdge(Point2D start, Point2D end, Point2D[] points) { 029 Point2D from = start; 030 Point2D to = from; 031 032 GeneralPath gp = new GeneralPath(GeneralPath.WIND_NON_ZERO); 033 gp.moveTo((float) from.getX(), (float) from.getY()); 034 035 int i; 036 for (i = 1; i + 2 < points.length; i += 3) { 037 Point2D cp1 = points[i]; 038 Point2D cp2 = points[i + 1]; 039 Point2D cur = points[i + 2]; 040 041 from = cp1; 042 to = cur; 043 044 gp.curveTo((float) cp1.getX(), (float) cp1.getY(), (float) cp2 045 .getX(), (float) cp2.getY(), (float) cur.getX(), 046 (float) cur.getY()); 047 048 } 049 050 051 if (i < points.length) { 052 from = to; 053 to = points[points.length - 1]; 054 gp.lineTo((float) to.getX(), (float) to.getY()); 055 } 056 057 gp.lineTo((float) end.getX(), (float) end.getY()); 058 059 // TODO: remove this hack that is used to retrieve the last 060 // direction of 061 // the edge 062 start.setLocation(from); 063 064 065 return gp; 066 } 067 068 protected Shape getArrowShape(Point2D start, Point2D end, VisualItem item) { 069 int i = getIntersectionPoint(item, start, end, m_isctPoints); 070 Point2D e = end; 071 if (i > 0) { 072 e = m_isctPoints[0]; 073 } 074 AffineTransform at = getArrowTrans(start, e, 1); 075 return at.createTransformedShape(m_arrowHead); 076 } 077 078 protected static void getAlignedPoint(Point2D p, Rectangle2D r, int xAlign, 079 int yAlign) { 080 double x = r.getX(), y = r.getY(), w = r.getWidth(), h = r.getHeight(); 081 if (xAlign == Constants.CENTER) { 082 x = x + (w / 2); 083 } else if (xAlign == Constants.RIGHT) { 084 x = x + w; 085 } 086 if (yAlign == Constants.CENTER) { 087 y = y + (h / 2); 088 } else if (yAlign == Constants.BOTTOM) { 089 y = y + h; 090 } 091 p.setLocation(x, y); 092 } 093 094 /** 095 * @copy super.getRawShape() but with support for arrows to different shapes than boxes, 096 * and without support for prefuse's curved edges. 097 */ 098 protected Shape getStraightEdge(VisualItem item) { 099 EdgeItem edge = (EdgeItem) item; 100 VisualItem item1 = edge.getSourceItem(); 101 VisualItem item2 = edge.getTargetItem(); 102 103 getAlignedPoint(m_tmpPoints[0], item1.getBounds(), m_xAlign1, m_yAlign1); 104 getAlignedPoint(m_tmpPoints[1], item2.getBounds(), m_xAlign2, m_yAlign2); 105 m_curWidth = (float) (m_width * getLineWidth(item)); 106 107 // create the arrow head, if needed 108 EdgeItem e = (EdgeItem) item; 109 if (e.isDirected() && m_edgeArrow != Constants.EDGE_ARROW_NONE) { 110 Shape arrow = getArrowShape(m_tmpPoints[0], m_tmpPoints[1], item2); 111 setRenderType(RENDER_TYPE_DRAW_AND_FILL); 112 m_curArrow = arrow; 113 } else { 114 m_curArrow = null; 115 } 116 117 // create the edge shape 118 Shape shape = null; 119 double n1x = m_tmpPoints[0].getX(); 120 double n1y = m_tmpPoints[0].getY(); 121 double n2x = m_tmpPoints[1].getX(); 122 double n2y = m_tmpPoints[1].getY(); 123 m_line.setLine(n1x, n1y, n2x, n2y); 124 shape = m_line; 125 126 // return the edge shape 127 return shape; 128 } 129 130 protected Shape getRawShape(VisualItem item) { 131 EdgeItem edge = (EdgeItem) item; 132 VisualItem item1 = edge.getSourceItem(); 133 VisualItem item2 = edge.getTargetItem(); 134 135 // If the nodes are not on their original dot positions, the curved 136 // edge degenerates to a straight edge. 137 if (!onDotLocation(item1) || !onDotLocation(item2)) { 138 return getStraightEdge(item); 139 } 140 141 // Find the middle of the start and end nodes 142 getAlignedPoint(start, item1.getBounds(), Constants.CENTER, 143 Constants.CENTER); 144 getAlignedPoint(end, item2.getBounds(), Constants.CENTER, 145 Constants.CENTER); 146 147 // Retrieve the dot curve points data 148 Point2D[] points = (Point2D[]) item.get(DotAdapter.DOT_CURVE_POINTS); 149 if (points == null) { 150 return getStraightEdge(item); 151 } 152 153 // now construct the curved edge 154 Shape shape = makeCurvedEdge(start, end, points); 155 AffineTransform at = getTransform(item); 156 shape = (at == null) ? shape : at.createTransformedShape(shape); 157 158 // create the arrow head, if needed 159 // TODO: fix this hack that takes care of arrow size offsets 160 end.setLocation(end.getX(), end.getY() - m_arrowHeight); 161 Shape arrow = getArrowShape(start, end, item2); 162 setRenderType(RENDER_TYPE_DRAW_AND_FILL); 163 m_curArrow = arrow; 164 165 setRenderType(RENDER_TYPE_DRAW); 166 return shape; 167 } 168 169 protected int getIntersectionPoint(VisualItem item, Point2D from, Point2D to, 170 Point2D[] intersection) { 171 String shapeName = item.getString(DotAdapter.DOT_SHAPE); 172 int i; 173 174 if (shapeName != null) { 175 if (shapeName.equals("box")) { 176 i = computeBoxIntersectionPoint(item, from, to, intersection); 177 } else if (shapeName.equals("circle")) { 178 i = computeCircleIntersectionPoint(item, from, to, intersection); 179 } else if (shapeName.equals("ellipse")) { 180 i = computeEllipseIntersectionPoint(item, from, to, 181 intersection); 182 } else { 183 i = computeBoxIntersectionPoint(item, from, to, intersection); 184 } 185 } else { 186 i = computeBoxIntersectionPoint(item, from, to, intersection); 187 } 188 189 return i; 190 } 191 192 protected int computeEllipseIntersectionPoint(VisualItem item, Point2D from, 193 Point2D to, Point2D[] intersection) { 194 int i = computeBoxIntersectionPoint(item, from, to, intersection); 195 Rectangle2D bounds = item.getBounds(); 196 float x = (float) (intersection[0].getX() - bounds.getCenterX()); 197 float y = (float) (intersection[0].getY() - bounds.getCenterY()); 198 float xrad = (float) (bounds.getWidth() / 2); 199 float yrad = (float) (bounds.getHeight() / 2); 200 float phi = x != 0 ? (float) Math.atan(Math 201 .abs((y / yrad) / (x / xrad))) : 0; 202 x = (float) Math.cos(phi) * xrad * (x < 0 ? -1 : 1); 203 y = (float) Math.sin(phi) * yrad * (y < 0 ? -1 : 1); 204 intersection[0].setLocation(x + bounds.getCenterX(), y 205 + bounds.getCenterY()); 206 return i; 207 } 208 209 protected int computeCircleIntersectionPoint(VisualItem item, Point2D from, 210 Point2D to, Point2D[] intersection) { 211 return computeEllipseIntersectionPoint(item, from, to, intersection); 212 } 213 214 protected int computeBoxIntersectionPoint(VisualItem item, Point2D from, 215 Point2D to, Point2D[] intersection) { 216 int i = GraphicsLib.intersectLineRectangle(from, to, item.getBounds(), 217 intersection); 218 return i; 219 } 220 }