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    }