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 Apr 16, 2005
9    */
10  
11  package edu.uci.ics.jung.visualization.transform;
12  
13  import java.awt.Shape;
14  import java.awt.geom.AffineTransform;
15  import java.awt.geom.GeneralPath;
16  import java.awt.geom.NoninvertibleTransformException;
17  import java.awt.geom.PathIterator;
18  import java.awt.geom.Point2D;
19  
20  import edu.uci.ics.jung.visualization.transform.shape.ShapeTransformer;
21  
22  /**
23   *
24   * Provides methods to map points from one coordinate system to
25   * another, by delegating to a wrapped AffineTransform (uniform)
26   * and its inverse.
27   * 
28   * @author Tom Nelson
29   */
30  public class AffineTransformer implements BidirectionalTransformer, ShapeTransformer {
31  
32      protected AffineTransform inverse;
33  
34      /**
35       * The AffineTransform to use; initialized to identity.
36       * 
37       */
38      protected AffineTransform transform = new AffineTransform();
39      
40      /**
41       * Create an instance that does not transform points.
42       */
43      public AffineTransformer() {
44          // nothing left to do
45      }
46  
47      /**
48       * Create an instance with the supplied transform.
49       * @param transform the transform to use
50       */
51      public AffineTransformer(AffineTransform transform) {
52          if(transform != null) 
53              this.transform = transform;
54      }
55  
56      /**
57       * @return Returns the transform.
58       */
59      public AffineTransform getTransform() {
60          return transform;
61      }
62      /**
63       * @param transform The transform to set.
64       */
65      public void setTransform(AffineTransform transform) {
66          this.transform = transform;
67      }
68      
69      /**
70       * applies the inverse transform to the supplied point
71       * @param p the point to transform
72       * @return the transformed point
73       */
74      public Point2D inverseTransform(Point2D p) {
75  
76          return getInverse().transform(p, null);
77      }
78      
79      public AffineTransform getInverse() {
80          if(inverse == null) {
81              try {
82                  inverse = transform.createInverse();
83              } catch (NoninvertibleTransformException e) {
84                  e.printStackTrace();
85              }
86          }
87          return inverse;
88      }
89      
90      /**
91       * @return the transform's x scale value
92       */
93      public double getScaleX() {
94          return transform.getScaleX();   
95      }
96      
97      /**
98       * @return the transform's y scale value
99       */
100     public double getScaleY() {
101         return transform.getScaleY();
102     }
103 
104     /**
105      * @return the transform's overall scale magnitude
106      */
107     public double getScale() {
108     		return Math.sqrt(transform.getDeterminant());
109     }
110     
111     /**
112      * @return the transform's x shear value
113      */
114     public double getShearX() {
115         return transform.getShearX();
116     }
117     
118     /**
119      * @return the transform's y shear value
120      */
121     public double getShearY() {
122         return transform.getShearY();
123     }
124     
125     /**
126      * @return the transform's x translate value
127      */
128     public double getTranslateX() {
129         return transform.getTranslateX();
130     }
131     
132     /**
133      * @return the transform's y translate value
134      */
135     public double getTranslateY() {
136         return transform.getTranslateY();
137     }
138     
139     /**
140      * Applies the transform to the supplied point.
141      * 
142      * @param p the point to be transformed
143      * @return the transformed point
144      */
145     public Point2D transform(Point2D p) {
146         if(p == null) return null;
147         return transform.transform(p, null);
148     }
149     
150     /**
151      * Transform the supplied shape from graph (layout) to screen (view) coordinates.
152      * 
153      * @return the GeneralPath of the transformed shape
154      */
155     public Shape transform(Shape shape) {
156         GeneralPath newPath = new GeneralPath();
157         float[] coords = new float[6];
158         for(PathIterator iterator=shape.getPathIterator(null);
159             iterator.isDone() == false;
160             iterator.next()) {
161             int type = iterator.currentSegment(coords);
162             switch(type) {
163             case PathIterator.SEG_MOVETO:
164                 Point2D p = transform(new Point2D.Float(coords[0], coords[1]));
165                 newPath.moveTo((float)p.getX(), (float)p.getY());
166                 break;
167                 
168             case PathIterator.SEG_LINETO:
169                 p = transform(new Point2D.Float(coords[0], coords[1]));
170                 newPath.lineTo((float)p.getX(), (float) p.getY());
171                 break;
172                 
173             case PathIterator.SEG_QUADTO:
174                 p = transform(new Point2D.Float(coords[0], coords[1]));
175                 Point2D q = transform(new Point2D.Float(coords[2], coords[3]));
176                 newPath.quadTo((float)p.getX(), (float)p.getY(), (float)q.getX(), (float)q.getY());
177                 break;
178                 
179             case PathIterator.SEG_CUBICTO:
180                 p = transform(new Point2D.Float(coords[0], coords[1]));
181                 q = transform(new Point2D.Float(coords[2], coords[3]));
182                 Point2D r = transform(new Point2D.Float(coords[4], coords[5]));
183                 newPath.curveTo((float)p.getX(), (float)p.getY(), 
184                         (float)q.getX(), (float)q.getY(),
185                         (float)r.getX(), (float)r.getY());
186                 break;
187                 
188             case PathIterator.SEG_CLOSE:
189                 newPath.closePath();
190                 break;
191                     
192             }
193         }
194         return newPath;
195     }
196 
197     /**
198      * Transform the supplied shape from screen (view) to graph (layout) coordinates.
199      * 
200      * @return the GeneralPath of the transformed shape
201      */
202     public Shape inverseTransform(Shape shape) {
203         GeneralPath newPath = new GeneralPath();
204         float[] coords = new float[6];
205         for(PathIterator iterator=shape.getPathIterator(null);
206             iterator.isDone() == false;
207             iterator.next()) {
208             int type = iterator.currentSegment(coords);
209             switch(type) {
210             case PathIterator.SEG_MOVETO:
211                 Point2D p = inverseTransform(new Point2D.Float(coords[0], coords[1]));
212                 newPath.moveTo((float)p.getX(), (float)p.getY());
213                 break;
214                 
215             case PathIterator.SEG_LINETO:
216                 p = inverseTransform(new Point2D.Float(coords[0], coords[1]));
217                 newPath.lineTo((float)p.getX(), (float) p.getY());
218                 break;
219                 
220             case PathIterator.SEG_QUADTO:
221                 p = inverseTransform(new Point2D.Float(coords[0], coords[1]));
222                 Point2D q = inverseTransform(new Point2D.Float(coords[2], coords[3]));
223                 newPath.quadTo((float)p.getX(), (float)p.getY(), (float)q.getX(), (float)q.getY());
224                 break;
225                 
226             case PathIterator.SEG_CUBICTO:
227                 p = inverseTransform(new Point2D.Float(coords[0], coords[1]));
228                 q = inverseTransform(new Point2D.Float(coords[2], coords[3]));
229                 Point2D r = inverseTransform(new Point2D.Float(coords[4], coords[5]));
230                 newPath.curveTo((float)p.getX(), (float)p.getY(), 
231                         (float)q.getX(), (float)q.getY(),
232                         (float)r.getX(), (float)r.getY());
233                 break;
234                 
235             case PathIterator.SEG_CLOSE:
236                 newPath.closePath();
237                 break;
238                     
239             }
240         }
241         return newPath;
242     }
243     
244     public double getRotation() {    
245         double[] unitVector = new double[]{0,0,1,0};
246         double[] result = new double[4];
247 
248         transform.transform(unitVector, 0, result, 0, 2);
249 
250         double dy = Math.abs(result[3] - result[1]);
251         double length = Point2D.distance(result[0], result[1], result[2], result[3]);
252         double rotation = Math.asin(dy / length);        
253         
254         if (result[3] - result[1] > 0) {
255             if (result[2] - result[0] < 0) {
256                 rotation = Math.PI - rotation;
257             }
258         } else {
259             if (result[2] - result[0] > 0) {
260                 rotation = 2 * Math.PI - rotation;
261             } else {
262                 rotation = rotation + Math.PI;
263             }
264         }
265 
266         return rotation;
267     }
268 
269     @Override
270     public String toString() {
271         return "Transformer using "+transform;
272     }
273 }