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