Annotation of OpenXM_contrib/gnuplot/interpol.c, Revision 1.1.1.3
1.1 maekawa 1: #ifndef lint
1.1.1.3 ! ohara 2: static char *RCSid = "$Id: interpol.c,v 1.6.2.2 2002/02/01 18:41:05 broeker Exp $";
1.1 maekawa 3: #endif
4:
5: /* GNUPLOT - interpol.c */
6:
7: /*[
8: * Copyright 1986 - 1993, 1998 Thomas Williams, Colin Kelley
9: *
10: * Permission to use, copy, and distribute this software and its
11: * documentation for any purpose with or without fee is hereby granted,
12: * provided that the above copyright notice appear in all copies and
13: * that both that copyright notice and this permission notice appear
14: * in supporting documentation.
15: *
16: * Permission to modify the software is granted, but not the right to
17: * distribute the complete modified source code. Modifications are to
18: * be distributed as patches to the released version. Permission to
19: * distribute binaries produced by compiling modified sources is granted,
20: * provided you
21: * 1. distribute the corresponding source modifications from the
22: * released version in the form of a patch file along with the binaries,
23: * 2. add special version identification to distinguish your version
24: * in addition to the base release version number,
25: * 3. provide your name and address as the primary contact for the
26: * support of your modified version, and
27: * 4. retain our contact information in regard to use of the base
28: * software.
29: * Permission to distribute the released version of the source code along
30: * with corresponding source modifications in the form of a patch file is
31: * granted with same provisions 2 through 4 for binary distributions.
32: *
33: * This software is provided "as is" without express or implied warranty
34: * to the extent permitted by applicable law.
35: ]*/
36:
37:
38: /*
39: * C-Source file identification Header
40: *
41: * This file belongs to a project which is:
42: *
43: * done 1993 by MGR-Software, Asgard (Lars Hanke)
44: * written by Lars Hanke
45: *
46: * Contact me via:
47: *
48: * InterNet: mgr@asgard.bo.open.de
49: * FIDO: Lars Hanke @ 2:243/4802.22 (as long as they keep addresses)
50: *
51: **************************************************************************
52: *
53: * Project: gnuplot
54: * Module:
55: * File: interpol.c
56: *
57: * Revisor: Lars Hanke
58: * Revised: 26/09/93
59: * Revision: 1.0
60: *
61: **************************************************************************
62: *
63: * LEGAL
64: * This module is part of gnuplot and distributed under whatever terms
65: * gnuplot is or will be published, unless exclusive rights are claimed.
66: *
67: * DESCRIPTION
68: * Supplies 2-D data interpolation and approximation routines
69: *
70: * IMPORTS
71: * plot.h
72: * - cp_extend()
73: * - structs: curve_points, coordval, coordinate
74: *
75: * setshow.h
76: * - samples, is_log_x, base_log_x, xmin, xmax, autoscale_lx
77: * - plottypes
78: *
79: * proto.h
80: * - solve_tri_diag()
81: * - typedef tri_diag
82: *
83: * EXPORTS
84: * gen_interp()
85: * sort_points()
86: * cp_implode()
87: *
88: * BUGS and TODO
89: * I would really have liked to use Gershon Elbers contouring code for
90: * all the stuff done here, but I failed. So I used my own code.
91: * If somebody is able to consolidate Gershon's code for this purpose
92: * a lot of gnuplot users would be very happy - due to memory problems.
93: *
94: **************************************************************************
95: *
96: * HISTORY
97: * Changes:
98: * Nov 24, 1995 Markus Schuh (M.Schuh@meteo.uni-koeln.de):
99: * changed the algorithm for csplines
100: * added algorithm for approximation csplines
101: * copied point storage and range fix from plot2d.c
102: *
103: * Dec 12, 1995 David Denholm
104: * oops - at the time this is called, stored co-ords are
105: * internal (ie maybe log of data) but min/max are in
106: * user co-ordinates.
107: * Work with min and max of internal co-ords, and
108: * check at the end whether external min and max need to
109: * be increased. (since samples is typically 100 ; we
110: * dont want to take more logs than necessary)
111: * Also, need to take into account which axes are active
112: *
113: * Jun 30, 1996 Jens Emmerich
114: * implemented handling of UNDEFINED points
115: */
116:
117: #include "plot.h"
118: #include "setshow.h"
119:
120:
121: /* in order to support multiple axes, and to simplify ranging in
122: * parametric plots, we use arrays to store some things. For 2d plots,
123: * elements are z=0,y1=1,x1=2,z2=4,y2=5,x2=6 these are given symbolic
124: * names in plot.h
125: */
126:
127: extern double min_array[AXIS_ARRAY_SIZE];
128: extern double max_array[AXIS_ARRAY_SIZE];
129: extern int auto_array[AXIS_ARRAY_SIZE];
130: extern TBOOLEAN log_array[AXIS_ARRAY_SIZE];
131: extern double base_array[AXIS_ARRAY_SIZE];
132: extern double log_base_array[AXIS_ARRAY_SIZE];
133:
134:
135: #define Inc_c_token if (++c_token >= num_tokens) \
136: int_error ("Syntax error", c_token);
137:
138:
139: /*
140: * IMHO, code is getting too cluttered with repeated chunks of
141: * code. Some macros to simplify, I hope.
142: *
143: * do { } while(0) is comp.lang.c recommendation for complex macros
144: * also means that break can be specified as an action, and it will
145: *
146: */
147:
148:
149: /* store VALUE or log(VALUE) in STORE, set TYPE as appropriate
150: * Do OUT_ACTION or UNDEF_ACTION as appropriate
151: * adjust range provided type is INRANGE (ie dont adjust y if x is outrange
152: * VALUE must not be same as STORE
153: */
154:
155: #define STORE_AND_FIXUP_RANGE(STORE, VALUE, TYPE, MIN, MAX, AUTO, OUT_ACTION, UNDEF_ACTION)\
156: do { STORE=VALUE; \
157: if (TYPE != INRANGE) break; /* dont set y range if x is outrange, for example */ \
158: if ( VALUE<MIN ) { \
159: if (AUTO & 1) MIN=VALUE; else { TYPE=OUTRANGE; OUT_ACTION; break; } \
160: } \
161: if ( VALUE>MAX ) {\
162: if (AUTO & 2) MAX=VALUE; else { TYPE=OUTRANGE; OUT_ACTION; } \
163: } \
164: } while(0)
165:
166: #define UPDATE_RANGE(TEST,OLD,NEW,AXIS) \
167: do { if (TEST) { \
168: if (log_array[AXIS]) OLD = pow(base_array[AXIS], NEW); else OLD = NEW; \
169: } \
170: } while(0)
171:
172: /* use this instead empty macro arguments to work around NeXT cpp bug */
173: /* if this fails on any system, we might use ((void)0) */
174:
175: #define NOOP /* */
176:
177: #define spline_coeff_size 4
178: typedef double spline_coeff[spline_coeff_size];
179: typedef double five_diag[5];
180:
181: static int next_curve __PROTO((struct curve_points * plot, int *curve_start));
182: static int num_curves __PROTO((struct curve_points * plot));
183: static double *cp_binomial __PROTO((int points));
184: GP_INLINE static double s_pow __PROTO((double base, unsigned int exponent));
185: static void eval_bezier __PROTO((struct curve_points * cp, int first_point, int num_points, double sr, coordval * px, coordval * py, double *c));
186: static void do_bezier __PROTO((struct curve_points * cp, double *bc, int first_point, int num_points, struct coordinate * dest));
187: static int solve_five_diag __PROTO((five_diag m[], double r[], double x[], int n));
188: static spline_coeff *cp_approx_spline __PROTO((struct curve_points * plot, int first_point, int num_points));
189: static spline_coeff *cp_tridiag __PROTO((struct curve_points * plot, int first_point, int num_points));
190: static void do_cubic __PROTO((struct curve_points * plot, spline_coeff * sc, int first_point, int num_points, struct coordinate * dest));
1.1.1.3 ! ohara 191: int compare_points __PROTO((SORTFUNC_ARGS p1, SORTFUNC_ARGS p2));
1.1 maekawa 192:
193:
194: /*
195: * position curve_start to index the next non-UNDEFINDED point,
196: * start search at initial curve_start,
197: * return number of non-UNDEFINDED points from there on,
198: * if no more valid points are found, curve_start is set
199: * to plot->p_count and 0 is returned
200: */
201:
202: static int next_curve(plot, curve_start)
203: struct curve_points *plot;
204: int *curve_start;
205: {
206: int curve_length;
207:
208: /* Skip undefined points */
209: while (*curve_start < plot->p_count
210: && plot->points[*curve_start].type == UNDEFINED) {
211: (*curve_start)++;
212: };
213: curve_length = 0;
214: /* curve_length is first used as an offset, then the correkt # points */
215: while ((*curve_start) + curve_length < plot->p_count
216: && plot->points[(*curve_start) + curve_length].type != UNDEFINED) {
217: curve_length++;
218: };
219: return (curve_length);
220: }
221:
222:
223: /*
224: * determine the number of curves in plot->points, separated by
225: * UNDEFINED points
226: */
227:
228: static int num_curves(plot)
229: struct curve_points *plot;
230: {
231: int curves;
232: int first_point;
233: int num_points;
234:
235: first_point = 0;
236: curves = 0;
237: while ((num_points = next_curve(plot, &first_point)) > 0) {
238: curves++;
239: first_point += num_points;
240: }
241: return (curves);
242: }
243:
244:
245:
246: /*
247: * build up a cntr_struct list from curve_points
248: * this funtion is only used for the alternate entry point to
249: * Gershon's code and thus commented out
250: ***deleted***
251: */
252:
253:
1.1.1.2 maekawa 254: /* HBB 990205: rewrote the 'bezier' interpolation routine,
255: * to prevent numerical overflow and other undesirable things happening
256: * for large data files (num_data about 1000 or so), where binomial
257: * coefficients would explode, and powers of 'sr' (0 < sr < 1) become
258: * extremely small. Method used: compute logarithms of these
259: * extremely large and small numbers, and only go back to the
260: * real numbers once they've cancelled out each other, leaving
261: * a reasonable-sized one. */
262:
1.1 maekawa 263: /*
264: * cp_binomial() computes the binomial coefficients needed for BEZIER stuff
265: * and stores them into an array which is hooked to sdat.
266: * (MGR 1992)
267: */
268: static double *cp_binomial(points)
269: int points;
270: {
271: register double *coeff;
272: register int n, k;
273: int e;
274:
275: e = points; /* well we're going from k=0 to k=p_count-1 */
276: coeff = (double *) gp_alloc(e * sizeof(double), "bezier coefficients");
277:
278: n = points - 1;
279: e = n / 2;
1.1.1.2 maekawa 280: /* HBB 990205: calculate these in 'logarithmic space',
281: * as they become _very_ large, with growing n (4^n) */
282: coeff[0] = 0.0;
1.1 maekawa 283:
284: for (k = 0; k < e; k++) {
1.1.1.2 maekawa 285: coeff[k + 1] = coeff[k] + log(((double) (n-k)) / ((double) (k+1)));
1.1 maekawa 286: }
287:
288: for (k = n; k >= e; k--)
289: coeff[k] = coeff[n - k];
290:
291: return (coeff);
292: }
293:
294:
295: /* This is a subfunction of do_bezier() for BEZIER style computations.
296: * It is passed the stepration (STEP/MAXSTEPS) and the addresses of
297: * the double values holding the next x and y coordinates.
298: * (MGR 1992)
299: */
300:
301: /*
302: * well, this routine runs faster with the 68040 striptease FPU
303: * and it handles zeroes correctly - there had been some trouble with TC
304: */
305:
306: GP_INLINE static double s_pow(base, exponent)
307: double base;
308: unsigned int exponent;
309: {
310: double y;
311:
312: if (exponent == 0)
313: return (1.0);
314: if (base == 0.0)
315: return (0.0);
316:
317: /* consider i in binary = abcd
318: * x^i = x^(8a+4b+2c+d) = x^8a * x^4b * x^2b * x^d
319: * = a?x^2^2^2:1 * b?x^2^2:1 + ...
320: * so for each bit in exponent, square x, multiplying it into accumulator
321: *
322: */
323:
324: y = 1;
325: while (exponent) {
326: if (exponent & 1)
327: y *= base;
328: base *= base;
329: /* if exponent was signed, this could be trouble ! */
330: exponent >>= 1;
331: }
332:
333: return (y);
334: }
335:
1.1.1.2 maekawa 336:
1.1 maekawa 337: static void eval_bezier(cp, first_point, num_points, sr, px, py, c)
338: struct curve_points *cp;
339: int first_point; /* where to start in plot->points (to find x-range) */
340: int num_points; /* to determine end in plot->points */
341: double sr;
342: coordval *px;
343: coordval *py;
344: double *c;
345: {
346: unsigned int n = num_points - 1;
347: /* HBB 980308: added 'GPHUGE' tag for DOS */
348: struct coordinate GPHUGE *this_points;
349:
350: this_points = (cp->points) + first_point;
351:
352: if (sr == 0.0) {
353: *px = this_points[0].x;
354: *py = this_points[0].y;
355: } else if (sr == 1.0) {
356: *px = this_points[n].x;
357: *py = this_points[n].y;
358: } else {
1.1.1.2 maekawa 359: /* HBB 990205: do calculation in 'logarithmic space',
360: * to avoid over/underflow errors, which would exactly cancel
361: * out each other, anyway, in an exact calculation
362: */
1.1 maekawa 363: unsigned int i;
364: double lx = 0.0, ly = 0.0;
1.1.1.2 maekawa 365: double log_dsr_to_the_n = n * log(1 -sr);
366: double log_sr_over_dsr = log(sr) - log(1 - sr);
1.1 maekawa 367:
368: for (i = 0; i <= n; i++) {
1.1.1.2 maekawa 369: double u = exp(c[i] + log_dsr_to_the_n + i * log_sr_over_dsr);
370:
1.1 maekawa 371: lx += this_points[i].x * u;
372: ly += this_points[i].y * u;
373: }
374:
375: *px = lx;
376: *py = ly;
377: }
378: }
379:
380: /*
381: * generate a new set of coordinates representing the bezier curve and
382: * set it to the plot
383: */
384:
385: static void do_bezier(cp, bc, first_point, num_points, dest)
386: struct curve_points *cp;
387: double *bc;
388: int first_point; /* where to start in plot->points */
389: int num_points; /* to determine end in plot->points */
390: struct coordinate *dest; /* where to put the interpolated data */
391: {
392: int i;
393: coordval x, y;
394: int xaxis = cp->x_axis;
395: int yaxis = cp->y_axis;
396:
397: /* min and max in internal (eg logged) co-ordinates. We update
398: * these, then update the external extrema in user co-ordinates
399: * at the end.
400: */
401:
402: double ixmin, ixmax, iymin, iymax;
403: double sxmin, sxmax, symin, symax; /* starting values of above */
404:
405: if (log_array[xaxis]) {
406: ixmin = sxmin = log(min_array[xaxis]) / log_base_array[xaxis];
407: ixmax = sxmax = log(max_array[xaxis]) / log_base_array[xaxis];
408: } else {
409: ixmin = sxmin = min_array[xaxis];
410: ixmax = sxmax = max_array[xaxis];
411: }
412:
413: if (log_array[yaxis]) {
414: iymin = symin = log(min_array[yaxis]) / log_base_array[yaxis];
415: iymax = symax = log(max_array[yaxis]) / log_base_array[yaxis];
416: } else {
417: iymin = symin = min_array[yaxis];
418: iymax = symax = max_array[yaxis];
419: }
420:
421: for (i = 0; i < samples; i++) {
422: eval_bezier(cp, first_point, num_points, (double) i / (double) (samples - 1), &x, &y, bc);
423:
424: /* now we have to store the points and adjust the ranges */
425:
426: dest[i].type = INRANGE;
427: STORE_AND_FIXUP_RANGE(dest[i].x, x, dest[i].type, ixmin, ixmax, auto_array[xaxis], NOOP, continue);
428: STORE_AND_FIXUP_RANGE(dest[i].y, y, dest[i].type, iymin, iymax, auto_array[yaxis], NOOP, NOOP);
429:
430: dest[i].xlow = dest[i].xhigh = dest[i].x;
431: dest[i].ylow = dest[i].yhigh = dest[i].y;
432:
433: dest[i].z = -1;
434: }
435:
436: UPDATE_RANGE(ixmax > sxmax, max_array[xaxis], ixmax, xaxis);
437: UPDATE_RANGE(ixmin < sxmin, min_array[xaxis], ixmin, xaxis);
438: UPDATE_RANGE(iymax > symax, max_array[yaxis], iymax, yaxis);
439: UPDATE_RANGE(iymin < symin, min_array[yaxis], iymin, yaxis);
440: }
441:
442: /*
443: * call contouring routines -- main entry
444: */
445:
446: /*
447: * it should be like this, but it doesn't run. If you find out why,
448: * contact me: mgr@asgard.bo.open.de or Lars Hanke 2:243/4802.22@fidonet
449: *
450: * Well, all this had originally been inside contour.c, so maybe links
451: * to functions and of contour.c are broken.
452: * ***deleted***
453: * end of unused entry point to Gershon's code
454: *
455: */
456:
457: /*
458: * Solve five diagonal linear system equation. The five diagonal matrix is
459: * defined via matrix M, right side is r, and solution X i.e. M * X = R.
460: * Size of system given in n. Return TRUE if solution exist.
461: * G. Engeln-Muellges/ F.Reutter:
462: * "Formelsammlung zur Numerischen Mathematik mit Standard-FORTRAN-Programmen"
463: * ISBN 3-411-01677-9
464: *
465: * / m02 m03 m04 0 0 0 0 . . . \ / x0 \ / r0 \
466: * I m11 m12 m13 m14 0 0 0 . . . I I x1 I I r1 I
467: * I m20 m21 m22 m23 m24 0 0 . . . I * I x2 I = I r2 I
468: * I 0 m30 m31 m32 m33 m34 0 . . . I I x3 I I r3 I
469: * . . . . . . . . . . . .
470: * \ m(n-3)0 m(n-2)1 m(n-1)2 / \x(n-1)/ \r(n-1)/
471: *
472: */
473: static int solve_five_diag(m, r, x, n)
474: five_diag m[];
475: double r[], x[];
476: int n;
477: {
478: int i;
479: five_diag *hv;
480:
481: hv = (five_diag *) gp_alloc((n + 1) * sizeof(five_diag), "five_diag help vars");
482:
483: hv[0][0] = m[0][2];
484: if (hv[0][0] == 0) {
485: free(hv);
486: return FALSE;
487: }
488: hv[0][1] = m[0][3] / hv[0][0];
489: hv[0][2] = m[0][4] / hv[0][0];
490:
491: hv[1][3] = m[1][1];
492: hv[1][0] = m[1][2] - hv[1][3] * hv[0][1];
493: if (hv[1][0] == 0) {
494: free(hv);
495: return FALSE;
496: }
497: hv[1][1] = (m[1][3] - hv[1][3] * hv[0][2]) / hv[1][0];
498: hv[1][2] = m[1][4] / hv[1][0];
499:
500: for (i = 2; i <= n - 1; i++) {
501: hv[i][3] = m[i][1] - m[i][0] * hv[i - 2][1];
502: hv[i][0] = m[i][2] - m[i][0] * hv[i - 2][2] - hv[i][3] * hv[i - 1][1];
503: if (hv[i][0] == 0) {
504: free(hv);
505: return FALSE;
506: }
507: hv[i][1] = (m[i][3] - hv[i][3] * hv[i - 1][2]) / hv[i][0];
508: hv[i][2] = m[i][4] / hv[i][0];
509: }
510:
511: hv[0][4] = 0;
512: hv[1][4] = r[0] / hv[0][0];
513: for (i = 1; i <= n - 1; i++) {
514: hv[i + 1][4] = (r[i] - m[i][0] * hv[i - 1][4] - hv[i][3] * hv[i][4]) / hv[i][0];
515: }
516:
517: x[n - 1] = hv[n][4];
518: x[n - 2] = hv[n - 1][4] - hv[n - 2][1] * x[n - 1];
519: for (i = n - 3; i >= 0; i--)
520: x[i] = hv[i + 1][4] - hv[i][1] * x[i + 1] - hv[i][2] * x[i + 2];
521:
522: free(hv);
523: return TRUE;
524: }
525:
526:
527: /*
528: * Calculation of approximation cubic splines
529: * Input: x[i], y[i], weights z[i]
530: *
531: * Returns matrix of spline coefficients
532: */
533: static spline_coeff *cp_approx_spline(plot, first_point, num_points)
534: struct curve_points *plot;
535: int first_point; /* where to start in plot->points */
536: int num_points; /* to determine end in plot->points */
537: {
538: spline_coeff *sc;
539: five_diag *m;
540: int xaxis = plot->x_axis;
541: int yaxis = plot->y_axis;
542: double *r, *x, *h, *xp, *yp;
543: /* HBB 980308: added 'GPHUGE' tag */
544: struct coordinate GPHUGE *this_points;
545: int i;
546:
547: sc = (spline_coeff *) gp_alloc((num_points) * sizeof(spline_coeff),
548: "spline matrix");
549:
550: if (num_points < 4)
551: int_error("Can't calculate approximation splines, need at least 4 points", NO_CARET);
552:
553: this_points = (plot->points) + first_point;
554:
555: for (i = 0; i <= num_points - 1; i++)
556: if (this_points[i].z <= 0)
557: int_error("Can't calculate approximation splines, all weights have to be > 0", NO_CARET);
558:
559: m = (five_diag *) gp_alloc((num_points - 2) * sizeof(five_diag), "spline help matrix");
560:
561: r = (double *) gp_alloc((num_points - 2) * sizeof(double), "spline right side");
562: x = (double *) gp_alloc((num_points - 2) * sizeof(double), "spline solution vector");
563: h = (double *) gp_alloc((num_points - 1) * sizeof(double), "spline help vector");
564:
565: xp = (double *) gp_alloc((num_points) * sizeof(double), "x pos");
566: yp = (double *) gp_alloc((num_points) * sizeof(double), "y pos");
567:
568: /* KB 981107: With logarithmic axis first convert back to linear scale */
569:
570: if (log_array[xaxis]) {
571: for (i = 0; i <= num_points - 1; i++)
572: xp[i] = exp(this_points[i].x * log_base_array[xaxis]);
573: } else {
574: for (i = 0; i <= num_points - 1; i++)
575: xp[i] = this_points[i].x;
576: }
577: if (log_array[yaxis]) {
578: for (i = 0; i <= num_points - 1; i++)
579: yp[i] = exp(this_points[i].y * log_base_array[yaxis]);
580: } else {
581: for (i = 0; i <= num_points - 1; i++)
582: yp[i] = this_points[i].y;
583: }
584:
585: for (i = 0; i <= num_points - 2; i++)
586: h[i] = xp[i + 1] - xp[i];
587:
588: /* set up the matrix and the vector */
589:
590: for (i = 0; i <= num_points - 3; i++) {
591: r[i] = 3 * ((yp[i + 2] - yp[i + 1]) / h[i + 1]
592: - (yp[i + 1] - yp[i]) / h[i]);
593:
594: if (i < 2)
595: m[i][0] = 0;
596: else
597: m[i][0] = 6 / this_points[i].z / h[i - 1] / h[i];
598:
599: if (i < 1)
600: m[i][1] = 0;
601: else
602: m[i][1] = h[i] - 6 / this_points[i].z / h[i] * (1 / h[i - 1] + 1 / h[i])
603: - 6 / this_points[i + 1].z / h[i] * (1 / h[i] + 1 / h[i + 1]);
604:
605: m[i][2] = 2 * (h[i] + h[i + 1])
606: + 6 / this_points[i].z / h[i] / h[i]
607: + 6 / this_points[i + 1].z * (1 / h[i] + 1 / h[i + 1]) * (1 / h[i] + 1 / h[i + 1])
608: + 6 / this_points[i + 2].z / h[i + 1] / h[i + 1];
609:
610: if (i > num_points - 4)
611: m[i][3] = 0;
612: else
613: m[i][3] = h[i + 1] - 6 / this_points[i + 1].z / h[i + 1] * (1 / h[i] + 1 / h[i + 1])
614: - 6 / this_points[i + 2].z / h[i + 1] * (1 / h[i + 1] + 1 / h[i + 2]);
615:
616: if (i > num_points - 5)
617: m[i][4] = 0;
618: else
619: m[i][4] = 6 / this_points[i + 2].z / h[i + 1] / h[i + 2];
620: }
621:
622: /* solve the matrix */
623: if (!solve_five_diag(m, r, x, num_points - 2)) {
624: free(h);
625: free(x);
626: free(r);
627: free(m);
628: free(xp);
629: free(yp);
630: int_error("Can't calculate approximation splines", NO_CARET);
631: }
632: sc[0][2] = 0;
633: for (i = 1; i <= num_points - 2; i++)
634: sc[i][2] = x[i - 1];
635: sc[num_points - 1][2] = 0;
636:
637: sc[0][0] = yp[0] + 2 / this_points[0].z / h[0] * (sc[0][2] - sc[1][2]);
638: for (i = 1; i <= num_points - 2; i++)
639: sc[i][0] = yp[i] - 2 / this_points[i].z *
640: (sc[i - 1][2] / h[i - 1]
641: - sc[i][2] * (1 / h[i - 1] + 1 / h[i])
642: + sc[i + 1][2] / h[i]);
643: sc[num_points - 1][0] = yp[num_points - 1]
644: - 2 / this_points[num_points - 1].z / h[num_points - 2]
645: * (sc[num_points - 2][2] - sc[num_points - 1][2]);
646:
647: for (i = 0; i <= num_points - 2; i++) {
648: sc[i][1] = (sc[i + 1][0] - sc[i][0]) / h[i]
649: - h[i] / 3 * (sc[i + 1][2] + 2 * sc[i][2]);
650: sc[i][3] = (sc[i + 1][2] - sc[i][2]) / 3 / h[i];
651: }
652:
653: free(h);
654: free(x);
655: free(r);
656: free(m);
657: free(xp);
658: free(yp);
659:
660: return (sc);
661: }
662:
663:
664: /*
665: * Calculation of cubic splines
666: *
667: * This can be treated as a special case of approximation cubic splines, with
668: * all weights -> infinity.
669: *
670: * Returns matrix of spline coefficients
671: */
672: static spline_coeff *cp_tridiag(plot, first_point, num_points)
673: struct curve_points *plot;
674: int first_point, num_points;
675: {
676: spline_coeff *sc;
677: tri_diag *m;
678: int xaxis = plot->x_axis;
679: int yaxis = plot->y_axis;
680: double *r, *x, *h, *xp, *yp;
681: /* HBB 980308: added 'GPHUGE' tag */
682: struct coordinate GPHUGE *this_points;
683: int i;
684:
685: if (num_points < 3)
686: int_error("Can't calculate splines, need at least 3 points", NO_CARET);
687:
688: this_points = (plot->points) + first_point;
689:
690: sc = (spline_coeff *) gp_alloc((num_points) * sizeof(spline_coeff), "spline matrix");
691: m = (tri_diag *) gp_alloc((num_points - 2) * sizeof(tri_diag), "spline help matrix");
692:
693: r = (double *) gp_alloc((num_points - 2) * sizeof(double), "spline right side");
694: x = (double *) gp_alloc((num_points - 2) * sizeof(double), "spline solution vector");
695: h = (double *) gp_alloc((num_points - 1) * sizeof(double), "spline help vector");
696:
697: xp = (double *) gp_alloc((num_points) * sizeof(double), "x pos");
698: yp = (double *) gp_alloc((num_points) * sizeof(double), "y pos");
699:
700: /* KB 981107: With logarithmic axis first convert back to linear scale */
701:
702: if (log_array[xaxis]) {
703: for (i = 0; i <= num_points - 1; i++)
704: xp[i] = exp(this_points[i].x * log_base_array[xaxis]);
705: } else {
706: for (i = 0; i <= num_points - 1; i++)
707: xp[i] = this_points[i].x;
708: }
709: if (log_array[yaxis]) {
710: for (i = 0; i <= num_points - 1; i++)
711: yp[i] = exp(this_points[i].y * log_base_array[yaxis]);
712: } else {
713: for (i = 0; i <= num_points - 1; i++)
714: yp[i] = this_points[i].y;
715: }
716:
717: for (i = 0; i <= num_points - 2; i++)
718: h[i] = xp[i + 1] - xp[i];
719:
720: /* set up the matrix and the vector */
721:
722: for (i = 0; i <= num_points - 3; i++) {
723: r[i] = 3 * ((yp[i + 2] - yp[i + 1]) / h[i + 1]
724: - (yp[i + 1] - yp[i]) / h[i]);
725:
726: if (i < 1)
727: m[i][0] = 0;
728: else
729: m[i][0] = h[i];
730:
731: m[i][1] = 2 * (h[i] + h[i + 1]);
732:
733: if (i > num_points - 4)
734: m[i][2] = 0;
735: else
736: m[i][2] = h[i + 1];
737: }
738:
739: /* solve the matrix */
740: if (!solve_tri_diag(m, r, x, num_points - 2)) {
741: free(h);
742: free(x);
743: free(r);
744: free(m);
745: free(xp);
746: free(yp);
747: int_error("Can't calculate cubic splines", NO_CARET);
748: }
749: sc[0][2] = 0;
750: for (i = 1; i <= num_points - 2; i++)
751: sc[i][2] = x[i - 1];
752: sc[num_points - 1][2] = 0;
753:
754: for (i = 0; i <= num_points - 1; i++)
755: sc[i][0] = yp[i];
756:
757: for (i = 0; i <= num_points - 2; i++) {
758: sc[i][1] = (sc[i + 1][0] - sc[i][0]) / h[i]
759: - h[i] / 3 * (sc[i + 1][2] + 2 * sc[i][2]);
760: sc[i][3] = (sc[i + 1][2] - sc[i][2]) / 3 / h[i];
761: }
762:
763: free(h);
764: free(x);
765: free(r);
766: free(m);
767: free(xp);
768: free(yp);
769:
770: return (sc);
771: }
772:
773: static void do_cubic(plot, sc, first_point, num_points, dest)
774: struct curve_points *plot; /* still containes old plot->points */
775: spline_coeff *sc; /* generated by cp_tridiag */
776: int first_point; /* where to start in plot->points */
777: int num_points; /* to determine end in plot->points */
778: struct coordinate *dest; /* where to put the interpolated data */
779: {
780: double xdiff, temp, x, y;
781: int i, l;
782: int xaxis = plot->x_axis;
783: int yaxis = plot->y_axis;
784: /* HBB 980308: added 'GPHUGE' tag */
785: struct coordinate GPHUGE *this_points;
786:
787: /* min and max in internal (eg logged) co-ordinates. We update
788: * these, then update the external extrema in user co-ordinates
789: * at the end.
790: */
791:
792: double ixmin, ixmax, iymin, iymax;
793: double sxmin, sxmax, symin, symax; /* starting values of above */
794:
795: if (log_array[xaxis]) {
796: ixmin = sxmin = log(min_array[xaxis]) / log_base_array[xaxis];
797: ixmax = sxmax = log(max_array[xaxis]) / log_base_array[xaxis];
798: } else {
799: ixmin = sxmin = min_array[xaxis];
800: ixmax = sxmax = max_array[xaxis];
801: }
802:
803: if (log_array[yaxis]) {
804: iymin = symin = log(min_array[yaxis]) / log_base_array[yaxis];
805: iymax = symax = log(max_array[yaxis]) / log_base_array[yaxis];
806: } else {
807: iymin = symin = min_array[yaxis];
808: iymax = symax = max_array[yaxis];
809: }
810:
811:
812: this_points = (plot->points) + first_point;
813:
814: l = 0;
815:
816: xdiff = (this_points[num_points - 1].x - this_points[0].x) / (samples - 1);
817:
818: for (i = 0; i < samples; i++) {
819: x = this_points[0].x + i * xdiff;
820:
821: while ((x >= this_points[l + 1].x) && (l < num_points - 2))
822: l++;
823:
824: /* KB 981107: With logarithmic x axis the values were converted back to linear */
825: /* scale before calculating the coefficients. Use exponential for log x values. */
826:
827: if (log_array[xaxis]) {
828: temp = exp(x * log_base_array[xaxis]) - exp(this_points[l].x * log_base_array[xaxis]);
829: y = ((sc[l][3] * temp + sc[l][2]) * temp + sc[l][1]) * temp + sc[l][0];
830: } else {
831: temp = x - this_points[l].x;
832: y = ((sc[l][3] * temp + sc[l][2]) * temp + sc[l][1]) * temp + sc[l][0];
833: }
834: /* With logarithmic y axis, we need to convert from linear to log scale now. */
835: if (log_array[yaxis]) {
836: if (y > 0.)
837: y = log(y) / log_base_array[yaxis];
838: else
839: y = symin - (symax - symin);
840: }
841: dest[i].type = INRANGE;
842: STORE_AND_FIXUP_RANGE(dest[i].x, x, dest[i].type, ixmin, ixmax, auto_array[xaxis], NOOP, continue);
843: STORE_AND_FIXUP_RANGE(dest[i].y, y, dest[i].type, iymin, iymax, auto_array[yaxis], NOOP, NOOP);
844:
845: dest[i].xlow = dest[i].xhigh = dest[i].x;
846: dest[i].ylow = dest[i].yhigh = dest[i].y;
847:
848: dest[i].z = -1;
849:
850: }
851:
852: UPDATE_RANGE(ixmax > sxmax, max_array[xaxis], ixmax, xaxis);
853: UPDATE_RANGE(ixmin < sxmin, min_array[xaxis], ixmin, xaxis);
854: UPDATE_RANGE(iymax > symax, max_array[yaxis], iymax, yaxis);
855: UPDATE_RANGE(iymin < symin, min_array[yaxis], iymin, yaxis);
856:
857: }
858:
859: /*
860: * This is the main entry point used. As stated in the header, it is fine,
861: * but I'm not too happy with it.
862: */
863:
864: void gen_interp(plot)
865: struct curve_points *plot;
866: {
867:
868: spline_coeff *sc;
869: double *bc;
870: struct coordinate *new_points;
871: int i, curves;
872: int first_point, num_points;
873:
874: curves = num_curves(plot);
875: new_points = (struct coordinate *) gp_alloc((samples + 1) * curves * sizeof(struct coordinate), "interpolation table");
876:
877: first_point = 0;
878: for (i = 0; i < curves; i++) {
879: num_points = next_curve(plot, &first_point);
880: switch (plot->plot_smooth) {
881: case CSPLINES:
882: sc = cp_tridiag(plot, first_point, num_points);
883: do_cubic(plot, sc, first_point, num_points,
884: new_points + i * (samples + 1));
885: free(sc);
886: break;
887: case ACSPLINES:
888: sc = cp_approx_spline(plot, first_point, num_points);
889: do_cubic(plot, sc, first_point, num_points,
890: new_points + i * (samples + 1));
891: free(sc);
892: break;
893:
894: case BEZIER:
895: case SBEZIER:
896: bc = cp_binomial(num_points);
897: do_bezier(plot, bc, first_point, num_points,
898: new_points + i * (samples + 1));
899: free((char *) bc);
900: break;
901: default: /* keep gcc -Wall quiet */
902: ;
903: }
904: new_points[(i + 1) * (samples + 1) - 1].type = UNDEFINED;
905: first_point += num_points;
906: }
907:
908: free(plot->points);
909: plot->points = new_points;
910: plot->p_max = curves * (samples + 1);
911: plot->p_count = plot->p_max - 1;
912:
913: return;
914: }
915:
916: /*
917: * sort_points
918: *
919: * sort data succession for further evaluation by plot_splines, etc.
920: * This routine is mainly introduced for compilers *NOT* supporting the
921: * UNIX qsort() routine. You can then easily replace it by more convenient
922: * stuff for your compiler.
923: * (MGR 1992)
924: */
925:
1.1.1.3 ! ohara 926: int compare_points(argp1, argp2)
! 927: SORTFUNC_ARGS argp1;
! 928: SORTFUNC_ARGS argp2;
1.1 maekawa 929: {
1.1.1.3 ! ohara 930: const struct coordinate *p1 = argp1;
! 931: const struct coordinate *p2 = argp2;
! 932:
1.1 maekawa 933: if (p1->x > p2->x)
934: return (1);
935: if (p1->x < p2->x)
936: return (-1);
937: return (0);
938: }
939:
940: void sort_points(plot)
1.1.1.3 ! ohara 941: struct curve_points *plot;
1.1 maekawa 942: {
943: int first_point, num_points;
944:
945: first_point = 0;
946: while ((num_points = next_curve(plot, &first_point)) > 0) {
947: /* Sort this set of points, does qsort handle 1 point correctly? */
948: qsort((char *) (plot->points + first_point), num_points,
1.1.1.3 ! ohara 949: sizeof(struct coordinate), compare_points);
1.1 maekawa 950: first_point += num_points;
951: }
952: return;
953: }
954:
955:
956: /*
957: * cp_implode() if averaging is selected this function computes the new
958: * entries and shortens the whole thing to the necessary
959: * size
960: * MGR Addendum
961: */
962:
963: void cp_implode(cp)
964: struct curve_points *cp;
965: {
966: int first_point, num_points;
967: int i, j, k;
968: double x, y, sux, slx, suy, sly;
969: enum coord_type dot;
970:
971:
972: j = 0;
973: first_point = 0;
974: while ((num_points = next_curve(cp, &first_point)) > 0) {
975: k = 0;
976: for (i = first_point; i < first_point + num_points; i++) {
977: if (!k) {
978: x = cp->points[i].x;
979: y = cp->points[i].y;
980: sux = cp->points[i].xhigh;
981: slx = cp->points[i].xlow;
982: suy = cp->points[i].yhigh;
983: sly = cp->points[i].ylow;
984: dot = INRANGE;
985: if (cp->points[i].type != INRANGE)
986: dot = UNDEFINED; /* This means somthing other than usual *//* just signal to check if INRANGE */
987: k = 1;
988: } else if (cp->points[i].x == x) {
989: y += cp->points[i].y;
990: sux += cp->points[i].xhigh;
991: slx += cp->points[i].xlow;
992: suy += cp->points[i].yhigh;
993: sly += cp->points[i].ylow;
994: if (cp->points[i].type != INRANGE)
995: dot = UNDEFINED;
996: k++;
997: } else {
998: cp->points[j].x = x;
999: cp->points[j].y = y / (double) k;
1000: cp->points[j].xhigh = sux / (double) k;
1001: cp->points[j].xlow = slx / (double) k;
1002: cp->points[j].yhigh = suy / (double) k;
1003: cp->points[j].ylow = sly / (double) k;
1004: cp->points[j].type = INRANGE;
1005: if (dot != INRANGE) {
1006: if ((cp->points[j].x > xmax) || (cp->points[j].x < xmin))
1007: cp->points[j].type = OUTRANGE;
1008: else if (!autoscale_ly) {
1009: if ((cp->points[j].y > ymax) || (cp->points[j].y < ymin))
1010: cp->points[j].type = OUTRANGE;
1011: } else
1012: cp->points[j].type = INRANGE;
1013: }
1014: j++; /* next valid entry */
1015: k = 0; /* to read */
1016: i--; /* from this (-> last after for(;;)) entry */
1017: }
1018: }
1019: if (k) {
1020: cp->points[j].x = x;
1021: cp->points[j].y = y / (double) k;
1022: cp->points[j].xhigh = sux / (double) k;
1023: cp->points[j].xlow = slx / (double) k;
1024: cp->points[j].yhigh = suy / (double) k;
1025: cp->points[j].ylow = sly / (double) k;
1026: cp->points[j].type = INRANGE;
1027: if (dot != INRANGE) {
1028: if ((cp->points[j].x > xmax) || (cp->points[j].x < xmin))
1029: cp->points[j].type = OUTRANGE;
1030: else if (!autoscale_ly) {
1031: if ((cp->points[j].y > ymax) || (cp->points[j].y < ymin))
1032: cp->points[j].type = OUTRANGE;
1033: } else
1034: cp->points[j].type = INRANGE;
1035: }
1036: j++; /* next valid entry */
1037: }
1038: /* insert invalid point to separate curves */
1039: if (j < cp->p_count) {
1040: cp->points[j].type = UNDEFINED;
1041: j++;
1042: }
1043: first_point += num_points;
1044: } /* end while */
1045: cp->p_count = j;
1046: cp_extend(cp, j);
1047: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>