| Line | Hits | Source |
|---|---|---|
| 1 | /* | |
| 2 | * Copyright (c) 2005, the JUNG Project and the Regents of the University | |
| 3 | * of California | |
| 4 | * All rights reserved. | |
| 5 | * | |
| 6 | * This software is open-source under the BSD license; see either | |
| 7 | * "license.txt" or | |
| 8 | * http://jung.sourceforge.net/license.txt for a description. | |
| 9 | * Created on Mar 11, 2005 | |
| 10 | * | |
| 11 | */ | |
| 12 | package edu.uci.ics.jung.visualization; | |
| 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.ConcurrentModificationException; | |
| 21 | import java.util.Iterator; | |
| 22 | ||
| 23 | import edu.uci.ics.jung.graph.Edge; | |
| 24 | import edu.uci.ics.jung.graph.Vertex; | |
| 25 | import edu.uci.ics.jung.utils.Pair; | |
| 26 | import edu.uci.ics.jung.visualization.transform.LayoutTransformer; | |
| 27 | import edu.uci.ics.jung.visualization.transform.MutableAffineTransformer; | |
| 28 | import edu.uci.ics.jung.visualization.transform.Transformer; | |
| 29 | ||
| 30 | /** | |
| 31 | * ShapePickSupport provides access to Vertices and Edges based on | |
| 32 | * their actual shapes. | |
| 33 | * | |
| 34 | * @author Tom Nelson - RABA Technologies | |
| 35 | * | |
| 36 | */ | |
| 37 | public class ShapePickSupport implements PickSupport { | |
| 38 | ||
| 39 | protected HasGraphLayout hasGraphLayout; | |
| 40 | protected HasShapeFunctions hasShapeFunctions; | |
| 41 | protected float pickSize; | |
| 42 | protected LayoutTransformer layoutTransformer; | |
| 43 | ||
| 44 | /** | |
| 45 | * Create an instance. | |
| 46 | * The HasGraphLayout is used as the source of the current | |
| 47 | * Graph Layout. The HasShapes | |
| 48 | * is used to access the VertexShapes and the EdgeShapes | |
| 49 | * @param hasGraphLayout source of the current layout. | |
| 50 | * @param hasShapeFunctions source of Vertex and Edge shapes. | |
| 51 | * @param pickSize how large to make the pick footprint for line edges | |
| 52 | */ | |
| 53 | public ShapePickSupport(HasGraphLayout hasGraphLayout, | |
| 54 | LayoutTransformer layoutTransformer, HasShapeFunctions hasShapeFunctions, | |
| 55 | 0 | float pickSize) { |
| 56 | 0 | this.hasGraphLayout = hasGraphLayout; |
| 57 | 0 | this.hasShapeFunctions = hasShapeFunctions; |
| 58 | 0 | this.layoutTransformer = layoutTransformer; |
| 59 | 0 | this.pickSize = pickSize; |
| 60 | 0 | } |
| 61 | ||
| 62 | 0 | public ShapePickSupport(float pickSize) { |
| 63 | 0 | this.pickSize = pickSize; |
| 64 | 0 | } |
| 65 | ||
| 66 | /** | |
| 67 | * Create an instance. | |
| 68 | * The pickSize footprint defaults to 2. | |
| 69 | */ | |
| 70 | public ShapePickSupport() { | |
| 71 | 0 | this(2); |
| 72 | 0 | } |
| 73 | ||
| 74 | /** | |
| 75 | * called by a HasLayout impl (like VisualizationViewer) when this | |
| 76 | * PickSupport impl is | |
| 77 | * added to it. This allows the PickSupport to | |
| 78 | * always get the current Layout and the current Renderer | |
| 79 | * from thecomponent it supports picking on. | |
| 80 | */ | |
| 81 | public void setHasGraphLayout(HasGraphLayout hasGraphLayout) { | |
| 82 | 0 | this.hasGraphLayout = hasGraphLayout; |
| 83 | 0 | } |
| 84 | ||
| 85 | /** | |
| 86 | * @param hasShapes The hasShapes to set. | |
| 87 | */ | |
| 88 | public void setHasShapes(HasShapeFunctions hasShapes) { | |
| 89 | 0 | this.hasShapeFunctions = hasShapes; |
| 90 | 0 | } |
| 91 | /** | |
| 92 | * @return Returns the layoutTransformer. | |
| 93 | */ | |
| 94 | public LayoutTransformer getLayoutTransformer() { | |
| 95 | 0 | return layoutTransformer; |
| 96 | } | |
| 97 | ||
| 98 | /** | |
| 99 | * When this PickSupport is set on a VisualizationViewer, | |
| 100 | * the VisualizationViewer calls this method to pass its | |
| 101 | * layout transformer in | |
| 102 | * | |
| 103 | * @param layoutTransformer The layoutTransformer to set. | |
| 104 | */ | |
| 105 | public void setLayoutTransformer(LayoutTransformer layoutTransformer) { | |
| 106 | 0 | this.layoutTransformer = layoutTransformer; |
| 107 | 0 | } |
| 108 | ||
| 109 | /** | |
| 110 | * Iterates over Vertices, checking to see if x,y is contained in the | |
| 111 | * Vertex's Shape. If (x,y) is contained in more than one vertex, use | |
| 112 | * the vertex whose center is closest to the pick point. | |
| 113 | * @see edu.uci.ics.jung.visualization.PickSupport#getVertex(double, double) | |
| 114 | */ | |
| 115 | public Vertex getVertex(double x, double y) { | |
| 116 | 0 | Layout layout = hasGraphLayout.getGraphLayout(); |
| 117 | ||
| 118 | 0 | Vertex closest = null; |
| 119 | 0 | double minDistance = Double.MAX_VALUE; |
| 120 | while(true) { | |
| 121 | try { | |
| 122 | 0 | for (Iterator iter=layout.getGraph().getVertices().iterator(); iter.hasNext(); ) { |
| 123 | 0 | if(hasShapeFunctions != null) { |
| 124 | 0 | Vertex v = (Vertex) iter.next(); |
| 125 | 0 | Shape shape = hasShapeFunctions.getVertexShapeFunction().getShape(v); |
| 126 | // transform the vertex location to screen coords | |
| 127 | 0 | Point2D p = layoutTransformer.layoutTransform(layout.getLocation(v)); |
| 128 | 0 | if(p == null) continue; |
| 129 | 0 | AffineTransform xform = |
| 130 | AffineTransform.getTranslateInstance(p.getX(), p.getY()); | |
| 131 | 0 | shape = xform.createTransformedShape(shape); |
| 132 | // see if this vertex center is closest to the pick point | |
| 133 | // among any other containing vertices | |
| 134 | 0 | if(shape.contains(x, y)) { |
| 135 | ||
| 136 | 0 | Rectangle2D bounds = shape.getBounds2D(); |
| 137 | 0 | double dx = bounds.getCenterX() - x; |
| 138 | 0 | double dy = bounds.getCenterY() - y; |
| 139 | 0 | double dist = dx * dx + dy * dy; |
| 140 | 0 | if (dist < minDistance) { |
| 141 | 0 | minDistance = dist; |
| 142 | 0 | closest = v; |
| 143 | } | |
| 144 | } | |
| 145 | } | |
| 146 | } | |
| 147 | 0 | break; |
| 148 | 0 | } catch(ConcurrentModificationException cme) {} |
| 149 | } | |
| 150 | 0 | return closest; |
| 151 | } | |
| 152 | ||
| 153 | /** | |
| 154 | * return an edge whose shape intersects the 'pickArea' footprint of the passed | |
| 155 | * x,y, coordinates. | |
| 156 | */ | |
| 157 | public Edge getEdge(double x, double y) { | |
| 158 | 0 | Layout layout = hasGraphLayout.getGraphLayout(); |
| 159 | ||
| 160 | // as a Line has no area, we can't always use edgeshape.contains(point) so we | |
| 161 | // make a small rectangular pickArea around the point and check if the | |
| 162 | // edgeshape.intersects(pickArea) | |
| 163 | 0 | Rectangle2D pickArea = |
| 164 | new Rectangle2D.Float((float)x-pickSize/2,(float)y-pickSize/2,pickSize,pickSize); | |
| 165 | 0 | Edge closest = null; |
| 166 | 0 | double minDistance = Double.MAX_VALUE; |
| 167 | while(true) { | |
| 168 | try { | |
| 169 | 0 | for (Iterator iter=layout.getGraph().getEdges().iterator(); iter.hasNext(); ) { |
| 170 | ||
| 171 | 0 | if(hasShapeFunctions != null) { |
| 172 | 0 | Edge e = (Edge) iter.next(); |
| 173 | 0 | Pair pair = e.getEndpoints(); |
| 174 | 0 | Vertex v1 = (Vertex)pair.getFirst(); |
| 175 | 0 | Vertex v2 = (Vertex)pair.getSecond(); |
| 176 | 0 | boolean isLoop = v1.equals(v2); |
| 177 | 0 | Point2D p1 = layoutTransformer.layoutTransform(layout.getLocation(v1)); |
| 178 | 0 | Point2D p2 = layoutTransformer.layoutTransform(layout.getLocation(v2)); |
| 179 | 0 | if(p1 == null || p2 == null) continue; |
| 180 | 0 | float x1 = (float) p1.getX(); |
| 181 | 0 | float y1 = (float) p1.getY(); |
| 182 | 0 | float x2 = (float) p2.getX(); |
| 183 | 0 | float y2 = (float) p2.getY(); |
| 184 | ||
| 185 | // translate the edge to the starting vertex | |
| 186 | 0 | AffineTransform xform = AffineTransform.getTranslateInstance(x1, y1); |
| 187 | ||
| 188 | 0 | Shape edgeShape = hasShapeFunctions.getEdgeShapeFunction().getShape(e); |
| 189 | 0 | if(isLoop) { |
| 190 | // make the loops proportional to the size of the vertex | |
| 191 | 0 | Shape s2 = hasShapeFunctions.getVertexShapeFunction().getShape(v2); |
| 192 | 0 | Rectangle2D s2Bounds = s2.getBounds2D(); |
| 193 | 0 | xform.scale(s2Bounds.getWidth(),s2Bounds.getHeight()); |
| 194 | // move the loop so that the nadir is centered in the vertex | |
| 195 | 0 | xform.translate(0, -edgeShape.getBounds2D().getHeight()/2); |
| 196 | } else { | |
| 197 | 0 | float dx = x2 - x1; |
| 198 | 0 | float dy = y2 - y1; |
| 199 | // rotate the edge to the angle between the vertices | |
| 200 | 0 | double theta = Math.atan2(dy,dx); |
| 201 | 0 | xform.rotate(theta); |
| 202 | // stretch the edge to span the distance between the vertices | |
| 203 | 0 | float dist = (float) Math.sqrt(dx*dx + dy*dy); |
| 204 | 0 | xform.scale(dist, 1.0f); |
| 205 | } | |
| 206 | ||
| 207 | // transform the edge to its location and dimensions | |
| 208 | 0 | edgeShape = xform.createTransformedShape(edgeShape); |
| 209 | ||
| 210 | // because of the transform, the edgeShape is now a GeneralPath | |
| 211 | // see if this edge is the closest of any that intersect | |
| 212 | 0 | if(edgeShape.intersects(pickArea)) { |
| 213 | 0 | float cx=0; |
| 214 | 0 | float cy=0; |
| 215 | 0 | float[] f = new float[6]; |
| 216 | 0 | PathIterator pi = new GeneralPath(edgeShape).getPathIterator(null); |
| 217 | 0 | if(pi.isDone()==false) { |
| 218 | 0 | pi.next(); |
| 219 | 0 | pi.currentSegment(f); |
| 220 | 0 | cx = f[0]; |
| 221 | 0 | cy = f[1]; |
| 222 | 0 | if(pi.isDone()==false) { |
| 223 | 0 | pi.currentSegment(f); |
| 224 | 0 | cx = f[0]; |
| 225 | 0 | cy = f[1]; |
| 226 | } | |
| 227 | } | |
| 228 | 0 | float dx = (float) (cx - x); |
| 229 | 0 | float dy = (float) (cy - y); |
| 230 | 0 | float dist = dx * dx + dy * dy; |
| 231 | 0 | if (dist < minDistance) { |
| 232 | 0 | minDistance = dist; |
| 233 | 0 | closest = e; |
| 234 | } | |
| 235 | } | |
| 236 | } | |
| 237 | } | |
| 238 | 0 | break; |
| 239 | 0 | } catch(ConcurrentModificationException cme) {} |
| 240 | } | |
| 241 | 0 | return closest; |
| 242 | } | |
| 243 | ||
| 244 | /** | |
| 245 | * <code>ShapePickSupport</code> gets its layout from its VisualizationViewer, so this | |
| 246 | * method currently does nothing. | |
| 247 | * @see edu.uci.ics.jung.visualization.PickSupport#setLayout(edu.uci.ics.jung.visualization.Layout) | |
| 248 | */ | |
| 249 | 0 | public void setLayout(Layout layout) {} |
| 250 | } |
|
this report was generated by version 1.0.5 of jcoverage. |
copyright © 2003, jcoverage ltd. all rights reserved. |