View Javadoc

1   /*
2    * Copyright (c) 2003, 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    */
9   package edu.uci.ics.jung.visualization.transform.shape;
10  
11  import java.awt.Component;
12  import java.awt.Shape;
13  import java.awt.geom.GeneralPath;
14  import java.awt.geom.PathIterator;
15  import java.awt.geom.Point2D;
16  
17  import edu.uci.ics.jung.algorithms.layout.PolarPoint;
18  import edu.uci.ics.jung.visualization.transform.HyperbolicTransformer;
19  import edu.uci.ics.jung.visualization.transform.MutableTransformer;
20  
21  
22  /**
23   * HyperbolicShapeTransformer extends HyperbolicTransformer and
24   * adds implementations for methods in ShapeFlatnessTransformer.
25   * It modifies the shapes (Vertex, Edge, and Arrowheads) so that
26   * they are distorted by the hyperbolic transformation
27   * 
28   * @author Tom Nelson
29   *
30   *
31   */
32  public class HyperbolicShapeTransformer extends HyperbolicTransformer 
33      implements ShapeFlatnessTransformer {
34  
35      /**
36       * Create an instance, setting values from the passed component
37       * and registering to listen for size changes on the component.
38       */
39      public HyperbolicShapeTransformer(Component component) {
40          this(component, null);
41      }
42      
43      /**
44       * Create an instance, setting values from the passed component
45       * and registering to listen for size changes on the component,
46       * with a possibly shared transform <code>delegate</code>.
47       */
48      public HyperbolicShapeTransformer(Component component, MutableTransformer delegate) {
49          super(component, delegate);
50     }
51      
52      /**
53       * Transform the supplied shape with the overridden transform
54       * method so that the shape is distorted by the hyperbolic 
55       * transform.
56       * @param shape a shape to transform
57       * @return a GeneralPath for the transformed shape
58       */
59      public Shape transform(Shape shape) {
60          return transform(shape, 0);
61      }
62      public Shape transform(Shape shape, float flatness) {
63          GeneralPath newPath = new GeneralPath();
64          float[] coords = new float[6];
65          PathIterator iterator = null;
66          if(flatness == 0) {
67              iterator = shape.getPathIterator(null);
68          } else {
69              iterator = shape.getPathIterator(null, flatness);
70          }
71          for( ;
72              iterator.isDone() == false;
73              iterator.next()) {
74              int type = iterator.currentSegment(coords);
75              switch(type) {
76              case PathIterator.SEG_MOVETO:
77                  Point2D p = _transform(new Point2D.Float(coords[0], coords[1]));
78                  newPath.moveTo((float)p.getX(), (float)p.getY());
79                  break;
80                  
81              case PathIterator.SEG_LINETO:
82                  p = _transform(new Point2D.Float(coords[0], coords[1]));
83                  newPath.lineTo((float)p.getX(), (float) p.getY());
84                  break;
85                  
86              case PathIterator.SEG_QUADTO:
87                  p = _transform(new Point2D.Float(coords[0], coords[1]));
88                  Point2D q = _transform(new Point2D.Float(coords[2], coords[3]));
89                  newPath.quadTo((float)p.getX(), (float)p.getY(), (float)q.getX(), (float)q.getY());
90                  break;
91                  
92              case PathIterator.SEG_CUBICTO:
93                  p = _transform(new Point2D.Float(coords[0], coords[1]));
94                  q = _transform(new Point2D.Float(coords[2], coords[3]));
95                  Point2D r = _transform(new Point2D.Float(coords[4], coords[5]));
96                  newPath.curveTo((float)p.getX(), (float)p.getY(), 
97                          (float)q.getX(), (float)q.getY(),
98                          (float)r.getX(), (float)r.getY());
99                  break;
100                 
101             case PathIterator.SEG_CLOSE:
102                 newPath.closePath();
103                 break;
104                     
105             }
106         }
107         return newPath;
108     }
109 
110     public Shape inverseTransform(Shape shape) {
111         GeneralPath newPath = new GeneralPath();
112         float[] coords = new float[6];
113         for(PathIterator iterator=shape.getPathIterator(null);
114             iterator.isDone() == false;
115             iterator.next()) {
116             int type = iterator.currentSegment(coords);
117             switch(type) {
118             case PathIterator.SEG_MOVETO:
119                 Point2D p = _inverseTransform(new Point2D.Float(coords[0], coords[1]));
120                 newPath.moveTo((float)p.getX(), (float)p.getY());
121                 break;
122                 
123             case PathIterator.SEG_LINETO:
124                 p = _inverseTransform(new Point2D.Float(coords[0], coords[1]));
125                 newPath.lineTo((float)p.getX(), (float) p.getY());
126                 break;
127                 
128             case PathIterator.SEG_QUADTO:
129                 p = _inverseTransform(new Point2D.Float(coords[0], coords[1]));
130                 Point2D q = _inverseTransform(new Point2D.Float(coords[2], coords[3]));
131                 newPath.quadTo((float)p.getX(), (float)p.getY(), (float)q.getX(), (float)q.getY());
132                 break;
133                 
134             case PathIterator.SEG_CUBICTO:
135                 p = _inverseTransform(new Point2D.Float(coords[0], coords[1]));
136                 q = _inverseTransform(new Point2D.Float(coords[2], coords[3]));
137                 Point2D r = _inverseTransform(new Point2D.Float(coords[4], coords[5]));
138                 newPath.curveTo((float)p.getX(), (float)p.getY(), 
139                         (float)q.getX(), (float)q.getY(),
140                         (float)r.getX(), (float)r.getY());
141                 break;
142                 
143             case PathIterator.SEG_CLOSE:
144                 newPath.closePath();
145                 break;
146                     
147             }
148         }
149         return newPath;
150     }
151     /**
152      * override base class transform to project the fisheye effect
153      */
154     private Point2D _transform(Point2D graphPoint) {
155         if(graphPoint == null) return null;
156         Point2D viewCenter = getViewCenter();
157         double viewRadius = getViewRadius();
158         double ratio = getRatio();
159         // transform the point from the graph to the view
160         Point2D viewPoint = graphPoint;//delegate.transform(graphPoint);
161         // calculate point from center
162         double dx = viewPoint.getX() - viewCenter.getX();
163         double dy = viewPoint.getY() - viewCenter.getY();
164         // factor out ellipse
165         dx *= ratio;
166         Point2D pointFromCenter = new Point2D.Double(dx, dy);
167         
168         PolarPoint polar = PolarPoint.cartesianToPolar(pointFromCenter);
169         double theta = polar.getTheta();
170         double radius = polar.getRadius();
171         if(radius > viewRadius) return viewPoint;
172         
173         double mag = Math.tan(Math.PI/2*magnification);
174         radius *= mag;
175         
176         radius = Math.min(radius, viewRadius);
177         radius /= viewRadius;
178         radius *= Math.PI/2;
179         radius = Math.abs(Math.atan(radius));
180         radius *= viewRadius;
181         Point2D projectedPoint = PolarPoint.polarToCartesian(theta, radius);
182         projectedPoint.setLocation(projectedPoint.getX()/ratio, projectedPoint.getY());
183         Point2D translatedBack = new Point2D.Double(projectedPoint.getX()+viewCenter.getX(),
184                 projectedPoint.getY()+viewCenter.getY());
185         return translatedBack;
186     }
187     
188     /**
189      * override base class to un-project the fisheye effect
190      */
191     private Point2D _inverseTransform(Point2D viewPoint) {
192     	
193         viewPoint = delegate.inverseTransform(viewPoint);
194         Point2D viewCenter = getViewCenter();
195         double viewRadius = getViewRadius();
196         double ratio = getRatio();
197         double dx = viewPoint.getX() - viewCenter.getX();
198         double dy = viewPoint.getY() - viewCenter.getY();
199         // factor out ellipse
200         dx *= ratio;
201 
202         Point2D pointFromCenter = new Point2D.Double(dx, dy);
203         
204         PolarPoint polar = PolarPoint.cartesianToPolar(pointFromCenter);
205 
206         double radius = polar.getRadius();
207         if(radius > viewRadius) return viewPoint;//elegate.inverseTransform(viewPoint);
208         
209         radius /= viewRadius;
210         radius = Math.abs(Math.tan(radius));
211         radius /= Math.PI/2;
212         radius *= viewRadius;
213         double mag = Math.tan(Math.PI/2*magnification);
214         radius /= mag;
215         polar.setRadius(radius);
216         Point2D projectedPoint = PolarPoint.polarToCartesian(polar);
217         projectedPoint.setLocation(projectedPoint.getX()/ratio, projectedPoint.getY());
218         Point2D translatedBack = new Point2D.Double(projectedPoint.getX()+viewCenter.getX(),
219                 projectedPoint.getY()+viewCenter.getY());
220         return translatedBack;
221         //delegate.inverseTransform(translatedBack);
222     }
223 }