001    /*
002     * Copyright (C) 2008-2010 by Holger Arndt
003     *
004     * This file is part of the Universal Java Matrix Package (UJMP).
005     * See the NOTICE file distributed with this work for additional
006     * information regarding copyright ownership and licensing.
007     *
008     * UJMP is free software; you can redistribute it and/or modify
009     * it under the terms of the GNU Lesser General Public License as
010     * published by the Free Software Foundation; either version 2
011     * of the License, or (at your option) any later version.
012     *
013     * UJMP is distributed in the hope that it will be useful,
014     * but WITHOUT ANY WARRANTY; without even the implied warranty of
015     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016     * GNU Lesser General Public License for more details.
017     *
018     * You should have received a copy of the GNU Lesser General Public
019     * License along with UJMP; if not, write to the
020     * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
021     * Boston, MA  02110-1301  USA
022     */
023    
024    package org.ujmp.jung;
025    
026    import java.awt.Shape;
027    import java.awt.geom.AffineTransform;
028    import java.awt.geom.PathIterator;
029    import java.awt.geom.Point2D;
030    import java.awt.geom.Rectangle2D;
031    import java.util.ConcurrentModificationException;
032    import java.util.Iterator;
033    
034    import edu.uci.ics.jung.graph.Edge;
035    import edu.uci.ics.jung.graph.Vertex;
036    import edu.uci.ics.jung.utils.Pair;
037    import edu.uci.ics.jung.visualization.Layout;
038    import edu.uci.ics.jung.visualization.ShapePickSupport;
039    
040    public class DefaultShapePickSupport extends ShapePickSupport {
041    
042            
043            public Edge getEdge(double x, double y) {
044                    Layout layout = hasGraphLayout.getGraphLayout();
045    
046                    // as a Line has no area, we can't always use edgeshape.contains(point)
047                    // so we
048                    // make a small rectangular pickArea around the point and check if the
049                    // edgeshape.intersects(pickArea)
050                    Rectangle2D pickArea = new Rectangle2D.Float((float) x - pickSize / 2,
051                                    (float) y - pickSize / 2, pickSize, pickSize);
052                    Edge closest = null;
053                    double minDistance = Double.MAX_VALUE;
054                    while (true) {
055                            try {
056                                    for (Iterator<?> iter = layout.getGraph().getEdges().iterator(); iter
057                                                    .hasNext();) {
058    
059                                            if (hasShapeFunctions != null) {
060                                                    Edge e = (Edge) iter.next();
061                                                    Pair pair = e.getEndpoints();
062                                                    Vertex v1 = (Vertex) pair.getFirst();
063                                                    Vertex v2 = (Vertex) pair.getSecond();
064                                                    boolean isLoop = v1.equals(v2);
065                                                    Point2D p1 = layoutTransformer.layoutTransform(layout
066                                                                    .getLocation(v1));
067                                                    Point2D p2 = layoutTransformer.layoutTransform(layout
068                                                                    .getLocation(v2));
069                                                    if (p1 == null || p2 == null)
070                                                            continue;
071                                                    float x1 = (float) p1.getX();
072                                                    float y1 = (float) p1.getY();
073                                                    float x2 = (float) p2.getX();
074                                                    float y2 = (float) p2.getY();
075    
076                                                    // translate the edge to the starting vertex
077                                                    AffineTransform xform = AffineTransform
078                                                                    .getTranslateInstance(x1, y1);
079    
080                                                    Shape edgeShape = hasShapeFunctions
081                                                                    .getEdgeShapeFunction().getShape(e);
082                                                    if (isLoop) {
083                                                            // make the loops proportional to the size of the
084                                                            // vertex
085                                                            Shape s2 = hasShapeFunctions
086                                                                            .getVertexShapeFunction().getShape(v2);
087                                                            Rectangle2D s2Bounds = s2.getBounds2D();
088                                                            xform.scale(s2Bounds.getWidth(), s2Bounds
089                                                                            .getHeight());
090                                                            // move the loop so that the nadir is centered in
091                                                            // the vertex
092                                                            xform.translate(0, -edgeShape.getBounds2D()
093                                                                            .getHeight() / 2);
094                                                    } else {
095                                                            float dx = x2 - x1;
096                                                            float dy = y2 - y1;
097                                                            // rotate the edge to the angle between the vertices
098                                                            double theta = Math.atan2(dy, dx);
099                                                            xform.rotate(theta);
100                                                            // stretch the edge to span the distance between the
101                                                            // vertices
102                                                            float dist = (float) Math.sqrt(dx * dx + dy * dy);
103                                                            xform.scale(dist, 1.0f);
104                                                    }
105    
106                                                    // transform the edge to its location and dimensions
107                                                    edgeShape = xform.createTransformedShape(edgeShape);
108    
109                                                    // because of the transform, the edgeShape is now a
110                                                    // GeneralPath
111                                                    // see if this edge is the closest of any that intersect
112                                                    if (edgeShape.intersects(pickArea)) {
113                                                            float cx = 0;
114                                                            float cy = 0;
115                                                            float[] f = new float[6];
116                                                            PathIterator pi = edgeShape.getPathIterator(null);
117                                                            if (pi.isDone() == false) {
118                                                                    pi.next();
119                                                                    pi.currentSegment(f);
120                                                                    cx = f[0];
121                                                                    cy = f[1];
122                                                                    if (pi.isDone() == false) {
123                                                                            pi.currentSegment(f);
124                                                                            cx = f[0];
125                                                                            cy = f[1];
126                                                                    }
127                                                            }
128                                                            float dx = (float) (cx - x);
129                                                            float dy = (float) (cy - y);
130                                                            float dist = dx * dx + dy * dy;
131                                                            if (dist < minDistance) {
132                                                                    minDistance = dist;
133                                                                    closest = e;
134                                                            }
135                                                    }
136                                            }
137                                    }
138                                    break;
139                            } catch (ConcurrentModificationException cme) {
140                            }
141                    }
142                    return closest;
143            }
144    
145    }