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.Shape;
13  import java.awt.geom.AffineTransform;
14  import java.awt.geom.GeneralPath;
15  import java.awt.geom.Line2D;
16  import java.awt.geom.PathIterator;
17  import java.awt.geom.Point2D;
18  
19  import edu.uci.ics.jung.visualization.RenderContext;
20  
21  public class CenterEdgeArrowRenderingSupport<V,E> implements EdgeArrowRenderingSupport<V,E> {
22  
23      /**
24       * Returns a transform to position the arrowhead on this edge shape at the
25       * point where it intersects the passed vertex shape.
26       */
27      public AffineTransform getArrowTransform(RenderContext<V,E> rc, Shape edgeShape, Shape vertexShape) {
28      	GeneralPath path = new GeneralPath(edgeShape);
29          float[] seg = new float[6];
30          Point2D p1=null;
31          Point2D p2=null;
32          AffineTransform at = new AffineTransform();
33          // count the segments.
34          int middleSegment = 0;
35          int current = 0;
36          for(PathIterator i=path.getPathIterator(null,1); !i.isDone(); i.next()) {
37          	current++;
38          }
39          middleSegment = current/2;
40          // find the middle segment
41          current = 0;
42          for(PathIterator i=path.getPathIterator(null,1); !i.isDone(); i.next()) {
43          	current++;
44          	int ret = i.currentSegment(seg);
45          	if(ret == PathIterator.SEG_MOVETO) {
46          		p2 = new Point2D.Float(seg[0],seg[1]);
47          	} else if(ret == PathIterator.SEG_LINETO) {
48          		p1 = p2;
49          		p2 = new Point2D.Float(seg[0],seg[1]);
50          	}
51          	if(current > middleSegment) { // done
52          		at = getArrowTransform(rc, new Line2D.Float(p1,p2),vertexShape);
53          		break;
54          	}
55  
56          } 
57          return at;
58      }
59  
60      /**
61       * Returns a transform to position the arrowhead on this edge shape at the
62       * point where it intersects the passed vertex shape.
63       */
64      public AffineTransform getReverseArrowTransform(RenderContext<V,E> rc, Shape edgeShape, Shape vertexShape) {
65          return getReverseArrowTransform(rc, edgeShape, vertexShape, true);
66      }
67              
68      /**
69       * <p>Returns a transform to position the arrowhead on this edge shape at the
70       * point where it intersects the passed vertex shape.</p>
71       * 
72       * <p>The Loop edge is a special case because its staring point is not inside
73       * the vertex. The passedGo flag handles this case.</p>
74       * 
75       * @param path
76       * @param vertexShape
77       * @param passedGo - used only for Loop edges
78       */
79      public AffineTransform getReverseArrowTransform(RenderContext<V,E> rc, Shape edgeShape, Shape vertexShape,
80              boolean passedGo) {
81      	GeneralPath path = new GeneralPath(edgeShape);
82          float[] seg = new float[6];
83          Point2D p1=null;
84          Point2D p2=null;
85          AffineTransform at = new AffineTransform();
86          // count the segments.
87          int middleSegment = 0;
88          int current = 0;
89          for(PathIterator i=path.getPathIterator(null,1); !i.isDone(); i.next()) {
90          	current++;
91          }
92          middleSegment = current/2;
93          // find the middle segment
94          current = 0;
95          for(PathIterator i=path.getPathIterator(null,1); !i.isDone(); i.next()) {
96          	current++;
97          	int ret = i.currentSegment(seg);
98          	if(ret == PathIterator.SEG_MOVETO) {
99          		p2 = new Point2D.Float(seg[0],seg[1]);
100         	} else if(ret == PathIterator.SEG_LINETO) {
101         		p1 = p2;
102         		p2 = new Point2D.Float(seg[0],seg[1]);
103         	}
104         	if(current > middleSegment) { // done
105         		at = getReverseArrowTransform(rc, new Line2D.Float(p1,p2),vertexShape);
106         		break;
107         	}
108         }
109         return at;
110     }
111 
112     /**
113      * This is used for the arrow of a directed and for one of the
114      * arrows for non-directed edges
115      * Get a transform to place the arrow shape on the passed edge at the
116      * point where it intersects the passed shape
117      * @param edgeShape
118      * @param vertexShape
119      * @return
120      */
121     public AffineTransform getArrowTransform(RenderContext<V,E> rc, Line2D edgeShape, Shape vertexShape) {
122         
123         // find the midpoint of the edgeShape line, and use it to make the transform
124         Line2D left = new Line2D.Float();
125         Line2D right = new Line2D.Float();
126         this.subdivide(edgeShape, left, right);
127         edgeShape = right;
128         float dx = (float) (edgeShape.getX1()-edgeShape.getX2());
129         float dy = (float) (edgeShape.getY1()-edgeShape.getY2());
130         double atheta = Math.atan2(dx,dy)+Math.PI/2;
131         AffineTransform at = 
132             AffineTransform.getTranslateInstance(edgeShape.getX1(), edgeShape.getY1());
133         at.rotate(-atheta);
134         return at;
135     }
136 
137     /**
138      * This is used for the reverse-arrow of a non-directed edge
139      * get a transform to place the arrow shape on the passed edge at the
140      * point where it intersects the passed shape
141      * @param edgeShape
142      * @param vertexShape
143      * @return
144      */
145     protected AffineTransform getReverseArrowTransform(RenderContext<V,E> rc, Line2D edgeShape, Shape vertexShape) {
146         // find the midpoint of the edgeShape line, and use it to make the transform
147         Line2D left = new Line2D.Float();
148         Line2D right = new Line2D.Float();
149         this.subdivide(edgeShape, left, right);
150         edgeShape = right;
151         float dx = (float) (edgeShape.getX1()-edgeShape.getX2());
152         float dy = (float) (edgeShape.getY1()-edgeShape.getY2());
153         // calculate the angle for the arrowhead
154         double atheta = Math.atan2(dx,dy)-Math.PI/2;
155         AffineTransform at = AffineTransform.getTranslateInstance(edgeShape.getX1(),edgeShape.getY1());
156         at.rotate(-atheta);
157         return at;
158     }
159     
160     /**
161      * divide a Line2D into 2 new Line2Ds that are returned
162      * in the passed left and right instances, if non-null
163      * @param src the line to divide
164      * @param left the left side, or null
165      * @param right the right side, or null
166      */
167     protected void subdivide(Line2D src,
168             Line2D left,
169             Line2D right) {
170         double x1 = src.getX1();
171         double y1 = src.getY1();
172         double x2 = src.getX2();
173         double y2 = src.getY2();
174         
175         double mx = x1 + (x2-x1)/2.0;
176         double my = y1 + (y2-y1)/2.0;
177         if (left != null) {
178             left.setLine(x1, y1, mx, my);
179         }
180         if (right != null) {
181             right.setLine(mx, my, x2, y2);
182         }
183     }
184 
185 }