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 }