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 bifocal distortion of space, magnifying a focus region of space
009: * and uniformly demagnifying the rest of the space. The affect is akin to
010: * passing a magnifying glass over the data.
011: * </p>
012: *
013: * <p>
014: * For more details on this form of transformation, see Y. K. Leung and
015: * M. D. Apperley, "A Review and Taxonomy of Distortion-Oriented Presentation
016: * Techniques", in Transactions of Computer-Human Interaction (TOCHI),
017: * 1(2): 126-160 (1994). Available online at
018: * <a href="portal.acm.org/citation.cfm?id=180173&dl=ACM">
019: * portal.acm.org/citation.cfm?id=180173&dl=ACM</a>.
020: * </p>
021: *
022: * @author <a href="http://jheer.org">jeffrey heer</a>
023: */
024: public class BifocalDistortion extends Distortion {
025:
026: private double rx, ry; // magnification ranges
027: private double mx, my; // magnification factor
028:
029: /**
030: * Create a new BifocalDistortion with default range and magnification.
031: */
032: public BifocalDistortion() {
033: this (0.1, 3);
034: }
035:
036: /**
037: * <p>Create a new BifocalDistortion with the specified range and
038: * magnification. The same range and magnification is used for both
039: * axes.</p>
040: *
041: * <p><strong>NOTE:</strong>if the range value times the magnification
042: * value is greater than 1, the resulting distortion can exceed the
043: * display bounds.</p>
044: *
045: * @param range the range around the focus that should be magnified. This
046: * specifies the size of the magnified focus region, and should be in the
047: * range of 0 to 1, 0 being no magnification range and 1 being the whole
048: * display.
049: * @param mag how much magnification should be used in the focal area
050: */
051: public BifocalDistortion(double range, double mag) {
052: this (range, mag, range, mag);
053: } //
054:
055: /**
056: * <p>Create a new BifocalDistortion with the specified range and
057: * magnification along both axes.</p>
058: *
059: * <p><strong>NOTE:</strong>if the range value times the magnification
060: * value is greater than 1, the resulting distortion can exceed the
061: * display bounds.</p>
062: *
063: * @param xrange the range around the focus that should be magnified along
064: * the x direction. This specifies the horizontal size of the magnified
065: * focus region, and should be a value between 0 and 1, 0 indicating no
066: * focus region and 1 indicating the whole display.
067: * @param xmag how much magnification along the x direction should be used
068: * in the focal area
069: * @param yrange the range around the focus that should be magnified along
070: * the y direction. This specifies the vertical size of the magnified
071: * focus region, and should be a value between 0 and 1, 0 indicating no
072: * focus region and 1 indicating the whole display.
073: * @param ymag how much magnification along the y direction should be used
074: * in the focal area
075: */
076: public BifocalDistortion(double xrange, double xmag, double yrange,
077: double ymag) {
078: rx = xrange;
079: mx = xmag;
080: ry = yrange;
081: my = ymag;
082: m_distortX = !(rx == 0 || mx == 1.0);
083: m_distortY = !(ry == 0 || my == 1.0);
084: }
085:
086: /**
087: * @see prefuse.action.distortion.Distortion#distortX(double, java.awt.geom.Point2D, java.awt.geom.Rectangle2D)
088: */
089: protected double distortX(double x, Point2D a, Rectangle2D b) {
090: return bifocal(x, a.getX(), rx, mx, b.getMinX(), b.getMaxX());
091: }
092:
093: /**
094: * @see prefuse.action.distortion.Distortion#distortY(double, java.awt.geom.Point2D, java.awt.geom.Rectangle2D)
095: */
096: protected double distortY(double y, Point2D a, Rectangle2D b) {
097: return bifocal(y, a.getY(), ry, my, b.getMinY(), b.getMaxY());
098: }
099:
100: /**
101: * @see prefuse.action.distortion.Distortion#distortSize(java.awt.geom.Rectangle2D, double, double, java.awt.geom.Point2D, java.awt.geom.Rectangle2D)
102: */
103: protected double distortSize(Rectangle2D bbox, double x, double y,
104: Point2D anchor, Rectangle2D bounds) {
105: boolean xmag = false, ymag = false;
106: double m;
107:
108: if (m_distortX) {
109: double cx = bbox.getCenterX(), ax = anchor.getX();
110: double minX = bounds.getMinX(), maxX = bounds.getMaxX();
111: m = (cx < ax ? ax - minX : maxX - ax);
112: if (m == 0)
113: m = maxX - minX;
114: if (Math.abs(cx - ax) <= rx * m)
115: xmag = true;
116: }
117:
118: if (m_distortY) {
119: double cy = bbox.getCenterY(), ay = anchor.getY();
120: double minY = bounds.getMinY(), maxY = bounds.getMaxY();
121: m = (cy < ay ? ay - minY : maxY - ay);
122: if (m == 0)
123: m = maxY - minY;
124: if (Math.abs(cy - ay) <= ry * m)
125: ymag = true;
126: }
127:
128: if (xmag && !m_distortY) {
129: return mx;
130: } else if (ymag && !m_distortX) {
131: return my;
132: } else if (xmag && ymag) {
133: return Math.min(mx, my);
134: } else {
135: return Math.min((1 - rx * mx) / (1 - rx), (1 - ry * my)
136: / (1 - ry));
137: }
138: }
139:
140: private double bifocal(double x, double a, double r, double mag,
141: double min, double max) {
142: double m = (x < a ? a - min : max - a);
143: if (m == 0)
144: m = max - min;
145: double v = x - a, s = m * r;
146: if (Math.abs(v) <= s) { // in focus
147: return x = v * mag + a;
148: } else { // out of focus
149: double bx = r * mag;
150: x = ((Math.abs(v) - s) / m) * ((1 - bx) / (1 - r));
151: return (v < 0 ? -1 : 1) * m * (x + bx) + a;
152: }
153: }
154:
155: /**
156: * Returns the magnification factor for the x-axis.
157: * @return Returns the magnification factor for the x-axis.
158: */
159: public double getXMagnification() {
160: return mx;
161: }
162:
163: /**
164: * Sets the magnification factor for the x-axis.
165: * @param mx The magnification factor for the x-axis.
166: */
167: public void setXMagnification(double mx) {
168: this .mx = mx;
169: }
170:
171: /**
172: * Returns the magnification factor for the y-axis.
173: * @return Returns the magnification factor for the y-axis.
174: */
175: public double getYMagnification() {
176: return my;
177: }
178:
179: /**
180: * Sets the magnification factor for the y-axis.
181: * @param my The magnification factor for the y-axis.
182: */
183: public void setYMagnification(double my) {
184: this .my = my;
185: }
186:
187: /**
188: * Returns the range of the focal area along the x-axis.
189: * @return Returns the range of the focal area along the x-axis.
190: */
191: public double getXRange() {
192: return rx;
193: }
194:
195: /**
196: * Sets the range of the focal area along the x-axis.
197: * @param rx The focal range for the x-axis, a value between 0 and 1.
198: */
199: public void setXRange(double rx) {
200: this .rx = rx;
201: }
202:
203: /**
204: * Returns the range of the focal area along the y-axis.
205: * @return Returns the range of the focal area along the y-axis.
206: */
207: public double getYRange() {
208: return ry;
209: }
210:
211: /**
212: * Sets the range of the focal area along the y-axis.
213: * @param ry The focal range for the y-axis, a value between 0 and 1.
214: */
215: public void setYRange(double ry) {
216: this .ry = ry;
217: }
218:
219: } // end of class BifocalDistortion
|