View Javadoc
1   package edu.uci.ics.jung.visualization.spatial;
2   
3   import java.awt.Shape;
4   import java.awt.geom.AffineTransform;
5   import java.awt.geom.GeneralPath;
6   import java.awt.geom.Point2D;
7   import java.awt.geom.Rectangle2D;
8   import java.util.Collection;
9   import java.util.HashSet;
10  import java.util.Set;
11  
12  import edu.uci.ics.jung.algorithms.layout.Layout;
13  import edu.uci.ics.jung.graph.Graph;
14  import edu.uci.ics.jung.graph.util.EdgeIndexFunction;
15  import edu.uci.ics.jung.graph.util.EdgeType;
16  import edu.uci.ics.jung.graph.util.Pair;
17  import edu.uci.ics.jung.visualization.BasicVisualizationServer;
18  import edu.uci.ics.jung.visualization.Layer;
19  import edu.uci.ics.jung.visualization.RenderContext;
20  import edu.uci.ics.jung.visualization.decorators.EdgeShape;
21  import edu.uci.ics.jung.visualization.decorators.ParallelEdgeShapeTransformer;
22  
23  /** 
24   * maintains caches of vertices and edges that will be the subset of the
25   * delegate graph's elements that are contained in some Rectangle.
26   * 
27   * @author tanelso
28   *
29   * @param <V> the vertex type
30   * @param <E> the edge type
31   */
32  public class FastRenderingGraph<V, E> implements Graph<V, E> {
33  
34  	protected Graph<V,E> graph;
35  	protected Set<V> vertices = new HashSet<V>();
36  	protected Set<E> edges = new HashSet<E>();
37  	protected boolean dirty;
38  	protected Set<Rectangle2D> bounds;
39  	protected RenderContext<V,E> rc;
40  	protected BasicVisualizationServer<V,E> vv;
41  	protected Layout<V,E> layout;
42  	
43  	public FastRenderingGraph(Graph<V,E> graph, Set<Rectangle2D> bounds, BasicVisualizationServer<V,E> vv) {
44  		this.graph = graph;
45  		this.bounds = bounds;
46  		this.vv = vv;
47  		this.rc = vv.getRenderContext();
48  	}
49  	
50  	private void cleanUp() {
51  		vertices.clear();
52  		edges.clear();
53  		for(V v : graph.getVertices()) {
54  			checkVertex(v);
55  		}
56  		for(E e : graph.getEdges()) {
57  			checkEdge(e);
58  		}
59  	}
60  	
61  	private void checkVertex(V v) {
62          // get the shape to be rendered
63          Shape shape = rc.getVertexShapeTransformer().apply(v);
64          Point2D p = layout.apply(v);
65          p = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, p);
66          float x = (float)p.getX();
67          float y = (float)p.getY();
68          // create a transform that translates to the location of
69          // the vertex to be rendered
70          AffineTransform xform = AffineTransform.getTranslateInstance(x,y);
71          // transform the vertex shape with xtransform
72          shape = xform.createTransformedShape(shape);
73          for(Rectangle2D r : bounds) {
74          	if(shape.intersects(r)) {
75          		vertices.add(v);
76          	}
77          }
78  	}
79  	
80  	private void checkEdge(E e) {
81          Pair<V> endpoints = graph.getEndpoints(e);
82          V v1 = endpoints.getFirst();
83          V v2 = endpoints.getSecond();
84          
85          Point2D p1 = layout.apply(v1);
86          Point2D p2 = layout.apply(v2);
87          p1 = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, p1);
88          p2 = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, p2);
89          float x1 = (float) p1.getX();
90          float y1 = (float) p1.getY();
91          float x2 = (float) p2.getX();
92          float y2 = (float) p2.getY();
93          
94          boolean isLoop = v1.equals(v2);
95          Shape s2 = rc.getVertexShapeTransformer().apply(v2);
96          Shape edgeShape = rc.getEdgeShapeTransformer().apply(e);
97  
98          AffineTransform xform = AffineTransform.getTranslateInstance(x1, y1);
99          
100         if(isLoop) {
101             // this is a self-loop. scale it is larger than the vertex
102             // it decorates and translate it so that its nadir is
103             // at the center of the vertex.
104             Rectangle2D s2Bounds = s2.getBounds2D();
105             xform.scale(s2Bounds.getWidth(),s2Bounds.getHeight());
106             xform.translate(0, -edgeShape.getBounds2D().getWidth()/2);
107         } else if(rc.getEdgeShapeTransformer() instanceof EdgeShape.Orthogonal) {
108             float dx = x2-x1;
109             float dy = y2-y1;
110             int index = 0;
111             if(rc.getEdgeShapeTransformer() instanceof ParallelEdgeShapeTransformer) {
112             	@SuppressWarnings("unchecked")
113 				EdgeIndexFunction<V,E> peif = 
114             		((ParallelEdgeShapeTransformer<V,E>)rc.getEdgeShapeTransformer())
115             			.getEdgeIndexFunction();
116             	index = peif.getIndex(null, e);
117             	index *= 20;
118             }
119             GeneralPath gp = new GeneralPath();
120             gp.moveTo(0,0);// the xform will do the translation to x1,y1
121             if(x1 > x2) {
122             	if(y1 > y2) {
123             		gp.lineTo(0, index);
124             		gp.lineTo(dx-index, index);
125             		gp.lineTo(dx-index, dy);
126             		gp.lineTo(dx, dy);
127             	} else {
128             		gp.lineTo(0, -index);
129             		gp.lineTo(dx-index, -index);
130             		gp.lineTo(dx-index, dy);
131             		gp.lineTo(dx, dy);
132             	}
133 
134             } else {
135             	if(y1 > y2) {
136             		gp.lineTo(0, index);
137             		gp.lineTo(dx+index, index);
138             		gp.lineTo(dx+index, dy);
139             		gp.lineTo(dx, dy);
140             		
141             	} else {
142             		gp.lineTo(0, -index);
143             		gp.lineTo(dx+index, -index);
144             		gp.lineTo(dx+index, dy);
145             		gp.lineTo(dx, dy);
146             		
147             	}
148             	
149             }
150 
151             edgeShape = gp;
152         	
153         } else {
154             // this is a normal edge. Rotate it to the angle between
155             // vertex endpoints, then scale it to the distance between
156             // the vertices
157             float dx = x2-x1;
158             float dy = y2-y1;
159             float thetaRadians = (float) Math.atan2(dy, dx);
160             xform.rotate(thetaRadians);
161             float dist = (float) Math.sqrt(dx*dx + dy*dy);
162             xform.scale(dist, 1.0);
163         }
164         
165         edgeShape = xform.createTransformedShape(edgeShape);
166         for(Rectangle2D r : bounds) {
167         	if(edgeShape.intersects(r)) {
168         		edges.add(e);
169         	}
170         }
171 	}
172 
173 	public Set<Rectangle2D> getBounds() {
174 		return bounds;
175 	}
176 
177 	public void setBounds(Set<Rectangle2D> bounds) {
178 		this.bounds = bounds;
179 	}
180 
181 	public boolean addEdge(E edge, Collection<? extends V> vertices,
182 			EdgeType edgeType) {
183 		return graph.addEdge(edge, vertices, edgeType);
184 	}
185 	public boolean addEdge(E edge, Collection<? extends V> vertices) {
186 		return graph.addEdge(edge, vertices);
187 	}
188 	public boolean addEdge(E e, V v1, V v2, EdgeType edgeType) {
189 		return graph.addEdge(e, v1, v2, edgeType);
190 	}
191 	public boolean addEdge(E e, V v1, V v2) {
192 		return graph.addEdge(e, v1, v2);
193 	}
194 	public boolean addVertex(V vertex) {
195 		return graph.addVertex(vertex);
196 	}
197 	public boolean containsEdge(E edge) {
198 		return graph.containsEdge(edge);
199 	}
200 	public boolean containsVertex(V vertex) {
201 		return graph.containsVertex(vertex);
202 	}
203 	public int degree(V vertex) {
204 		return graph.degree(vertex);
205 	}
206 	public E findEdge(V v1, V v2) {
207 		return graph.findEdge(v1, v2);
208 	}
209 	public Collection<E> findEdgeSet(V v1, V v2) {
210 		return graph.findEdgeSet(v1, v2);
211 	}
212 	public EdgeType getDefaultEdgeType() {
213 		return graph.getDefaultEdgeType();
214 	}
215 	public V getDest(E directedEdge) {
216 		return graph.getDest(directedEdge);
217 	}
218 	public int getEdgeCount() {
219 		return graph.getEdgeCount();
220 	}
221 	public int getEdgeCount(EdgeType edgeType) {
222 		return graph.getEdgeCount(edgeType);
223 	}
224 	public Collection<E> getEdges() {
225 		if(dirty) {
226 			cleanUp();
227 		}
228 		return edges;
229 	}
230 	public Collection<E> getEdges(EdgeType edgeType) {
231 		return graph.getEdges(edgeType);
232 	}
233 	public EdgeType getEdgeType(E edge) {
234 		return graph.getEdgeType(edge);
235 	}
236 	public Pair<V> getEndpoints(E edge) {
237 		return graph.getEndpoints(edge);
238 	}
239 	public int getIncidentCount(E edge) {
240 		return graph.getIncidentCount(edge);
241 	}
242 	public Collection<E> getIncidentEdges(V vertex) {
243 		return graph.getIncidentEdges(vertex);
244 	}
245 	public Collection<V> getIncidentVertices(E edge) {
246 		return graph.getIncidentVertices(edge);
247 	}
248 	public Collection<E> getInEdges(V vertex) {
249 		return graph.getInEdges(vertex);
250 	}
251 	public int getNeighborCount(V vertex) {
252 		return graph.getNeighborCount(vertex);
253 	}
254 	public Collection<V> getNeighbors(V vertex) {
255 		return graph.getNeighbors(vertex);
256 	}
257 	public V getOpposite(V vertex, E edge) {
258 		return graph.getOpposite(vertex, edge);
259 	}
260 	public Collection<E> getOutEdges(V vertex) {
261 		return graph.getOutEdges(vertex);
262 	}
263 	public int getPredecessorCount(V vertex) {
264 		return graph.getPredecessorCount(vertex);
265 	}
266 	public Collection<V> getPredecessors(V vertex) {
267 		return graph.getPredecessors(vertex);
268 	}
269 	public V getSource(E directedEdge) {
270 		return graph.getSource(directedEdge);
271 	}
272 	public int getSuccessorCount(V vertex) {
273 		return graph.getSuccessorCount(vertex);
274 	}
275 	public Collection<V> getSuccessors(V vertex) {
276 		return graph.getSuccessors(vertex);
277 	}
278 	public int getVertexCount() {
279 		return graph.getVertexCount();
280 	}
281 	public Collection<V> getVertices() {
282 		if(dirty) cleanUp();
283 		return vertices;
284 	}
285 	public int inDegree(V vertex) {
286 		return graph.inDegree(vertex);
287 	}
288 	public boolean isDest(V vertex, E edge) {
289 		return graph.isDest(vertex, edge);
290 	}
291 	public boolean isIncident(V vertex, E edge) {
292 		return graph.isIncident(vertex, edge);
293 	}
294 	public boolean isNeighbor(V v1, V v2) {
295 		return graph.isNeighbor(v1, v2);
296 	}
297 	public boolean isPredecessor(V v1, V v2) {
298 		return graph.isPredecessor(v1, v2);
299 	}
300 	public boolean isSource(V vertex, E edge) {
301 		return graph.isSource(vertex, edge);
302 	}
303 	public boolean isSuccessor(V v1, V v2) {
304 		return graph.isSuccessor(v1, v2);
305 	}
306 	public int outDegree(V vertex) {
307 		return graph.outDegree(vertex);
308 	}
309 	public boolean removeEdge(E edge) {
310 		return graph.removeEdge(edge);
311 	}
312 	public boolean removeVertex(V vertex) {
313 		return graph.removeVertex(vertex);
314 	}
315 }