001: package prefuse.action.distortion;
002:
003: import java.awt.geom.Point2D;
004: import java.awt.geom.Rectangle2D;
005:
006: /**
007: * <p>
008: * Computes a graphical fisheye distortion of a graph view. This distortion
009: * allocates more space to items near the layout anchor and less space to
010: * items further away, magnifying space near the anchor and demagnifying
011: * distant space in a continuous fashion.
012: * </p>
013: *
014: * <p>
015: * For more details on this form of transformation, see Manojit Sarkar and
016: * Marc H. Brown, "Graphical Fisheye Views of Graphs", in Proceedings of
017: * CHI'92, Human Factors in Computing Systems, p. 83-91, 1992. Available
018: * online at <a href="http://citeseer.ist.psu.edu/sarkar92graphical.html">
019: * http://citeseer.ist.psu.edu/sarkar92graphical.html</a>.
020: * </p>
021: *
022: * @author <a href="http://jheer.org">jeffrey heer</a>
023: */
024: public class FisheyeDistortion extends Distortion {
025:
026: private double dx, dy; // distortion factors
027: private double sz = 3.0; // size factor
028:
029: /**
030: * Create a new FisheyeDistortion with default distortion factor.
031: */
032: public FisheyeDistortion() {
033: this (4);
034: }
035:
036: /**
037: * Create a new FisheyeDistortion with the given distortion factor
038: * for use along both the x and y directions.
039: * @param dfactor the distortion factor (same for both axes)
040: */
041: public FisheyeDistortion(double dfactor) {
042: this (dfactor, dfactor);
043: }
044:
045: /**
046: * Create a new FisheyeDistortion with the given distortion factors
047: * along the x and y directions.
048: * @param xfactor the distortion factor along the x axis
049: * @param yfactor the distortion factor along the y axis
050: */
051: public FisheyeDistortion(double xfactor, double yfactor) {
052: super ();
053: dx = xfactor;
054: dy = yfactor;
055: m_distortX = dx > 0;
056: m_distortY = dy > 0;
057: }
058:
059: /**
060: * Returns the distortion factor for the x-axis.
061: * @return returns the distortion factor for the x-axis.
062: */
063: public double getXDistortionFactor() {
064: return dx;
065: }
066:
067: /**
068: * Sets the distortion factor for the x-axis.
069: * @param d The distortion factor to set.
070: */
071: public void setXDistortionFactor(double d) {
072: dx = d;
073: m_distortX = dx > 0;
074: }
075:
076: /**
077: * Returns the distortion factor for the y-axis.
078: * @return returns the distortion factor for the y-axis.
079: */
080: public double getYDistortionFactor() {
081: return dy;
082: }
083:
084: /**
085: * Sets the distortion factor for the y-axis.
086: * @param d The distortion factor to set.
087: */
088: public void setYDistortionFactor(double d) {
089: dy = d;
090: m_distortY = dy > 0;
091: }
092:
093: /**
094: * @see prefuse.action.distortion.Distortion#distortX(double, java.awt.geom.Point2D, java.awt.geom.Rectangle2D)
095: */
096: protected double distortX(double x, Point2D anchor,
097: Rectangle2D bounds) {
098: return fisheye(x, anchor.getX(), dx, bounds.getMinX(), bounds
099: .getMaxX());
100: }
101:
102: /**
103: * @see prefuse.action.distortion.Distortion#distortY(double, java.awt.geom.Point2D, java.awt.geom.Rectangle2D)
104: */
105: protected double distortY(double y, Point2D anchor,
106: Rectangle2D bounds) {
107: return fisheye(y, anchor.getY(), dy, bounds.getMinY(), bounds
108: .getMaxY());
109: }
110:
111: /**
112: * @see prefuse.action.distortion.Distortion#distortSize(java.awt.geom.Rectangle2D, double, double, java.awt.geom.Point2D, java.awt.geom.Rectangle2D)
113: */
114: protected double distortSize(Rectangle2D bbox, double x, double y,
115: Point2D anchor, Rectangle2D bounds) {
116: if (!m_distortX && !m_distortY)
117: return 1.;
118: double fx = 1, fy = 1;
119:
120: if (m_distortX) {
121: double ax = anchor.getX();
122: double minX = bbox.getMinX(), maxX = bbox.getMaxX();
123: double xx = (Math.abs(minX - ax) > Math.abs(maxX - ax) ? minX
124: : maxX);
125: if (xx < bounds.getMinX() || xx > bounds.getMaxX())
126: xx = (xx == minX ? maxX : minX);
127: fx = fisheye(xx, ax, dx, bounds.getMinX(), bounds.getMaxX());
128: fx = Math.abs(x - fx) / bbox.getWidth();
129: }
130:
131: if (m_distortY) {
132: double ay = anchor.getY();
133: double minY = bbox.getMinY(), maxY = bbox.getMaxY();
134: double yy = (Math.abs(minY - ay) > Math.abs(maxY - ay) ? minY
135: : maxY);
136: if (yy < bounds.getMinY() || yy > bounds.getMaxY())
137: yy = (yy == minY ? maxY : minY);
138: fy = fisheye(yy, ay, dy, bounds.getMinY(), bounds.getMaxY());
139: fy = Math.abs(y - fy) / bbox.getHeight();
140: }
141:
142: double sf = (!m_distortY ? fx : (!m_distortX ? fy : Math.min(
143: fx, fy)));
144: if (Double.isInfinite(sf) || Double.isNaN(sf)) {
145: return 1.;
146: } else {
147: return sz * sf;
148: }
149: }
150:
151: private double fisheye(double x, double a, double d, double min,
152: double max) {
153: if (d != 0) {
154: boolean left = x < a;
155: double v, m = (left ? a - min : max - a);
156: if (m == 0)
157: m = max - min;
158: v = Math.abs(x - a) / m;
159: v = (d + 1) / (d + (1 / v));
160: return (left ? -1 : 1) * m * v + a;
161: } else {
162: return x;
163: }
164: }
165:
166: } // end of class FisheyeDistortion
|