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 if (i > 0) { 071 end = m_isctPoints[0]; 072 } 073 AffineTransform at = getArrowTrans(start, end, 1); 074 return at.createTransformedShape(m_arrowHead); 075 } 076 077 protected static void getAlignedPoint(Point2D p, Rectangle2D r, int xAlign, 078 int yAlign) { 079 double x = r.getX(), y = r.getY(), w = r.getWidth(), h = r.getHeight(); 080 if (xAlign == Constants.CENTER) { 081 x = x + (w / 2); 082 } else if (xAlign == Constants.RIGHT) { 083 x = x + w; 084 } 085 if (yAlign == Constants.CENTER) { 086 y = y + (h / 2); 087 } else if (yAlign == Constants.BOTTOM) { 088 y = y + h; 089 } 090 p.setLocation(x, y); 091 } 092 093 /** 094 * @copy super.getRawShape() but with support for arrows to different shapes than boxes, 095 * and without support for prefuse's curved edges. 096 */ 097 protected Shape getStraightEdge(VisualItem item) { 098 EdgeItem edge = (EdgeItem) item; 099 VisualItem item1 = edge.getSourceItem(); 100 VisualItem item2 = edge.getTargetItem(); 101 102 getAlignedPoint(m_tmpPoints[0], item1.getBounds(), m_xAlign1, m_yAlign1); 103 getAlignedPoint(m_tmpPoints[1], item2.getBounds(), m_xAlign2, m_yAlign2); 104 m_curWidth = (float) (m_width * getLineWidth(item)); 105 106 // create the arrow head, if needed 107 EdgeItem e = (EdgeItem) item; 108 if (e.isDirected() && m_edgeArrow != Constants.EDGE_ARROW_NONE) { 109 Shape arrow = getArrowShape(m_tmpPoints[0], m_tmpPoints[1], item2); 110 setRenderType(RENDER_TYPE_DRAW_AND_FILL); 111 m_curArrow = arrow; 112 } else { 113 m_curArrow = null; 114 } 115 116 // create the edge shape 117 Shape shape = null; 118 double n1x = m_tmpPoints[0].getX(); 119 double n1y = m_tmpPoints[0].getY(); 120 double n2x = m_tmpPoints[1].getX(); 121 double n2y = m_tmpPoints[1].getY(); 122 m_line.setLine(n1x, n1y, n2x, n2y); 123 shape = m_line; 124 125 // return the edge shape 126 return shape; 127 } 128 129 protected Shape getRawShape(VisualItem item) { 130 EdgeItem edge = (EdgeItem) item; 131 VisualItem item1 = edge.getSourceItem(); 132 VisualItem item2 = edge.getTargetItem(); 133 134 // If the nodes are not on their original dot positions, the curved 135 // edge degenerates to a straight edge. 136 if (!onDotLocation(item1) || !onDotLocation(item2)) { 137 return getStraightEdge(item); 138 } 139 140 // Find the middle of the start and end nodes 141 getAlignedPoint(start, item1.getBounds(), Constants.CENTER, 142 Constants.CENTER); 143 getAlignedPoint(end, item2.getBounds(), Constants.CENTER, 144 Constants.CENTER); 145 146 // Retrieve the dot curve points data 147 Point2D[] points = (Point2D[]) item.get(DotAdapter.DOT_CURVE_POINTS); 148 if (points == null) { 149 return getStraightEdge(item); 150 } 151 152 // now construct the curved edge 153 Shape shape = makeCurvedEdge(start, end, points); 154 AffineTransform at = getTransform(item); 155 shape = (at == null) ? shape : at.createTransformedShape(shape); 156 157 // create the arrow head, if needed 158 // TODO: fix this hack that takes care of arrow size offsets 159 // end.setLocation(end.getX(), end.getY() - m_arrowHeight); 160 Shape arrow = getArrowShape(start, end, item2); 161 setRenderType(RENDER_TYPE_DRAW_AND_FILL); 162 m_curArrow = arrow; 163 164 setRenderType(RENDER_TYPE_DRAW); 165 return shape; 166 } 167 168 protected int getIntersectionPoint(VisualItem item, Point2D from, Point2D to, 169 Point2D[] intersection) { 170 String shapeName = item.getString(DotAdapter.DOT_SHAPE); 171 int i; 172 173 if (shapeName != null) { 174 if (shapeName.equals("box")) { 175 i = computeBoxIntersectionPoint(item, from, to, intersection); 176 } else if (shapeName.equals("circle")) { 177 i = computeCircleIntersectionPoint(item, from, to, intersection); 178 } else if (shapeName.equals("ellipse")) { 179 i = computeEllipseIntersectionPoint(item, from, to, 180 intersection); 181 } else { 182 i = computeBoxIntersectionPoint(item, from, to, intersection); 183 } 184 } else { 185 i = computeBoxIntersectionPoint(item, from, to, intersection); 186 } 187 188 return i; 189 } 190 191 protected int computeEllipseIntersectionPoint(VisualItem item, Point2D from, 192 Point2D to, Point2D[] intersection) { 193 int i = computeBoxIntersectionPoint(item, from, to, intersection); 194 Rectangle2D bounds = item.getBounds(); 195 float x = (float) (intersection[0].getX() - bounds.getCenterX()); 196 float y = (float) (intersection[0].getY() - bounds.getCenterY()); 197 float xrad = (float) (bounds.getWidth() / 2); 198 float yrad = (float) (bounds.getHeight() / 2); 199 float phi = x != 0 ? (float) Math.atan(Math 200 .abs((y / yrad) / (x / xrad))) : 0; 201 x = (float) Math.cos(phi) * xrad * (x < 0 ? -1 : 1); 202 y = (float) Math.sin(phi) * yrad * (y < 0 ? -1 : 1); 203 intersection[0].setLocation(x + bounds.getCenterX(), y 204 + bounds.getCenterY()); 205 return i; 206 } 207 208 protected int computeCircleIntersectionPoint(VisualItem item, Point2D from, 209 Point2D to, Point2D[] intersection) { 210 return computeEllipseIntersectionPoint(item, from, to, intersection); 211 } 212 213 protected int computeBoxIntersectionPoint(VisualItem item, Point2D from, 214 Point2D to, Point2D[] intersection) { 215 int i = GraphicsLib.intersectLineRectangle(from, to, item.getBounds(), 216 intersection); 217 return i; 218 } 219 }