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;
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   *
31   */
32  public class HyperbolicTransformer extends LensTransformer implements MutableTransformer {
33  
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
39       */
40      public HyperbolicTransformer(Component component) {
41          this(component, new MutableAffineTransformer());
42      }
43      /**
44       * create an instance with a possibly shared transform
45       * @param component
46       * @param delegate
47       */
48      public HyperbolicTransformer(Component component, MutableTransformer delegate) {
49      		super(component, delegate);
50     }
51      
52      /**
53       * override base class transform to project the fisheye effect
54       */
55      public Point2D transform(Point2D graphPoint) {
56          if(graphPoint == null) return null;
57          Point2D viewCenter = getViewCenter();
58          double viewRadius = getViewRadius();
59          double ratio = getRatio();
60          // transform the point from the graph to the view
61          Point2D viewPoint = delegate.transform(graphPoint);
62          // calculate point from center
63          double dx = viewPoint.getX() - viewCenter.getX();
64          double dy = viewPoint.getY() - viewCenter.getY();
65          // factor out ellipse
66          dx *= ratio;
67          Point2D pointFromCenter = new Point2D.Double(dx, dy);
68          
69          PolarPoint polar = PolarPoint.cartesianToPolar(pointFromCenter);
70          double theta = polar.getTheta();
71          double radius = polar.getRadius();
72          if(radius > viewRadius) return viewPoint;
73          
74          double mag = Math.tan(Math.PI/2*magnification);
75          radius *= mag;
76          
77          radius = Math.min(radius, viewRadius);
78          radius /= viewRadius;
79          radius *= Math.PI/2;
80          radius = Math.abs(Math.atan(radius));
81          radius *= viewRadius;
82          Point2D projectedPoint = PolarPoint.polarToCartesian(theta, radius);
83          projectedPoint.setLocation(projectedPoint.getX()/ratio, projectedPoint.getY());
84          Point2D translatedBack = new Point2D.Double(projectedPoint.getX()+viewCenter.getX(),
85                  projectedPoint.getY()+viewCenter.getY());
86          return translatedBack;
87      }
88      
89      /**
90       * override base class to un-project the fisheye effect
91       */
92      public Point2D inverseTransform(Point2D viewPoint) {
93          
94          Point2D viewCenter = getViewCenter();
95          double viewRadius = getViewRadius();
96          double ratio = getRatio();
97          double dx = viewPoint.getX() - viewCenter.getX();
98          double dy = viewPoint.getY() - viewCenter.getY();
99          // factor out ellipse
100         dx *= ratio;
101 
102         Point2D pointFromCenter = new Point2D.Double(dx, dy);
103         
104         PolarPoint polar = PolarPoint.cartesianToPolar(pointFromCenter);
105 
106         double radius = polar.getRadius();
107         if(radius > viewRadius) return delegate.inverseTransform(viewPoint);
108         
109         radius /= viewRadius;
110         radius = Math.abs(Math.tan(radius));
111         radius /= Math.PI/2;
112         radius *= viewRadius;
113         double mag = Math.tan(Math.PI/2*magnification);
114         radius /= mag;
115         polar.setRadius(radius);
116         Point2D projectedPoint = PolarPoint.polarToCartesian(polar);
117         projectedPoint.setLocation(projectedPoint.getX()/ratio, projectedPoint.getY());
118         Point2D translatedBack = new Point2D.Double(projectedPoint.getX()+viewCenter.getX(),
119                 projectedPoint.getY()+viewCenter.getY());
120         return delegate.inverseTransform(translatedBack);
121     }
122 }