001: /*
002: JOpenChart Java Charting Library and Toolkit
003: Copyright (C) 2001 Sebastian Müller
004: http://jopenchart.sourceforge.net
005:
006: This library is free software; you can redistribute it and/or
007: modify it under the terms of the GNU Lesser General Public
008: License as published by the Free Software Foundation; either
009: version 2.1 of the License, or (at your option) any later version.
010:
011: This library is distributed in the hope that it will be useful,
012: but WITHOUT ANY WARRANTY; without even the implied warranty of
013: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: Lesser General Public License for more details.
015:
016: You should have received a copy of the GNU Lesser General Public
017: License along with this library; if not, write to the Free Software
018: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019:
020: ChartUtilities.java
021: Created on 21. September 2001, 17:42
022: */
023:
024: package de.progra.charting;
025:
026: import java.util.*;
027:
028: /**
029: * This class offers multiple static methods to perform mathematical
030: * operations concerning the Chart, e.g. methods for rounding the minimal and
031: * maximal x-values gracefully.
032: * @author mueller
033: * @version 1.0
034: */
035: public class ChartUtilities {
036:
037: /** This method calculates the optimal rounding for the minimal and
038: * maximal ChartModel values. It computes the difference of the
039: * minimal and maximal value and rounds the values min and max according
040: * to the exponent of the difference.
041: * @param min the minimal column value of the ChartDataModel
042: * @param max the maximal column value of the ChartDataModel
043: * @return a double[] with the rounded minimal value at index 0 and
044: * the maximal value at index 1.
045: */
046: public static double[] performAutoScale(double min, double max) {
047: double[] d = new double[2]; // d[0] = min d[1] = max
048:
049: double diff = max - min;
050:
051: d[0] = floor(min, exp(diff));
052: d[1] = ceil(max, exp(diff));
053:
054: return d;
055: }
056:
057: /** Calculates the best tick spacing for the rounded minimal and maximal
058: * values.
059: * @param min the rounded minimal value
060: * @param max the rounded maximal value
061: * @return the spacing of ticks on the x-axis.
062: */
063: public static double calculateTickSpacing(double min, double max) {
064: double spacing = 1.0;
065:
066: double diff = max - min;
067:
068: int exp = exp(diff);
069:
070: exp--;
071:
072: spacing = 1.0 * Math.pow(10.0, (double) exp);
073:
074: // Currently, only every second tick gets a label, so 20 - 40 ticks are fine.
075: // This should be reduced in a loop probably.
076: if ((diff / spacing) < 20)
077: return 0.5 * spacing;
078: else if ((diff / spacing) > 40)
079: return 2 * spacing;
080: else
081: return spacing;
082: }
083:
084: /** This function performs a polynomial interpolation using a set of
085: * given x and y values. It uses Neville's interpolation algorithm.
086: * @param xa the array of known x-values
087: * @param ya the array of known y-values
088: * @param x the x value for which the y value will be computed
089: * @return the corresponding y value
090: */
091: public static double interpolate(double xa[], double ya[], double x) {
092: /*
093: Given arrays xa[1..n] and ya[1..n], and given a value x,
094: this routine returns a value y.
095: If P(x) is the polynomial of degree N ? 1
096: such that P(xa[i]) = ya[i];
097: i = 1...n, then the returned value y = P(x).
098: */
099:
100: if (xa.length != ya.length || xa.length == 0 || ya.length == 0) {
101: System.out.println("** Invalid Parameter");
102: return Double.NaN;
103: }
104:
105: int n = xa.length;
106: double y = 0.0;
107: double dy = 0.0;
108:
109: int i, m, ns = 1;
110: double den, dif, dift, ho, hp, w;
111: double[] c = new double[n];
112: double[] d = new double[n];
113: dif = Math.abs(x - xa[0]);
114:
115: for (i = 0; i < n; i++) { // Here we find the index ns of the closest table entry,
116: if ((dift = Math.abs(x - xa[i])) < dif) {
117: ns = i;
118: dif = dift;
119: }
120: c[i] = ya[i]; // and initialize the tableau of c's and d's.
121: d[i] = ya[i];
122: }
123:
124: y = ya[ns--]; // This is the initial approximation to y.
125: //System.out.println("** y ~ "+y);
126:
127: for (m = 0; m < n - 1; m++) { // For each column of the tableau,
128: for (i = 0; i < n - m - 1; i++) { // we loop over the current c's and d's and update them.
129:
130: //System.out.println("** m = "+m+", i = "+i);
131: ho = xa[i] - x;
132: hp = xa[i + m + 1] - x;
133: w = c[i + 1] - d[i];
134:
135: if ((den = ho - hp) == 0.0) {
136: return Double.NaN;
137: }
138: // This error can occur only if two input xa's are (to within roundof identical.
139:
140: //System.out.println("** ho = "+ho+", hp = "+hp);
141:
142: den = w / den;
143: d[i] = hp * den; // Here the c's and d's are updated.
144: c[i] = ho * den;
145: //System.out.println("** c[i] = "+c[i]+", d[i] = "+d[i]);
146: }
147:
148: y += (dy = (2 * (ns + 1) < (n - m) ? c[ns + 1] : d[ns--]));
149: //System.out.println("** dy = "+dy+", y = "+y);
150:
151: /*
152: After each column in the tableau is completed, we decide which correction, c or d,
153: we want to add to our accumulating value of y, i.e., which path to take through the
154: tableau forking up or down. We do this in such a way as to take the most "straight
155: line" route through the tableau to its apex, updating ns accordingly to keep track of
156: where we are. This route keeps the partial approximations centered (insofar as possible)
157: on the target x. The last dy added is thus the error indication.
158: */
159: }
160:
161: return y;
162: }
163:
164: /** This method returns the largest double value that is smaller than
165: * <code> d = x * 10<sup>exp</sup></code> where x is rounded down to
166: * the closest integer.
167: * @param d the double value to be rounded
168: * @param exp the exponent of 10 to which d should be rounded
169: * @return <code> Math.floor(x) * 10<sup>exp</sup></code>
170: */
171: public static double floor(double d, int exp) {
172: double x = 1.0 * Math.pow(10.0, (double) exp);
173:
174: return Math.floor(d / x) * x;
175: }
176:
177: /** This method returns the smallest double value that is smaller than
178: * <code> d = x * 10<sup>exp</exp></code> where x is rounded up to
179: * the closest integer.
180: * @param d the double value to be rounded
181: * @param exp the exponent of 10 to which d should be rounded
182: * @return <code> Math.ceil(x) * 10<sup>exp</sup></code>
183: */
184: public static double ceil(double d, int exp) {
185: double x = 1.0 * Math.pow(10.0, (double) exp);
186:
187: return Math.ceil(d / x) * x;
188: }
189:
190: /** A double value can be represented like
191: * <code>d = x * 10<sup>exp</sup></code> and this method returns
192: * the value of exp for a double d.
193: * @param d the double value
194: * @return the exponent of 10
195: */
196: public static int exp(double d) {
197: int exp = 0;
198: boolean positive = (d <= -1 || d >= 1);
199:
200: while ((d <= -10) || (d >= 10) || ((d > -1) && (d < 1))) {
201: if (positive) {
202: d /= 10;
203: exp++;
204: } else {
205: d *= 10;
206: exp--;
207: }
208: }
209:
210: return exp;
211: }
212:
213: /** Transforms a two-dimensional array of primitives
214: * to an array of Numbers.
215: */
216: public static Number[][] transformArray(int[][] data) {
217: Number[][] n = new Number[data.length][data[0].length];
218:
219: for (int i = 0; i < data.length; i++)
220: for (int j = 0; j < data[0].length; j++)
221: n[i][j] = new Integer(data[i][j]);
222:
223: return n;
224: }
225:
226: /** Transforms a two-dimensional array of primitives
227: * to an array of Numbers.
228: */
229: public static Number[][] transformArray(double[][] data) {
230: Number[][] n = new Number[data.length][data[0].length];
231:
232: for (int i = 0; i < data.length; i++)
233: for (int j = 0; j < data[0].length; j++)
234: n[i][j] = new Double(data[i][j]);
235:
236: return n;
237: }
238:
239: /** Transforms an array of primitives
240: * to an array of Numbers.
241: */
242: public static Number[] transformArray(double[] data) {
243: Number[] n = new Number[data.length];
244:
245: for (int i = 0; i < data.length; i++)
246: n[i] = new Double(data[i]);
247:
248: return n;
249: }
250:
251: /** Transforms an array of primitives
252: * to an array of Numbers.
253: */
254: public static Number[] transformArray(int[] data) {
255: Number[] n = new Number[data.length];
256:
257: for (int i = 0; i < data.length; i++)
258: n[i] = new Integer(data[i]);
259:
260: return n;
261: }
262:
263: /** Adds a two-dimensional array to a TreeSet. */
264: public static void addDataToSet(TreeSet set, Number[][] data) {
265: for (int i = 0; i < data.length; i++) {
266: set.addAll(Arrays.asList(data[i]));
267: }
268: }
269:
270: /** A test routine. */
271: public static void main(String[] args) {
272: double min = -0.00337;
273: double max = 0.00745;
274:
275: double[] d = performAutoScale(min, max);
276:
277: System.out.println("** AutoScaling: (" + min + ", " + max
278: + ") -> (" + d[0] + ", " + d[1] + ")");
279:
280: double s = calculateTickSpacing(d[0], d[1]);
281:
282: System.out.print("** Ticks: ");
283: for (double i = d[0]; i <= d[1]; i += s)
284: System.out.print(" " + i + " ");
285: System.out.println();
286:
287: System.out.println("** Performing interpolation for 4*x^2");
288: System.out.println("** Given values [-4, 64], [0, 0], [3, 36]");
289:
290: double xa[] = { -4.0, 0.0, 3.0 };
291: double ya[] = { 64.0, 0.0, 36.0 };
292:
293: System.out.print("** Calculating values");
294: //double f = interpolate(xa, ya, 1.0);
295: //System.out.println("** f(1) = "+f);
296:
297: for (double i = -5.0; i < 6.0; i += 0.5) {
298: System.out.print("[" + i + ", " + interpolate(xa, ya, i)
299: + "]");
300: }
301:
302: System.out.println();
303:
304: System.out
305: .println("** Performing interpolation for 5 * x^3 - 4 * x^2 + 2 * x - 5");
306: System.out
307: .println("** Given values [-5, -740], [0, -5], [1, -2], [5, 530]");
308:
309: double xb[] = { -5.0, 0.0, 1.0, 5.0 };
310: double yb[] = { -740.0, -5.0, -2.0, 530.0 };
311:
312: System.out.print("** Calculating values ");
313:
314: for (double i = -5.0; i < 6.0; i += 0.5) {
315: System.out.print("[" + i + ", " + interpolate(xb, yb, i)
316: + "]");
317: }
318:
319: System.out.println();
320:
321: }
322: }
|