View Javadoc

1   /*
2    * Copyright (c) 2005, the JUNG Project and the Regents of the University of
3    * California All rights reserved.
4    *
5    * This software is open-source under the BSD license; see either "license.txt"
6    * or http://jung.sourceforge.net/license.txt 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.EdgeShape.IndexedRendering;
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 edgeArrowRenderingSupport =
41  		new BasicEdgeArrowRenderingSupport();
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().evaluate(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().evaluate(Context.<Graph<V,E>,V>getInstance(graph,v1)) || 
54              !rc.getVertexIncludePredicate().evaluate(Context.<Graph<V,E>,V>getInstance(graph,v2)))
55              return;
56          
57          Stroke new_stroke = rc.getEdgeStrokeTransformer().transform(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      /**
71       * Draws the edge <code>e</code>, whose endpoints are at <code>(x1,y1)</code>
72       * and <code>(x2,y2)</code>, on the graphics context <code>g</code>.
73       * The <code>Shape</code> provided by the <code>EdgeShapeFunction</code> instance
74       * is scaled in the x-direction so that its width is equal to the distance between
75       * <code>(x1,y1)</code> and <code>(x2,y2)</code>.
76       */
77      @SuppressWarnings("unchecked")
78      protected void drawSimpleEdge(RenderContext<V,E> rc, Layout<V,E> layout, E e) {
79          
80          GraphicsDecorator g = rc.getGraphicsContext();
81          Graph<V,E> graph = layout.getGraph();
82          Pair<V> endpoints = graph.getEndpoints(e);
83          V v1 = endpoints.getFirst();
84          V v2 = endpoints.getSecond();
85          
86          Point2D p1 = layout.transform(v1);
87          Point2D p2 = layout.transform(v2);
88          p1 = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, p1);
89          p2 = rc.getMultiLayerTransformer().transform(Layer.LAYOUT, p2);
90          float x1 = (float) p1.getX();
91          float y1 = (float) p1.getY();
92          float x2 = (float) p2.getX();
93          float y2 = (float) p2.getY();
94          
95          boolean isLoop = v1.equals(v2);
96          Shape s2 = rc.getVertexShapeTransformer().transform(v2);
97          Shape edgeShape = rc.getEdgeShapeTransformer().transform(Context.<Graph<V,E>,E>getInstance(graph, e));
98          
99          boolean edgeHit = true;
100         boolean arrowHit = true;
101         Rectangle deviceRectangle = null;
102         JComponent vv = rc.getScreenDevice();
103         if(vv != null) {
104             Dimension d = vv.getSize();
105             deviceRectangle = new Rectangle(0,0,d.width,d.height);
106         }
107 
108         AffineTransform xform = AffineTransform.getTranslateInstance(x1, y1);
109         
110         if(isLoop) {
111             // this is a self-loop. scale it is larger than the vertex
112             // it decorates and translate it so that its nadir is
113             // at the center of the vertex.
114             Rectangle2D s2Bounds = s2.getBounds2D();
115             xform.scale(s2Bounds.getWidth(),s2Bounds.getHeight());
116             xform.translate(0, -edgeShape.getBounds2D().getWidth()/2);
117         } else if(rc.getEdgeShapeTransformer() instanceof EdgeShape.Orthogonal) {
118             float dx = x2-x1;
119             float dy = y2-y1;
120             int index = 0;
121             if(rc.getEdgeShapeTransformer() instanceof IndexedRendering) {
122             	EdgeIndexFunction<V,E> peif = 
123             		((IndexedRendering<V,E>)rc.getEdgeShapeTransformer()).getEdgeIndexFunction();
124             	index = peif.getIndex(graph, e);
125             	index *= 20;
126             }
127             GeneralPath gp = new GeneralPath();
128             gp.moveTo(0,0);// the xform will do the translation to x1,y1
129             if(x1 > x2) {
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             	} else {
136             		gp.lineTo(0, -index);
137             		gp.lineTo(dx-index, -index);
138             		gp.lineTo(dx-index, dy);
139             		gp.lineTo(dx, dy);
140             	}
141 
142             } else {
143             	if(y1 > y2) {
144             		gp.lineTo(0, index);
145             		gp.lineTo(dx+index, index);
146             		gp.lineTo(dx+index, dy);
147             		gp.lineTo(dx, dy);
148             		
149             	} else {
150             		gp.lineTo(0, -index);
151             		gp.lineTo(dx+index, -index);
152             		gp.lineTo(dx+index, dy);
153             		gp.lineTo(dx, dy);
154             		
155             	}
156             	
157             }
158 
159             edgeShape = gp;
160         	
161         } else {
162             // this is a normal edge. Rotate it to the angle between
163             // vertex endpoints, then scale it to the distance between
164             // the vertices
165             float dx = x2-x1;
166             float dy = y2-y1;
167             float thetaRadians = (float) Math.atan2(dy, dx);
168             xform.rotate(thetaRadians);
169             float dist = (float) Math.sqrt(dx*dx + dy*dy);
170             xform.scale(dist, 1.0);
171         }
172         
173         edgeShape = xform.createTransformedShape(edgeShape);
174         
175         MutableTransformer vt = rc.getMultiLayerTransformer().getTransformer(Layer.VIEW);
176         if(vt instanceof LensTransformer) {
177         	vt = ((LensTransformer)vt).getDelegate();
178         }
179         edgeHit = vt.transform(edgeShape).intersects(deviceRectangle);
180 
181         if(edgeHit == true) {
182             
183             Paint oldPaint = g.getPaint();
184             
185             // get Paints for filling and drawing
186             // (filling is done first so that drawing and label use same Paint)
187             Paint fill_paint = rc.getEdgeFillPaintTransformer().transform(e); 
188             if (fill_paint != null)
189             {
190                 g.setPaint(fill_paint);
191                 g.fill(edgeShape);
192             }
193             Paint draw_paint = rc.getEdgeDrawPaintTransformer().transform(e);
194             if (draw_paint != null)
195             {
196                 g.setPaint(draw_paint);
197                 g.draw(edgeShape);
198             }
199             
200             float scalex = (float)g.getTransform().getScaleX();
201             float scaley = (float)g.getTransform().getScaleY();
202             // see if arrows are too small to bother drawing
203             if(scalex < .3 || scaley < .3) return;
204             
205             if (rc.getEdgeArrowPredicate().evaluate(Context.<Graph<V,E>,E>getInstance(graph, e))) {
206             	
207                 Stroke new_stroke = rc.getEdgeArrowStrokeTransformer().transform(e);
208                 Stroke old_stroke = g.getStroke();
209                 if (new_stroke != null)
210                     g.setStroke(new_stroke);
211 
212                 
213                 Shape destVertexShape = 
214                     rc.getVertexShapeTransformer().transform(graph.getEndpoints(e).getSecond());
215 
216                 AffineTransform xf = AffineTransform.getTranslateInstance(x2, y2);
217                 destVertexShape = xf.createTransformedShape(destVertexShape);
218                 
219                 arrowHit = rc.getMultiLayerTransformer().getTransformer(Layer.VIEW).transform(destVertexShape).intersects(deviceRectangle);
220                 if(arrowHit) {
221                     
222                     AffineTransform at = 
223                         edgeArrowRenderingSupport.getArrowTransform(rc, edgeShape, destVertexShape);
224                     if(at == null) return;
225                     Shape arrow = rc.getEdgeArrowTransformer().transform(Context.<Graph<V,E>,E>getInstance(graph, e));
226                     arrow = at.createTransformedShape(arrow);
227                     g.setPaint(rc.getArrowFillPaintTransformer().transform(e));
228                     g.fill(arrow);
229                     g.setPaint(rc.getArrowDrawPaintTransformer().transform(e));
230                     g.draw(arrow);
231                 }
232                 if (graph.getEdgeType(e) == EdgeType.UNDIRECTED) {
233                     Shape vertexShape = 
234                         rc.getVertexShapeTransformer().transform(graph.getEndpoints(e).getFirst());
235                     xf = AffineTransform.getTranslateInstance(x1, y1);
236                     vertexShape = xf.createTransformedShape(vertexShape);
237                     
238                     arrowHit = rc.getMultiLayerTransformer().getTransformer(Layer.VIEW).transform(vertexShape).intersects(deviceRectangle);
239                     
240                     if(arrowHit) {
241                         AffineTransform at = edgeArrowRenderingSupport.getReverseArrowTransform(rc, edgeShape, vertexShape, !isLoop);
242                         if(at == null) return;
243                         Shape arrow = rc.getEdgeArrowTransformer().transform(Context.<Graph<V,E>,E>getInstance(graph, e));
244                         arrow = at.createTransformedShape(arrow);
245                         g.setPaint(rc.getArrowFillPaintTransformer().transform(e));
246                         g.fill(arrow);
247                         g.setPaint(rc.getArrowDrawPaintTransformer().transform(e));
248                         g.draw(arrow);
249                     }
250                 }
251                 // restore paint and stroke
252                 if (new_stroke != null)
253                     g.setStroke(old_stroke);
254 
255             }
256             
257             // restore old paint
258             g.setPaint(oldPaint);
259         }
260     }
261 
262 	public EdgeArrowRenderingSupport getEdgeArrowRenderingSupport() {
263 		return edgeArrowRenderingSupport;
264 	}
265 
266 	public void setEdgeArrowRenderingSupport(
267 			EdgeArrowRenderingSupport edgeArrowRenderingSupport) {
268 		this.edgeArrowRenderingSupport = edgeArrowRenderingSupport;
269 	}
270 }