View Javadoc
1   /*
2    * Copyright (c) 2005, The JUNG Authors
3    * All rights reserved.
4    *
5    * This software is open-source under the BSD license; see either "license.txt"
6    * or https://github.com/jrtom/jung/blob/master/LICENSE for a description.
7    *
8    * Created on Aug 23, 2005
9    */
10  package edu.uci.ics.jung.visualization.renderers;
11  
12  import java.awt.Dimension;
13  import java.awt.Paint;
14  import java.awt.Rectangle;
15  import java.awt.Shape;
16  import java.awt.Stroke;
17  import java.awt.geom.AffineTransform;
18  import java.awt.geom.GeneralPath;
19  import java.awt.geom.Point2D;
20  import java.awt.geom.Rectangle2D;
21  
22  import javax.swing.JComponent;
23  
24  import edu.uci.ics.jung.algorithms.layout.Layout;
25  import edu.uci.ics.jung.graph.Graph;
26  import edu.uci.ics.jung.graph.util.Context;
27  import edu.uci.ics.jung.graph.util.EdgeIndexFunction;
28  import edu.uci.ics.jung.graph.util.EdgeType;
29  import edu.uci.ics.jung.graph.util.Pair;
30  import edu.uci.ics.jung.visualization.Layer;
31  import edu.uci.ics.jung.visualization.RenderContext;
32  import edu.uci.ics.jung.visualization.decorators.EdgeShape;
33  import edu.uci.ics.jung.visualization.decorators.ParallelEdgeShapeTransformer;
34  import edu.uci.ics.jung.visualization.transform.LensTransformer;
35  import edu.uci.ics.jung.visualization.transform.MutableTransformer;
36  import edu.uci.ics.jung.visualization.transform.shape.GraphicsDecorator;
37  
38  public class BasicEdgeRenderer<V,E> implements Renderer.Edge<V,E> {
39  	
40  	protected EdgeArrowRenderingSupport<V,E> edgeArrowRenderingSupport =
41  		new BasicEdgeArrowRenderingSupport<V,E>();
42  
43      public void paintEdge(RenderContext<V,E> rc, Layout<V, E> layout, E e) {
44          GraphicsDecorator g2d = rc.getGraphicsContext();
45          Graph<V,E> graph = layout.getGraph();
46          if (!rc.getEdgeIncludePredicate().apply(Context.<Graph<V,E>,E>getInstance(graph,e)))
47              return;
48          
49          // don't draw edge if either incident vertex is not drawn
50          Pair<V> endpoints = graph.getEndpoints(e);
51          V v1 = endpoints.getFirst();
52          V v2 = endpoints.getSecond();
53          if (!rc.getVertexIncludePredicate().apply(Context.<Graph<V,E>,V>getInstance(graph,v1)) || 
54              !rc.getVertexIncludePredicate().apply(Context.<Graph<V,E>,V>getInstance(graph,v2)))
55              return;
56          
57          Stroke new_stroke = rc.getEdgeStrokeTransformer().apply(e);
58          Stroke old_stroke = g2d.getStroke();
59          if (new_stroke != null)
60              g2d.setStroke(new_stroke);
61          
62          drawSimpleEdge(rc, layout, e);
63  
64          // restore paint and stroke
65          if (new_stroke != null)
66              g2d.setStroke(old_stroke);
67  
68      }
69  
70  	protected Shape prepareFinalEdgeShape(RenderContext<V,E> rc, Layout<V, E> layout, E e, int[] coords, boolean[] loop) {
71          Graph<V,E> graph = layout.getGraph();
72          Pair<V> endpoints = graph.getEndpoints(e);
73          V v1 = endpoints.getFirst();
74          V v2 = endpoints.getSecond();
75          
76          Point2D p1 = layout.apply(v1);
77          Point2D p2 = layout.apply(v2);
78          p1 = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, p1);
79          p2 = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, p2);
80          float x1 = (float) p1.getX();
81          float y1 = (float) p1.getY();
82          float x2 = (float) p2.getX();
83          float y2 = (float) p2.getY();
84          coords[0] = (int)x1;
85          coords[1] = (int)y1;
86          coords[2] = (int)x2;
87          coords[3] = (int)y2;
88          
89          boolean isLoop = loop[0] = v1.equals(v2);
90          Shape s2 = rc.getVertexShapeTransformer().apply(v2);
91          Shape edgeShape = rc.getEdgeShapeTransformer().apply(e);
92          
93          AffineTransform xform = AffineTransform.getTranslateInstance(x1, y1);
94          
95          if(isLoop) {
96              // this is a self-loop. scale it is larger than the vertex
97              // it decorates and translate it so that its nadir is
98              // at the center of the vertex.
99              Rectangle2D s2Bounds = s2.getBounds2D();
100             xform.scale(s2Bounds.getWidth(),s2Bounds.getHeight());
101             xform.translate(0, -edgeShape.getBounds2D().getWidth()/2);
102         } else if(rc.getEdgeShapeTransformer() instanceof EdgeShape.Orthogonal) {
103             float dx = x2-x1;
104             float dy = y2-y1;
105             int index = 0;
106             if(rc.getEdgeShapeTransformer() instanceof ParallelEdgeShapeTransformer) {
107 				@SuppressWarnings("unchecked")
108 				EdgeIndexFunction<V,E> peif =
109 					((ParallelEdgeShapeTransformer<V,E>)rc.getEdgeShapeTransformer())
110 						.getEdgeIndexFunction();
111             	index = peif.getIndex(null, e);
112             	index *= 20;
113             }
114             GeneralPath gp = new GeneralPath();
115             gp.moveTo(0,0);// the xform will do the translation to x1,y1
116             if(x1 > x2) {
117             	if(y1 > y2) {
118             		gp.lineTo(0, index);
119             		gp.lineTo(dx-index, index);
120             		gp.lineTo(dx-index, dy);
121             		gp.lineTo(dx, dy);
122             	} else {
123             		gp.lineTo(0, -index);
124             		gp.lineTo(dx-index, -index);
125             		gp.lineTo(dx-index, dy);
126             		gp.lineTo(dx, dy);
127             	}
128 
129             } else {
130             	if(y1 > y2) {
131             		gp.lineTo(0, index);
132             		gp.lineTo(dx+index, index);
133             		gp.lineTo(dx+index, dy);
134             		gp.lineTo(dx, dy);
135             		
136             	} else {
137             		gp.lineTo(0, -index);
138             		gp.lineTo(dx+index, -index);
139             		gp.lineTo(dx+index, dy);
140             		gp.lineTo(dx, dy);
141             		
142             	}
143             	
144             }
145 
146             edgeShape = gp;
147         	
148         } else {
149             // this is a normal edge. Rotate it to the angle between
150             // vertex endpoints, then scale it to the distance between
151             // the vertices
152             float dx = x2-x1;
153             float dy = y2-y1;
154             float thetaRadians = (float) Math.atan2(dy, dx);
155             xform.rotate(thetaRadians);
156             float dist = (float) Math.sqrt(dx*dx + dy*dy);
157             xform.scale(dist, 1.0);
158         }
159         
160         edgeShape = xform.createTransformedShape(edgeShape);
161         
162         return edgeShape;
163 	}
164 
165 	/**
166      * Draws the edge <code>e</code>, whose endpoints are at <code>(x1,y1)</code>
167      * and <code>(x2,y2)</code>, on the graphics context <code>g</code>.
168      * The <code>Shape</code> provided by the <code>EdgeShapeFunction</code> instance
169      * is scaled in the x-direction so that its width is equal to the distance between
170      * <code>(x1,y1)</code> and <code>(x2,y2)</code>.
171      * @param rc the render context used for rendering the edge
172      * @param layout the layout instance which provides the edge's endpoints' coordinates
173      * @param e the edge to be drawn
174      */
175     protected void drawSimpleEdge(RenderContext<V,E> rc, Layout<V,E> layout, E e) {
176     	
177     	int[] coords = new int[4];
178     	boolean[] loop = new boolean[1];
179     	Shape edgeShape = prepareFinalEdgeShape(rc, layout, e, coords, loop);
180     	
181     	int x1 = coords[0];
182     	int y1 = coords[1];
183     	int x2 = coords[2];
184     	int y2 = coords[3];
185     	boolean isLoop = loop[0];
186         
187         GraphicsDecorator g = rc.getGraphicsContext();
188         Graph<V,E> graph = layout.getGraph();
189         boolean edgeHit = true;
190         boolean arrowHit = true;
191         Rectangle deviceRectangle = null;
192         JComponent vv = rc.getScreenDevice();
193         if(vv != null) {
194             Dimension d = vv.getSize();
195             deviceRectangle = new Rectangle(0,0,d.width,d.height);
196         }
197         
198         MutableTransformer vt = rc.getMultiLayerTransformer().getTransformer(Layer.VIEW);
199         if(vt instanceof LensTransformer) {
200         	vt = ((LensTransformer)vt).getDelegate();
201         }
202         edgeHit = vt.transform(edgeShape).intersects(deviceRectangle);
203 
204         if(edgeHit == true) {
205             
206             Paint oldPaint = g.getPaint();
207             
208             // get Paints for filling and drawing
209             // (filling is done first so that drawing and label use same Paint)
210             Paint fill_paint = rc.getEdgeFillPaintTransformer().apply(e); 
211             if (fill_paint != null)
212             {
213                 g.setPaint(fill_paint);
214                 g.fill(edgeShape);
215             }
216             Paint draw_paint = rc.getEdgeDrawPaintTransformer().apply(e);
217             if (draw_paint != null)
218             {
219                 g.setPaint(draw_paint);
220                 g.draw(edgeShape);
221             }
222             
223             float scalex = (float)g.getTransform().getScaleX();
224             float scaley = (float)g.getTransform().getScaleY();
225             // see if arrows are too small to bother drawing
226             if(scalex < .3 || scaley < .3) return;
227             
228             if (rc.getEdgeArrowPredicate().apply(Context.<Graph<V,E>,E>getInstance(graph, e))) {
229             	
230                 Stroke new_stroke = rc.getEdgeArrowStrokeTransformer().apply(e);
231                 Stroke old_stroke = g.getStroke();
232                 if (new_stroke != null)
233                     g.setStroke(new_stroke);
234 
235                 
236                 Shape destVertexShape = 
237                     rc.getVertexShapeTransformer().apply(graph.getEndpoints(e).getSecond());
238 
239                 AffineTransform xf = AffineTransform.getTranslateInstance(x2, y2);
240                 destVertexShape = xf.createTransformedShape(destVertexShape);
241                 
242                 arrowHit = rc.getMultiLayerTransformer().getTransformer(Layer.VIEW).transform(destVertexShape).intersects(deviceRectangle);
243                 if(arrowHit) {
244                     
245                     AffineTransform at = 
246                         edgeArrowRenderingSupport.getArrowTransform(rc, edgeShape, destVertexShape);
247                     if(at == null) return;
248                     Shape arrow = rc.getEdgeArrowTransformer().apply(Context.<Graph<V,E>,E>getInstance(graph, e));
249                     arrow = at.createTransformedShape(arrow);
250                     g.setPaint(rc.getArrowFillPaintTransformer().apply(e));
251                     g.fill(arrow);
252                     g.setPaint(rc.getArrowDrawPaintTransformer().apply(e));
253                     g.draw(arrow);
254                 }
255                 if (graph.getEdgeType(e) == EdgeType.UNDIRECTED) {
256                     Shape vertexShape = 
257                         rc.getVertexShapeTransformer().apply(graph.getEndpoints(e).getFirst());
258                     xf = AffineTransform.getTranslateInstance(x1, y1);
259                     vertexShape = xf.createTransformedShape(vertexShape);
260                     
261                     arrowHit = rc.getMultiLayerTransformer().getTransformer(Layer.VIEW).transform(vertexShape).intersects(deviceRectangle);
262                     
263                     if(arrowHit) {
264                         AffineTransform at = edgeArrowRenderingSupport.getReverseArrowTransform(rc, edgeShape, vertexShape, !isLoop);
265                         if(at == null) return;
266                         Shape arrow = rc.getEdgeArrowTransformer().apply(Context.<Graph<V,E>,E>getInstance(graph, e));
267                         arrow = at.createTransformedShape(arrow);
268                         g.setPaint(rc.getArrowFillPaintTransformer().apply(e));
269                         g.fill(arrow);
270                         g.setPaint(rc.getArrowDrawPaintTransformer().apply(e));
271                         g.draw(arrow);
272                     }
273                 }
274                 // restore paint and stroke
275                 if (new_stroke != null)
276                     g.setStroke(old_stroke);
277 
278             }
279             
280             // restore old paint
281             g.setPaint(oldPaint);
282         }
283     }
284 
285 	public EdgeArrowRenderingSupport<V, E> getEdgeArrowRenderingSupport() {
286 		return edgeArrowRenderingSupport;
287 	}
288 
289 	public void setEdgeArrowRenderingSupport(
290 			EdgeArrowRenderingSupport<V, E> edgeArrowRenderingSupport) {
291 		this.edgeArrowRenderingSupport = edgeArrowRenderingSupport;
292 	}
293 }