View Javadoc
1   /*
2    * Copyright (c) 2005, The JUNG Authors 
3    *
4    * All rights reserved.
5    *
6    * This software is open-source under the BSD license; see either
7    * "license.txt" or
8    * https://github.com/jrtom/jung/blob/master/LICENSE for a description.
9    * Created on Mar 11, 2005
10   *
11   */
12  package edu.uci.ics.jung.visualization.picking;
13  
14  import java.awt.Shape;
15  import java.awt.geom.AffineTransform;
16  import java.awt.geom.GeneralPath;
17  import java.awt.geom.PathIterator;
18  import java.awt.geom.Point2D;
19  import java.awt.geom.Rectangle2D;
20  import java.util.Collection;
21  import java.util.ConcurrentModificationException;
22  import java.util.HashSet;
23  import java.util.Set;
24  
25  import edu.uci.ics.jung.algorithms.layout.GraphElementAccessor;
26  import edu.uci.ics.jung.algorithms.layout.Layout;
27  import edu.uci.ics.jung.graph.util.Pair;
28  import edu.uci.ics.jung.visualization.Layer;
29  import edu.uci.ics.jung.visualization.VisualizationServer;
30  
31  /**
32   * ShapePickSupport provides access to Vertices and EdgeType based on
33   * their actual shapes. 
34   * 
35   * @author Tom Nelson
36   *
37   */
38  public class LayoutLensShapePickSupport<V, E> extends ShapePickSupport<V,E> 
39  	implements GraphElementAccessor<V,E> {
40  
41      public LayoutLensShapePickSupport(VisualizationServer<V,E> vv, float pickSize) {
42      	super(vv,pickSize);
43      }
44      
45      public LayoutLensShapePickSupport(VisualizationServer<V,E> vv) {
46          this(vv,2);
47      }
48      
49      public V getVertex(Layout<V, E> layout, double x, double y) {
50  
51          V closest = null;
52          double minDistance = Double.MAX_VALUE;
53  
54          while(true) {
55              try {
56                  for(V v : getFilteredVertices(layout)) {
57                  	
58                      Shape shape = vv.getRenderContext().getVertexShapeTransformer().apply(v);
59                      // get the vertex location
60                      Point2D p = layout.apply(v);
61                      if(p == null) continue;
62                      // transform the vertex location to screen coords
63                      p = vv.getRenderContext().getMultiLayerTransformer().transform(p);
64                      AffineTransform xform = 
65                          AffineTransform.getTranslateInstance(p.getX(), p.getY());
66                      shape = xform.createTransformedShape(shape);
67                      
68                      // see if this vertex center is closest to the pick point
69                      // among any other containing vertices
70                      if(shape.contains(x, y)) {
71  
72                      	if(style == Style.LOWEST) {
73                      		// return the first match
74                      		return v;
75                      	} else if(style == Style.HIGHEST) {
76                      		// will return the last match
77                      		closest = v;
78                      	} else {
79                      		Rectangle2D bounds = shape.getBounds2D();
80                      		double dx = bounds.getCenterX() - x;
81                      		double dy = bounds.getCenterY() - y;
82                      		double dist = dx * dx + dy * dy;
83                      		if (dist < minDistance) {
84                      			minDistance = dist;
85                      			closest = v;
86                      		}
87                      	}
88                      }
89                  }
90                  break;
91              } catch(ConcurrentModificationException cme) {}
92          }
93          return closest;
94      }
95  
96      public Collection<V> getVertices(Layout<V, E> layout, Shape rectangle) {
97      	Set<V> pickedVertices = new HashSet<V>();
98      	
99          while(true) {
100             try {
101                 for(V v : getFilteredVertices(layout)) {
102                     Point2D p = layout.apply(v);
103                     if(p == null) continue;
104 
105                     p = vv.getRenderContext().getMultiLayerTransformer().transform(p);
106                     if(rectangle.contains(p)) {
107                     	pickedVertices.add(v);
108                     }
109                 }
110                 break;
111             } catch(ConcurrentModificationException cme) {}
112         }
113         return pickedVertices;
114     }
115 
116     public E getEdge(Layout<V, E> layout, double x, double y) {
117 
118         Point2D ip = vv.getRenderContext().getMultiLayerTransformer().inverseTransform(Layer.VIEW, new Point2D.Double(x,y));
119         x = ip.getX();
120         y = ip.getY();
121 
122         // as a Line has no area, we can't always use edgeshape.contains(point) so we
123         // make a small rectangular pickArea around the point and check if the
124         // edgeshape.intersects(pickArea)
125         Rectangle2D pickArea = 
126             new Rectangle2D.Float((float)x-pickSize/2,(float)y-pickSize/2,pickSize,pickSize);
127         E closest = null;
128         double minDistance = Double.MAX_VALUE;
129         while(true) {
130             try {
131                 for(E e : getFilteredEdges(layout)) {
132 
133                     Pair<V> pair = layout.getGraph().getEndpoints(e);
134                     V v1 = pair.getFirst();
135                     V v2 = pair.getSecond();
136                     boolean isLoop = v1.equals(v2);
137                     Point2D p1 = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, layout.apply(v1));
138                     Point2D p2 = vv.getRenderContext().getMultiLayerTransformer().transform(Layer.LAYOUT, layout.apply(v2));
139                     if(p1 == null || p2 == null) continue;
140                     float x1 = (float) p1.getX();
141                     float y1 = (float) p1.getY();
142                     float x2 = (float) p2.getX();
143                     float y2 = (float) p2.getY();
144 
145                     // translate the edge to the starting vertex
146                     AffineTransform xform = AffineTransform.getTranslateInstance(x1, y1);
147 
148                     Shape edgeShape = vv.getRenderContext().getEdgeShapeTransformer().apply(e);
149                     if(isLoop) {
150                         // make the loops proportional to the size of the vertex
151                         Shape s2 = vv.getRenderContext().getVertexShapeTransformer().apply(v2);
152                         Rectangle2D s2Bounds = s2.getBounds2D();
153                         xform.scale(s2Bounds.getWidth(),s2Bounds.getHeight());
154                         // move the loop so that the nadir is centered in the vertex
155                         xform.translate(0, -edgeShape.getBounds2D().getHeight()/2);
156                     } else {
157                         float dx = x2 - x1;
158                         float dy = y2 - y1;
159                         // rotate the edge to the angle between the vertices
160                         double theta = Math.atan2(dy,dx);
161                         xform.rotate(theta);
162                         // stretch the edge to span the distance between the vertices
163                         float dist = (float) Math.sqrt(dx*dx + dy*dy);
164                         xform.scale(dist, 1.0f);
165                     }
166 
167                     // transform the edge to its location and dimensions
168                     edgeShape = xform.createTransformedShape(edgeShape);
169 
170                     // because of the transform, the edgeShape is now a GeneralPath
171                     // see if this edge is the closest of any that intersect
172                     if(edgeShape.intersects(pickArea)) {
173                         float cx=0;
174                         float cy=0;
175                         float[] f = new float[6];
176                         PathIterator pi = new GeneralPath(edgeShape).getPathIterator(null);
177                         if(pi.isDone()==false) {
178                             pi.next();
179                             pi.currentSegment(f);
180                             cx = f[0];
181                             cy = f[1];
182                             if(pi.isDone()==false) {
183                                 pi.currentSegment(f);
184                                 cx = f[0];
185                                 cy = f[1];
186                             }
187                         }
188                         float dx = (float) (cx - x);
189                         float dy = (float) (cy - y);
190                         float dist = dx * dx + dy * dy;
191                         if (dist < minDistance) {
192                             minDistance = dist;
193                             closest = e;
194                         }
195                     }
196 		        }
197 		        break;
198 		    } catch(ConcurrentModificationException cme) {}
199 		}
200 		return closest;
201     }
202 }