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;
10  
11  import java.awt.Component;
12  import java.awt.geom.Point2D;
13  
14  import edu.uci.ics.jung.algorithms.layout.PolarPoint;
15  
16  /**
17   * HyperbolicTransformer wraps a MutableAffineTransformer and modifies
18   * the transform and inverseTransform methods so that they create a
19   * fisheye projection of the graph points, with points near the
20   * center spread out and points near the edges collapsed onto the
21   * circumference of an ellipse.
22   * 
23   * HyperbolicTransformer is not an affine transform, but it uses an
24   * affine transform to cause translation, scaling, rotation, and shearing
25   * while applying a non-affine hyperbolic filter in its transform and
26   * inverseTransform methods.
27   * 
28   * @author Tom Nelson 
29   */
30  public class HyperbolicTransformer extends LensTransformer implements MutableTransformer {
31      /**
32       * create an instance, setting values from the passed component
33       * and registering to listen for size changes on the component
34       * @param component the component used for rendering
35       */
36      public HyperbolicTransformer(Component component) {
37          this(component, new MutableAffineTransformer());
38      }
39  
40      /**
41       * Create an instance with a possibly shared transform.
42       * 
43       * @param component the component used for rendering
44       * @param delegate the transformer to use
45       */
46      public HyperbolicTransformer(Component component, MutableTransformer delegate) {
47      		super(component, delegate);
48     }
49      
50      /**
51       * override base class transform to project the fisheye effect
52       */
53      public Point2D transform(Point2D graphPoint) {
54          if(graphPoint == null) return null;
55          Point2D viewCenter = getViewCenter();
56          double viewRadius = getViewRadius();
57          double ratio = getRatio();
58          // transform the point from the graph to the view
59          Point2D viewPoint = delegate.transform(graphPoint);
60          // calculate point from center
61          double dx = viewPoint.getX() - viewCenter.getX();
62          double dy = viewPoint.getY() - viewCenter.getY();
63          // factor out ellipse
64          dx *= ratio;
65          Point2D pointFromCenter = new Point2D.Double(dx, dy);
66          
67          PolarPoint polar = PolarPoint.cartesianToPolar(pointFromCenter);
68          double theta = polar.getTheta();
69          double radius = polar.getRadius();
70          if(radius > viewRadius) return viewPoint;
71          
72          double mag = Math.tan(Math.PI/2*magnification);
73          radius *= mag;
74          
75          radius = Math.min(radius, viewRadius);
76          radius /= viewRadius;
77          radius *= Math.PI/2;
78          radius = Math.abs(Math.atan(radius));
79          radius *= viewRadius;
80          Point2D projectedPoint = PolarPoint.polarToCartesian(theta, radius);
81          projectedPoint.setLocation(projectedPoint.getX()/ratio, projectedPoint.getY());
82          Point2D translatedBack = new Point2D.Double(projectedPoint.getX()+viewCenter.getX(),
83                  projectedPoint.getY()+viewCenter.getY());
84          return translatedBack;
85      }
86      
87      /**
88       * override base class to un-project the fisheye effect
89       */
90      public Point2D inverseTransform(Point2D viewPoint) {
91          
92          Point2D viewCenter = getViewCenter();
93          double viewRadius = getViewRadius();
94          double ratio = getRatio();
95          double dx = viewPoint.getX() - viewCenter.getX();
96          double dy = viewPoint.getY() - viewCenter.getY();
97          // factor out ellipse
98          dx *= ratio;
99  
100         Point2D pointFromCenter = new Point2D.Double(dx, dy);
101         
102         PolarPoint polar = PolarPoint.cartesianToPolar(pointFromCenter);
103 
104         double radius = polar.getRadius();
105         if(radius > viewRadius) return delegate.inverseTransform(viewPoint);
106         
107         radius /= viewRadius;
108         radius = Math.abs(Math.tan(radius));
109         radius /= Math.PI/2;
110         radius *= viewRadius;
111         double mag = Math.tan(Math.PI/2*magnification);
112         radius /= mag;
113         polar.setRadius(radius);
114         Point2D projectedPoint = PolarPoint.polarToCartesian(polar);
115         projectedPoint.setLocation(projectedPoint.getX()/ratio, projectedPoint.getY());
116         Point2D translatedBack = new Point2D.Double(projectedPoint.getX()+viewCenter.getX(),
117                 projectedPoint.getY()+viewCenter.getY());
118         return delegate.inverseTransform(translatedBack);
119     }
120 }