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    }