Annotation of OpenXM_contrib/gnuplot/plot2d.c, Revision 1.1.1.3
1.1 maekawa 1: #ifndef lint
1.1.1.3 ! ohara 2: static char *RCSid = "$Id: plot2d.c,v 1.16.2.13 2002/11/09 18:04:31 lhecking Exp $";
1.1 maekawa 3: #endif
4:
5: /* GNUPLOT - plot2d.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: #include "plot.h"
38: #include "setshow.h"
39: #include "fit.h"
40: #include "binary.h"
41:
42: #ifndef _Windows
43: # include "help.h"
44: #endif
45:
46: #ifndef STDOUT
47: #define STDOUT 1
48: #endif
49:
50: /* static prototypes */
51:
52: void plotrequest __PROTO((void));
53: void plot3drequest __PROTO((void));
54: void define __PROTO((void));
55: static void get_data __PROTO((struct curve_points * this_plot));
56: static void store2d_point __PROTO((struct curve_points * this_plot, int i, double x, double y, double xlow, double xhigh, double ylow, double yhigh, double width));
57: static void print_table __PROTO((struct curve_points * first_plot, int plot_num));
58: static void eval_plots __PROTO((void));
59: static void parametric_fixup __PROTO((struct curve_points * start_plot, int *plot_num));
60:
61:
62: /* the curves/surfaces of the plot */
63: struct curve_points *first_plot = NULL;
64: static struct udft_entry plot_func;
65: extern struct udft_entry *dummy_func;
66:
67: /* jev -- for passing data thru user-defined function */
68: /* Needed by datafile.c */
69: struct udft_entry ydata_func;
70:
71: extern int datatype[];
72: extern char timefmt[];
73:
74: extern TBOOLEAN is_3d_plot;
75: extern int plot_token;
76:
77: /* in order to support multiple axes, and to
78: * simplify ranging in parametric plots, we use
79: * arrays to store some things.
80: * Elements are z = 0, y1 = 1, x1 = 2, [z2 =4 ], y2 = 5, x2 = 6
81: * these are given symbolic names in plot.h
82: */
83:
84: /* Were declared in command.c */
85: double min_array[AXIS_ARRAY_SIZE], max_array[AXIS_ARRAY_SIZE];
86: int auto_array[AXIS_ARRAY_SIZE];
87: TBOOLEAN log_array[AXIS_ARRAY_SIZE];
88: double base_array[AXIS_ARRAY_SIZE];
89: double log_base_array[AXIS_ARRAY_SIZE];
90:
91: /* Deleted from setshow.h and renamed */
92: extern FILE *gpoutfile;
93:
94: /* if user specifies [10:-10] we use [-10:10] internally, and swap at end */
95: int reverse_range[AXIS_ARRAY_SIZE];
96:
97: /* info from datafile module */
98: extern int df_datum;
99: extern int df_line_number;
100: extern int df_no_use_specs;
101: extern int df_eof;
102: extern int df_timecol[];
103: extern TBOOLEAN df_binary;
104:
105: #define Inc_c_token if (++c_token >= num_tokens) \
106: int_error ("Syntax error", c_token);
107:
108:
109: /*
110: * IMHO, code is getting too cluttered with repeated chunks of
111: * code. Some macros to simplify, I hope.
112: *
113: * do { } while(0) is comp.lang.c recommendation for complex macros
114: * also means that break can be specified as an action, and it will
115: *
116: */
117:
118: /* copy scalar data to arrays
119: * optimiser should optimise infinite away
120: * dont know we have to support ranges [10:-10] - lets reverse
121: * it for now, then fix it at the end.
122: */
1.1.1.3 ! ohara 123:
! 124: /* HBB 20021103: try to circumvent BC31 bug by turning this macro into
! 125: * a function... */
! 126: static GP_INLINE void
! 127: init_arrays(int axis, double min, double max, int autosc, int is_log, double base, double log_base, TBOOLEAN infinite)
! 128: {
! 129: auto_array[axis] = autosc;
! 130: min_array[axis] = (infinite && (autosc & 1)) ? VERYLARGE : min;
! 131: max_array[axis] = (infinite && (autosc & 2)) ? -VERYLARGE : max;
! 132: log_array[axis] = is_log;
! 133: base_array[axis] = base;
! 134: log_base_array[axis] = log_base;
! 135: }
! 136: #define INIT_ARRAYS init_arrays
! 137:
1.1 maekawa 138: /* handle reversed ranges */
139: #define CHECK_REVERSE(axis) \
140: do{\
141: if (auto_array[axis] == 0 && max_array[axis] < min_array[axis]) {\
142: double temp = min_array[axis]; min_array[axis] = max_array[axis]; max_array[axis] = temp;\
143: reverse_range[axis] = 1; \
144: } else reverse_range[axis] = (range_flags[axis]&RANGE_REVERSE); \
145: }while(0)
146:
147:
148: /* get optional [min:max] */
149: #define LOAD_RANGE(axis) \
150: do {\
151: if (equals(c_token, "[")) { \
152: c_token++; \
153: auto_array[axis] = load_range(axis,&min_array[axis], &max_array[axis], auto_array[axis]);\
154: if (!equals(c_token, "]"))\
155: int_error("']' expected", c_token);\
156: c_token++;\
157: }\
158: } while (0)
159:
160:
161: /* store VALUE or log(VALUE) in STORE, set TYPE as appropriate
162: * Do OUT_ACTION or UNDEF_ACTION as appropriate
163: * adjust range provided type is INRANGE (ie dont adjust y if x is outrange
164: * VALUE must not be same as STORE
165: */
166:
167: #define STORE_WITH_LOG_AND_FIXUP_RANGE(STORE, VALUE, TYPE, AXIS, OUT_ACTION, UNDEF_ACTION)\
168: do { if (log_array[AXIS]) { if (VALUE<0.0) {TYPE = UNDEFINED; UNDEF_ACTION; break;} \
169: else if (VALUE == 0.0){STORE = -VERYLARGE; TYPE = OUTRANGE; OUT_ACTION; break;} \
170: else { STORE = log(VALUE)/log_base_array[AXIS]; } \
171: } else STORE = VALUE; \
172: if (TYPE != INRANGE) break; /* dont set y range if x is outrange, for example */ \
173: if ( VALUE<min_array[AXIS] ) { \
174: if (auto_array[AXIS] & 1) min_array[AXIS] = VALUE; else { TYPE = OUTRANGE; OUT_ACTION; break; } \
175: } \
176: if ( VALUE>max_array[AXIS] ) { \
177: if (auto_array[AXIS] & 2) max_array[AXIS] = VALUE; else { TYPE = OUTRANGE; OUT_ACTION; } \
178: } \
179: } while(0)
180:
181: /* use this instead empty macro arguments to work around NeXT cpp bug */
182: /* if this fails on any system, we might use ((void)0) */
183: #define NOOP /* */
184:
185: /* check range and take logs of min and max if logscale
186: * this also restores min and max for ranges like [10:-10]
187: */
188: #ifdef HAVE_STRINGIZE
189: # define LOG_MSG(x) #x " range must be greater than 0 for log scale!"
190: #else
191: # define LOG_MSG(x) "x range must be greater than 0 for log scale!"
192: #endif
193:
194: #define FIXUP_RANGE_FOR_LOG(AXIS, WHICH) \
195: do { if (reverse_range[AXIS]) { \
196: double temp = min_array[AXIS]; \
197: min_array[AXIS] = max_array[AXIS]; \
198: max_array[AXIS] = temp; \
199: }\
200: if (log_array[AXIS]) { \
201: if (min_array[AXIS] <= 0.0 || max_array[AXIS] <= 0.0) \
202: int_error(LOG_MSG(WHICH), NO_CARET); \
203: min_array[AXIS] = log(min_array[AXIS])/log_base_array[AXIS]; \
204: max_array[AXIS] = log(max_array[AXIS])/log_base_array[AXIS]; \
205: } \
206: } while(0)
207:
208:
209:
210: void plotrequest()
211: /*
212: * In the parametric case we can say plot [a= -4:4] [-2:2] [-1:1] sin(a),a**2
213: * while in the non-parametric case we would say only plot [b= -2:2] [-1:1]
214: * sin(b)
215: */
216: {
217: int dummy_token = -1;
218:
219: if (!term) /* unknown */
220: int_error("use 'set term' to set terminal type first", c_token);
221:
222: is_3d_plot = FALSE;
223:
224: if (parametric && strcmp(dummy_var[0], "u") == 0)
225: strcpy(dummy_var[0], "t");
226:
227: /* initialise the arrays from the 'set' scalars */
228:
229: INIT_ARRAYS(FIRST_X_AXIS, xmin, xmax, autoscale_x, is_log_x, base_log_x, log_base_log_x, 0);
230: INIT_ARRAYS(FIRST_Y_AXIS, ymin, ymax, autoscale_y, is_log_y, base_log_y, log_base_log_y, 1);
231: INIT_ARRAYS(SECOND_X_AXIS, x2min, x2max, autoscale_x2, is_log_x2, base_log_x2, log_base_log_x2, 0);
232: INIT_ARRAYS(SECOND_Y_AXIS, y2min, y2max, autoscale_y2, is_log_y2, base_log_y2, log_base_log_y2, 1);
233:
234: min_array[T_AXIS] = tmin;
235: max_array[T_AXIS] = tmax;
236:
237: if (equals(c_token, "[")) {
238: c_token++;
239: if (isletter(c_token)) {
240: if (equals(c_token + 1, "=")) {
241: dummy_token = c_token;
242: c_token += 2;
243: } else {
244: /* oops; probably an expression with a variable. */
245: /* Parse it as an xmin expression. */
246: /* used to be: int_error("'=' expected",c_token); */
247: }
248: } {
249: int axis = (parametric || polar) ? T_AXIS : FIRST_X_AXIS;
250:
251:
252: auto_array[axis] = load_range(axis, &min_array[axis], &max_array[axis], auto_array[axis]);
253: if (!equals(c_token, "]"))
254: int_error("']' expected", c_token);
255: c_token++;
256: } /* end of scope of 'axis' */
257: } /* first '[' */
258: if (parametric || polar) /* set optional x ranges */
259: LOAD_RANGE(FIRST_X_AXIS);
1.1.1.3 ! ohara 260:
! 261: /* order of x range does matter, even if we're in parametric mode */
! 262: CHECK_REVERSE(FIRST_X_AXIS);
1.1 maekawa 263:
264: LOAD_RANGE(FIRST_Y_AXIS);
265: CHECK_REVERSE(FIRST_Y_AXIS);
266: LOAD_RANGE(SECOND_X_AXIS);
267: CHECK_REVERSE(SECOND_X_AXIS);
268: LOAD_RANGE(SECOND_Y_AXIS);
269: CHECK_REVERSE(SECOND_Y_AXIS);
270:
271: /* use the default dummy variable unless changed */
272: if (dummy_token >= 0)
273: copy_str(c_dummy_var[0], dummy_token, MAX_ID_LEN);
274: else
275: (void) strcpy(c_dummy_var[0], dummy_var[0]);
276:
277: eval_plots();
278: }
279:
280: /* Use up to 7 columns in data file at once -- originally it was 5 */
281: #define NCOL 7
282:
283:
284: /* A quick note about boxes style. For boxwidth auto, we cannot
285: * calculate widths yet, since it may be sorted, etc. But if
286: * width is set, we must do it now, before logs of xmin/xmax
287: * are taken.
288: * We store -1 in point->z as a marker to mean width needs to be
289: * calculated, or 0 to mean that xmin/xmax are set correctly
290: */
291:
292:
293: static void get_data(this_plot)
294: struct curve_points *this_plot;
295: /* this_plot->token is after datafile spec, for error reporting
296: * it will later be moved passed title/with/linetype/pointtype
297: */
298: {
1.1.1.2 maekawa 299: int i /* num. points ! */ , j;
300: int max_cols, min_cols; /* allowed range of column numbers */
1.1 maekawa 301: double v[NCOL];
302: int storetoken = this_plot->token;
303:
304: /* eval_plots has already opened file */
305:
306: switch (this_plot->plot_style) { /* set maximum columns to scan */
307: case XYERRORBARS:
308: case BOXXYERROR:
1.1.1.2 maekawa 309: max_cols = 7;
310: min_cols = 4;
1.1 maekawa 311: break;
312:
313: case FINANCEBARS:
314: case CANDLESTICKS:
1.1.1.2 maekawa 315: min_cols = max_cols = 5;
316: break;
317:
318: case BOXERROR:
319: max_cols = 5;
320: min_cols = 4;
321: break;
322:
323: case VECTOR:
324: min_cols = max_cols = 4;
1.1 maekawa 325: break;
326:
327: case XERRORBARS:
328: case YERRORBARS:
1.1.1.2 maekawa 329: max_cols = 4;
330: min_cols = 3;
1.1 maekawa 331: break;
332:
333: case BOXES:
1.1.1.2 maekawa 334: min_cols = 2;
335: max_cols = 4;
1.1.1.3 ! ohara 336: break;
1.1 maekawa 337:
338: default:
1.1.1.2 maekawa 339: min_cols = 1;
340: max_cols = 2;
1.1 maekawa 341: }
342:
343: if (this_plot->plot_smooth == ACSPLINES)
1.1.1.2 maekawa 344: max_cols = 3;
345:
346: if (df_no_use_specs > max_cols)
347: int_error("Too many using specs for this style", NO_CARET);
1.1 maekawa 348:
1.1.1.2 maekawa 349: if (df_no_use_specs >0 && df_no_use_specs < min_cols)
350: int_error("Not enough columns for this style", NO_CARET);
1.1 maekawa 351:
352: i = 0;
1.1.1.2 maekawa 353: while ((j = df_readline(v, max_cols)) != DF_EOF) {
354: /* j <= max_cols */
1.1 maekawa 355:
356: if (i >= this_plot->p_max) {
357: /*
358: * overflow about to occur. Extend size of points[] array. We
359: * either double the size, or add 1000 points, whichever is a
360: * smaller increment. Note i = p_max.
361: */
362: cp_extend(this_plot, i + (i < 1000 ? i : 1000));
363: }
364: /* Limitation: No xerrorbars with boxes */
365: switch (j) {
366: default:
367: {
368: char message[80];
369: sprintf(message, "internal error : df_readline returned %d : datafile line %d", j, df_line_number);
370: df_close();
371: int_error(message, c_token);
372: }
373: case DF_UNDEFINED:
374: /* bad result from extended using expression */
375: this_plot->points[i].type = UNDEFINED;
376: i++;
377: continue;
378:
379: case DF_FIRST_BLANK:
380: /* break in data, make next point undefined */
381: this_plot->points[i].type = UNDEFINED;
382: i++;
383: continue;
384:
385: case DF_SECOND_BLANK:
386: /* second blank line. We dont do anything
387: * (we did everything when we got FIRST one)
388: */
389: continue;
390:
391: case 0: /* not blank line, but df_readline couldn't parse it */
392: {
393: char message[80];
394: sprintf(message, "Bad data on line %d", df_line_number);
395: df_close();
396: int_error(message, this_plot->token);
397: }
398:
399: case 1:
400: { /* only one number */
401: /* x is index, assign number to y */
402: v[1] = v[0];
403: v[0] = df_datum;
404: /* nobreak */
405: }
406:
407: case 2:
408: /* x, y */
409: /* ylow and yhigh are same as y */
410:
411: if (this_plot->plot_style == BOXES && boxwidth > 0) {
412: /* calc width now */
413: store2d_point(this_plot, i++, v[0], v[1], v[0] - boxwidth / 2, v[0] + boxwidth / 2, v[1], v[1], 0.0);
414: } else {
415: /* xlow and xhigh are same as x */
416: store2d_point(this_plot, i++, v[0], v[1], v[0], v[0], v[1], v[1],
417: -1.0); /* auto width if boxes, else ignored */
418: }
419: break;
420:
421:
422: case 3:
423: /* x, y, ydelta OR x, y, xdelta OR x, y, width */
424: if (this_plot->plot_smooth == ACSPLINES)
425: store2d_point(this_plot, i++, v[0], v[1], v[0], v[0], v[1], v[1], v[2]);
426: else
427: switch (this_plot->plot_style) {
428: default:
429: int_warn("This plot style not work with 3 cols. Setting to yerrorbars", storetoken);
430: this_plot->plot_style = YERRORBARS;
431: /* fall through */
432:
433: case YERRORBARS:
434: case BOXERROR: /* x, y, dy */
435: store2d_point(this_plot, i++, v[0], v[1], v[0], v[0], v[1] - v[2], v[1] + v[2],
436: -1.0); /* auto width if boxes, else ignored */
437: break;
438:
439: case XERRORBARS:
440: store2d_point(this_plot, i++, v[0], v[1], v[0] - v[2], v[0] + v[2], v[1], v[1], 0.0);
441: break;
442:
443: case BOXES:
444: /* calculate xmin and xmax here, so that logs are taken if
445: * if necessary
446: */
447: store2d_point(this_plot, i++, v[0], v[1], v[0] - v[2] / 2, v[0] + v[2] / 2, v[1], v[1], 0.0);
448: break;
449:
450: } /*inner switch */
451:
452: break;
453:
454:
455:
456: case 4:
457: /* x, y, ylow, yhigh OR
458: * x, y, xlow, xhigh OR
459: * x, y, xdelta, ydelta OR
460: * x, y, ydelta, width
461: */
462:
463: switch (this_plot->plot_style) {
464: default:
465: int_warn("This plot style does not work with 4 cols. Setting to yerrorbars",
466: storetoken);
467: this_plot->plot_style = YERRORBARS;
468: /* fall through */
469:
470: case YERRORBARS:
471: store2d_point(this_plot, i++, v[0], v[1], v[0], v[0], v[2], v[3], -1.0);
472: break;
473:
474: case BOXXYERROR: /* x, y, dx, dy */
475: case XYERRORBARS:
476: store2d_point(this_plot, i++, v[0], v[1], v[0] - v[2], v[0] + v[2], v[1] - v[3], v[1] + v[3], 0.0);
477: break;
478:
479:
480: case BOXES: /* x, y, xmin, xmax */
481: store2d_point(this_plot, i++, v[0], v[1], v[2], v[3], v[1], v[1], 0.0);
482: break;
483:
484: case XERRORBARS:
485: store2d_point(this_plot, i++, v[0], v[1], v[2], v[3], v[1], v[1], 0.0);
486: break;
487:
488: case BOXERROR:
489: /* x,y, xleft, xright */
490: store2d_point(this_plot, i++, v[0], v[1], v[0], v[0], v[1] - v[2], v[1] + v[2], 0.0);
491: break;
492:
493: case VECTOR:
494: /* x,y,dx,dy */
495: store2d_point(this_plot, i++, v[0], v[1], v[0], v[0] + v[2], v[1], v[1] + v[3], -1.0);
496: break;
497: } /*inner switch */
498:
499: break;
500:
501:
502: case 5:
503: { /* x, y, ylow, yhigh, width or x open low high close */
504: switch (this_plot->plot_style) {
505: default:
506: int_warn("Five col. plot style must be boxerrorbars, financebars or candlesticks. Setting to boxerrorbars", storetoken);
507: this_plot->plot_style = BOXERROR;
508: /*fall through */
509:
510: case BOXERROR: /* x, y, ylow, yhigh, width */
511: store2d_point(this_plot, i++, v[0], v[1], v[0] - v[4] / 2, v[0] + v[4] / 2, v[2], v[3], 0.0);
512: break;
513:
514: case FINANCEBARS:
515: case CANDLESTICKS:
516: store2d_point(this_plot, i++, v[0], v[1], v[0], v[0], v[2], v[3], v[4]);
517: break;
518: }
519: break;
520: }
521:
522: case 7:
523: /* same as six columns. Width ignored */
524: /* eh ? - fall through */
525: case 6:
526: /* x, y, xlow, xhigh, ylow, yhigh */
527: switch (this_plot->plot_style) {
528: default:
529: int_warn("This plot style not work with 6 cols. Setting to xyerrorbars", storetoken);
530: this_plot->plot_style = XYERRORBARS;
531: /*fall through */
532: case XYERRORBARS:
533: case BOXXYERROR:
534: store2d_point(this_plot, i++, v[0], v[1], v[2], v[3], v[4], v[5], 0.0);
535: break;
536: }
537:
538: } /*switch */
539:
540: } /*while */
541:
542: this_plot->p_count = i;
543: cp_extend(this_plot, i); /* shrink to fit */
544:
545: df_close();
546: }
547:
548: /* called by get_data for each point */
549: static void store2d_point(this_plot, i, x, y, xlow, xhigh, ylow, yhigh, width)
550: struct curve_points *this_plot;
551: int i; /* point number */
552: double x, y;
553: double ylow, yhigh;
554: double xlow, xhigh;
555: double width; /* -1 means autocalc, 0 means use xmin/xmax */
556: {
557: struct coordinate GPHUGE *cp = &(this_plot->points[i]);
558: int dummy_type = INRANGE; /* sometimes we dont care about outranging */
559:
560:
561: /* jev -- pass data values thru user-defined function */
562: /* div -- y is dummy variable 2 - copy value there */
563: if (ydata_func.at) {
564: struct value val;
565:
566: (void) Gcomplex(&ydata_func.dummy_values[0], y, 0.0);
567: ydata_func.dummy_values[2] = ydata_func.dummy_values[0];
568: evaluate_at(ydata_func.at, &val);
569: y = undefined ? 0.0 : real(&val);
570:
571: (void) Gcomplex(&ydata_func.dummy_values[0], ylow, 0.0);
572: ydata_func.dummy_values[2] = ydata_func.dummy_values[0];
573: evaluate_at(ydata_func.at, &val);
574: ylow = undefined ? 0 : real(&val);
575:
576: (void) Gcomplex(&ydata_func.dummy_values[0], yhigh, 0.0);
577: ydata_func.dummy_values[2] = ydata_func.dummy_values[0];
578: evaluate_at(ydata_func.at, &val);
579: yhigh = undefined ? 0 : real(&val);
580: }
581: dummy_type = cp->type = INRANGE;
582:
583: if (polar) {
584: double newx, newy;
585: if (!(autoscale_r & 2) && y > rmax) {
586: cp->type = OUTRANGE;
587: }
588: if (!(autoscale_r & 1)) {
589: /* we store internally as if plotting r(t)-rmin */
590: y -= rmin;
591: }
592: newx = y * cos(x * ang2rad);
593: newy = y * sin(x * ang2rad);
594: #if 0 /* HBB 981118: added polar errorbars */
595: /* only lines and points supported with polar */
596: y = ylow = yhigh = newy;
597: x = xlow = xhigh = newx;
598: #else
599: y = newy;
600: x = newx;
601:
602: if (!(autoscale_r & 2) && yhigh > rmax) {
603: cp->type = OUTRANGE;
604: }
605: if (!(autoscale_r & 1)) {
606: /* we store internally as if plotting r(t)-rmin */
607: yhigh -= rmin;
608: }
609: newx = yhigh * cos(xhigh * ang2rad);
610: newy = yhigh * sin(xhigh * ang2rad);
611: yhigh = newy;
612: xhigh = newx;
613:
614: if (!(autoscale_r & 2) && ylow > rmax) {
615: cp->type = OUTRANGE;
616: }
617: if (!(autoscale_r & 1)) {
618: /* we store internally as if plotting r(t)-rmin */
619: ylow -= rmin;
620: }
621: newx = ylow * cos(xlow * ang2rad);
622: newy = ylow * sin(xlow * ang2rad);
623: ylow = newy;
624: xlow = newx;
625: #endif
626: }
627: /* return immediately if x or y are undefined
628: * we dont care if outrange for high/low.
629: * BUT if high/low undefined (ie log( < 0 ), no number is stored,
630: * but graphics.c doesn't know.
631: * explicitly store -VERYLARGE;
632: */
633: STORE_WITH_LOG_AND_FIXUP_RANGE(cp->x, x, cp->type, this_plot->x_axis, NOOP, return);
634: STORE_WITH_LOG_AND_FIXUP_RANGE(cp->xlow, xlow, dummy_type, this_plot->x_axis, NOOP, cp->xlow = -VERYLARGE);
635: STORE_WITH_LOG_AND_FIXUP_RANGE(cp->xhigh, xhigh, dummy_type, this_plot->x_axis, NOOP, cp->xhigh = -VERYLARGE);
636: STORE_WITH_LOG_AND_FIXUP_RANGE(cp->y, y, cp->type, this_plot->y_axis, NOOP, return);
637: STORE_WITH_LOG_AND_FIXUP_RANGE(cp->ylow, ylow, dummy_type, this_plot->y_axis, NOOP, cp->ylow = -VERYLARGE);
638: STORE_WITH_LOG_AND_FIXUP_RANGE(cp->yhigh, yhigh, dummy_type, this_plot->y_axis, NOOP, cp->yhigh = -VERYLARGE);
639: cp->z = width;
640: } /* store2d_point */
641:
642:
643:
644: /*
645: * print_points: a debugging routine to print out the points of a curve, and
646: * the curve structure. If curve<0, then we print the list of curves.
647: */
648:
649: #if 0 /* not used */
650: static char *plot_type_names[4] =
651: {
652: "Function", "Data", "3D Function", "3d data"
653: };
654: static char *plot_style_names[14] =
655: {
656: "Lines", "Points", "Impulses", "LinesPoints", "Dots", "XErrorbars",
657: "YErrorbars", "XYErrorbars", "BoxXYError", "Boxes", "Boxerror", "Steps",
658: "FSteps", "Vector"
659: };
660: static char *plot_smooth_names[5] =
661: {
662: "None", "Unique", "CSplines", "ACSplines", "Bezier", "SBezier"
663: };
664:
665: static void print_points(curve)
666: int curve; /* which curve to print */
667: {
668: register struct curve_points *this_plot;
669: int i;
670:
671: if (curve < 0) {
672: for (this_plot = first_plot, i = 0;
673: this_plot != NULL;
674: i++, this_plot = this_plot->next_cp) {
675: printf("Curve %d:\n", i);
676: if ((int) this_plot->plot_type >= 0 && (int) (this_plot->plot_type) < 4)
677: printf("Plot type %d: %s\n", (int) (this_plot->plot_type),
678: plot_type_names[(int) (this_plot->plot_type)]);
679: else
680: printf("Plot type %d: BAD\n", (int) (this_plot->plot_type));
681: if ((int) this_plot->plot_style >= 0 && (int) (this_plot->plot_style) < 14)
682: printf("Plot style %d: %s\n", (int) (this_plot->plot_style),
683: plot_style_names[(int) (this_plot->plot_style)]);
684: else
685: printf("Plot style %d: BAD\n", (int) (this_plot->plot_style));
686: if ((int) this_plot->plot_smooth >= 0 && (int) (this_plot->plot_smooth) < 6)
687: printf("Plot smooth style %d: %s\n", (int) (this_plot->plot_style),
688: plot_smooth_names[(int) (this_plot->plot_smooth)]);
689: else
690: printf("Plot smooth style %d: BAD\n", (int) (this_plot->plot_smooth));
691: printf("Plot title: '%s'\n", this_plot->title);
692: printf("Line type %d\n", this_plot->line_type);
693: printf("Point type %d\n", this_plot->point_type);
694: printf("max points %d\n", this_plot->p_max);
695: printf("current points %d\n", this_plot->p_count);
696: printf("\n");
697: }
698: } else {
699: for (this_plot = first_plot, i = 0;
700: i < curve && this_plot != NULL;
701: i++, this_plot = this_plot->next_cp);
702: if (this_plot == NULL)
703: printf("Curve %d does not exist; list has %d curves\n", curve, i);
704: else {
705: printf("Curve %d, %d points\n", curve, this_plot->p_count);
706: for (i = 0; i < this_plot->p_count; i++) {
707: printf("%c x=%g y=%g z=%g xlow=%g xhigh=%g ylow=%g yhigh=%g\n",
708: this_plot->points[i].type == INRANGE ? 'i'
709: : this_plot->points[i].type == OUTRANGE ? 'o'
710: : 'u',
711: this_plot->points[i].x,
712: this_plot->points[i].y,
713: this_plot->points[i].z,
714: this_plot->points[i].xlow,
715: this_plot->points[i].xhigh,
716: this_plot->points[i].ylow,
717: this_plot->points[i].yhigh);
718: }
719: printf("\n");
720: }
721: }
722: }
723: #endif /* not used */
724:
725: static void print_table(this_plot, plot_num)
726: struct curve_points *this_plot;
727: int plot_num;
728: {
729: int i, curve;
1.1.1.3 ! ohara 730: char *table_format = NULL;
! 731: /* The data format is determined by the format of the axis labels.
! 732: * See 'set format'. Patch by Don Taber
! 733: */
! 734: table_format = gp_alloc(strlen(xformat)+strlen(yformat)+5, "table format");
! 735: strcpy(table_format, xformat);
! 736: strcat(table_format, " ");
! 737: strcat(table_format, yformat);
! 738: strcat(table_format, " %c\n");
1.1 maekawa 739:
1.1.1.3 ! ohara 740: /* Not sure whether the missing plot styles require special treatment.
! 741: * They all fall under the "default" case right now. Lars
! 742: */
1.1 maekawa 743: for (curve = 0; curve < plot_num;
744: curve++, this_plot = this_plot->next_cp) {
1.1.1.3 ! ohara 745: fprintf(gpoutfile, "#Curve %d, %d points\n#x y", curve, this_plot->p_count);
! 746: switch (this_plot->plot_style) {
! 747: case BOXES:
! 748: case XERRORBARS:
! 749: fprintf(gpoutfile, " xlow xhigh");
! 750: break;
! 751: case BOXERROR:
! 752: case YERRORBARS:
! 753: fprintf(gpoutfile, " ylow yhigh");
! 754: break;
! 755: case BOXXYERROR:
! 756: case XYERRORBARS:
! 757: fprintf(gpoutfile, " xlow xhigh ylow yhigh");
! 758: break;
! 759: case FINANCEBARS:
! 760: case CANDLESTICKS:
! 761: default:
! 762: /* ? */
! 763: break;
! 764: }
! 765:
! 766: fprintf(gpoutfile, " type\n");
1.1 maekawa 767: for (i = 0; i < this_plot->p_count; i++) {
1.1.1.3 ! ohara 768: fprintf(gpoutfile, "%g %g",
1.1 maekawa 769: this_plot->points[i].x,
1.1.1.3 ! ohara 770: this_plot->points[i].y);
! 771: switch (this_plot->plot_style) {
! 772: case BOXES:
! 773: case XERRORBARS:
! 774: fprintf(gpoutfile, " %g %g",
! 775: this_plot->points[i].xlow,
! 776: this_plot->points[i].xhigh);
! 777: break;
! 778: case BOXERROR:
! 779: case YERRORBARS:
! 780: fprintf(gpoutfile, " %g %g",
! 781: this_plot->points[i].ylow,
! 782: this_plot->points[i].yhigh);
! 783: break;
! 784: case BOXXYERROR:
! 785: case XYERRORBARS:
! 786: fprintf(gpoutfile, " %g %g %g %g",
! 787: this_plot->points[i].xlow,
! 788: this_plot->points[i].xhigh,
! 789: this_plot->points[i].ylow,
! 790: this_plot->points[i].yhigh);
! 791: break;
! 792: case FINANCEBARS:
! 793: case CANDLESTICKS:
! 794: default:
! 795: /* ? */
! 796: break;
! 797: }
! 798: fprintf(gpoutfile, " %c\n",
1.1 maekawa 799: this_plot->points[i].type == INRANGE ? 'i'
800: : this_plot->points[i].type == OUTRANGE ? 'o'
801: : 'u');
802: }
803: fputc('\n', gpoutfile);
804: }
1.1.1.3 ! ohara 805:
! 806: /* two blank lines between plots in table output */
1.1 maekawa 807: fputc('\n', gpoutfile);
808: fflush(gpoutfile);
1.1.1.3 ! ohara 809:
! 810: free(table_format);
1.1 maekawa 811: }
812:
813: /*
814: * This parses the plot command after any range specifications. To support
815: * autoscaling on the x axis, we want any data files to define the x range,
816: * then to plot any functions using that range. We thus parse the input
817: * twice, once to pick up the data files, and again to pick up the functions.
818: * Definitions are processed twice, but that won't hurt.
819: * div - okay, it doesn't hurt, but every time an option as added for
820: * datafiles, code to parse it has to be added here. Change so that
821: * we store starting-token in the plot structure.
822: */
823: static void eval_plots()
824: {
825: register int i;
826: register struct curve_points *this_plot, **tp_ptr;
827:
828: int some_functions = 0;
829: int plot_num, line_num, point_num, xparam = 0;
830: char *xtitle;
831: int begin_token = c_token; /* so we can rewind for second pass */
832:
833: int uses_axis[AXIS_ARRAY_SIZE];
834:
835: uses_axis[FIRST_X_AXIS] =
836: uses_axis[FIRST_Y_AXIS] =
837: uses_axis[SECOND_X_AXIS] =
838: uses_axis[SECOND_Y_AXIS] = 0;
839:
840: /* Reset first_plot. This is usually done at the end of this function.
841: If there is an error within this function, the memory is left allocated,
842: since we cannot call cp_free if the list is incomplete. Making sure that
843: the list structure is always vaild requires some rewriting */
844: first_plot = NULL;
845:
846: tp_ptr = &(first_plot);
847: plot_num = 0;
848: line_num = 0; /* default line type */
849: point_num = 0; /* default point type */
850:
851: xtitle = NULL;
852:
853: /*** First Pass: Read through data files ***
854: * This pass serves to set the xrange and to parse the command, as well
855: * as filling in every thing except the function data. That is done after
856: * the xrange is defined.
857: */
858: while (TRUE) {
859: if (END_OF_COMMAND)
860: int_error("function to plot expected", c_token);
861:
862: if (is_definition(c_token)) {
863: define();
864: } else {
865: int x_axis = 0, y_axis = 0;
866: int specs;
867:
868: /* for datafile plot, record datafile spec for title */
869: int start_token = c_token, end_token;
870:
871: plot_num++;
872:
873: if (isstring(c_token)) { /* data file to plot */
874:
875: if (parametric && xparam)
876: int_error("previous parametric function not fully specified", c_token);
877:
878: if (*tp_ptr)
879: this_plot = *tp_ptr;
880: else { /* no memory malloc()'d there yet */
881: this_plot = cp_alloc(MIN_CRV_POINTS);
882: *tp_ptr = this_plot;
883: }
884: this_plot->plot_type = DATA;
885: this_plot->plot_style = data_style;
886: this_plot->plot_smooth = NONE;
887:
888: specs = df_open(NCOL); /* up to NCOL cols */
889: /* this parses data-file-specific modifiers only */
890: /* we'll sort points when we know style, if necessary */
891: if (df_binary)
892: int_error("2d binary files not yet supported", c_token);
893:
894: this_plot->token = end_token = c_token - 1; /* include modifiers in default title */
895:
896: } else {
897:
898: /* function to plot */
899:
900: some_functions = 1;
901: if (parametric) /* working on x parametric function */
902: xparam = 1 - xparam;
903: if (*tp_ptr) {
904: this_plot = *tp_ptr;
905: cp_extend(this_plot, samples + 1);
906: } else { /* no memory malloc()'d there yet */
907: this_plot = cp_alloc(samples + 1);
908: *tp_ptr = this_plot;
909: }
910: this_plot->plot_type = FUNC;
911: this_plot->plot_style = func_style;
912: dummy_func = &plot_func;
913: plot_func.at = temp_at();
914: dummy_func = NULL;
915: /* ignore it for now */
916: end_token = c_token - 1;
917: } /* end of IS THIS A FILE OR A FUNC block */
918:
919:
920: /* deal with smooth */
921: if (almost_equals(c_token, "s$mooth")) {
922:
923: if (END_OF_COMMAND)
924: int_error("expecting smooth parameter", c_token);
925: else {
926: c_token++;
927: if (almost_equals(c_token, "u$nique"))
928: this_plot->plot_smooth = UNIQUE;
929: else if (almost_equals(c_token, "a$csplines"))
930: this_plot->plot_smooth = ACSPLINES;
931: else if (almost_equals(c_token, "c$splines"))
932: this_plot->plot_smooth = CSPLINES;
933: else if (almost_equals(c_token, "b$ezier"))
934: this_plot->plot_smooth = BEZIER;
935: else if (almost_equals(c_token, "s$bezier"))
936: this_plot->plot_smooth = SBEZIER;
937: else
938: int_error("expecting 'unique', 'acsplines', 'csplines', 'bezier' or 'sbezier'", c_token);
939: }
940: this_plot->plot_style = LINES;
941: c_token++; /* skip format */
942: }
943: /* look for axes/axis */
944:
945: if (almost_equals(c_token, "ax$es") || almost_equals(c_token, "ax$is")) {
946: if (parametric && xparam)
947: int_error("previous parametric function not fully specified", c_token);
948:
949: if (equals(++c_token, "x1y1")) {
950: x_axis = FIRST_X_AXIS;
951: y_axis = FIRST_Y_AXIS;
952: ++c_token;
953: } else if (equals(c_token, "x2y2")) {
954: x_axis = SECOND_X_AXIS;
955: y_axis = SECOND_Y_AXIS;
956: ++c_token;
957: } else if (equals(c_token, "x1y2")) {
958: x_axis = FIRST_X_AXIS;
959: y_axis = SECOND_Y_AXIS;
960: ++c_token;
961: } else if (equals(c_token, "x2y1")) {
962: x_axis = SECOND_X_AXIS;
963: y_axis = FIRST_Y_AXIS;
964: ++c_token;
965: } else
966: int_error("axes must be x1y1, x1y2, x2y1 or x2y2", c_token);
967: } else {
968: x_axis = FIRST_X_AXIS;
969: y_axis = FIRST_Y_AXIS;
970: }
971:
972:
973: this_plot->x_axis = x_axis;
974: this_plot->y_axis = y_axis;
975:
976: /* we can now do some checks that we deferred earlier */
977:
978: if (this_plot->plot_type == DATA) {
979: if (!(uses_axis[x_axis] & 1) && autoscale_lx) {
980: if (auto_array[x_axis] & 1)
981: min_array[x_axis] = VERYLARGE;
982: if (auto_array[x_axis] & 2)
983: max_array[x_axis] = -VERYLARGE;
984: }
985: if (datatype[x_axis] == TIME) {
986: if (specs < 2)
987: int_error("Need full using spec for x time data", c_token);
988: df_timecol[0] = 1;
989: }
990: if (datatype[y_axis] == TIME) {
991: if (specs < 1)
992: int_error("Need using spec for y time data", c_token);
993: df_timecol[y_axis] = 1; /* need other cols, but I'm lazy */
994: }
995: uses_axis[x_axis] |= 1; /* separate record of datafile and func */
996: uses_axis[y_axis] |= 1;
997: } else if (!parametric || !xparam) {
998: /* for x part of a parametric function, axes are possibly wrong */
999: uses_axis[x_axis] |= 2; /* separate record of data and func */
1000: uses_axis[y_axis] |= 2;
1001: }
1002: if (almost_equals(c_token, "t$itle")) {
1003: if (parametric) {
1004: if (xparam)
1005: int_error("\"title\" allowed only after parametric function fully specified", c_token);
1006: else if (xtitle != NULL)
1007: xtitle[0] = '\0'; /* Remove default title . */
1008: }
1009: c_token++;
1010: if (isstring(c_token)) {
1011: m_quote_capture(&(this_plot->title), c_token, c_token);
1012: } else {
1013: int_error("expecting \"title\" for plot", c_token);
1014: }
1015: c_token++;
1016: } else if (almost_equals(c_token, "not$itle")) {
1017: if (xtitle != NULL)
1018: xtitle[0] = '\0';
1019: c_token++;
1020: } else {
1021: m_capture(&(this_plot->title), start_token, end_token);
1022: if (xparam)
1023: xtitle = this_plot->title;
1024: }
1025:
1026:
1027: if (almost_equals(c_token, "w$ith")) {
1028: if (parametric && xparam)
1029: int_error("\"with\" allowed only after parametric function fully specified", c_token);
1030: this_plot->plot_style = get_style();
1031: }
1032: /* pick up line/point specs
1033: * - point spec allowed if style uses points, ie style&2 != 0
1034: * - keywords for lt and pt are optional
1035: */
1036: LP_PARSE(this_plot->lp_properties, 1, this_plot->plot_style & 2,
1037: line_num, point_num);
1038:
1039: /* allow old-style syntax too - ignore case lt 3 4 for example */
1040: if (!equals(c_token, ",") && !END_OF_COMMAND) {
1041: struct value t;
1042: this_plot->lp_properties.l_type =
1043: this_plot->lp_properties.p_type = (int) real(const_express(&t)) - 1;
1044:
1045: if (!equals(c_token, ",") && !END_OF_COMMAND)
1046: this_plot->lp_properties.p_type = (int) real(const_express(&t)) - 1;
1047: }
1048: if (!xparam) {
1049: if (this_plot->plot_style & 2) /* style includes points */
1050: ++point_num;
1051: ++line_num;
1052: }
1053: if (this_plot->plot_type == DATA) {
1054: /* actually get the data now */
1055: get_data(this_plot);
1056:
1057: /* sort */
1058: switch (this_plot->plot_smooth) { /* sort and average, if */
1059: case UNIQUE: /* the style requires */
1060: case CSPLINES:
1061: case ACSPLINES:
1062: case SBEZIER:
1063: sort_points(this_plot);
1064: cp_implode(this_plot);
1065: break;
1066: default:
1067: ; /* keep gcc -Wall happy */
1068: }
1069: switch (this_plot->plot_smooth) { /* create new data set */
1070: case SBEZIER: /* by evaluation of */
1071: case BEZIER: /* interpolation routines */
1072: case ACSPLINES:
1073: case CSPLINES:
1074: gen_interp(this_plot);
1075: break;
1076: default:
1077: ; /* keep gcc -Wall happy */
1078: }
1079:
1080: /* now that we know the plot style, adjust the x- and yrange */
1081: /* adjust_range(this_plot); no longer needed */
1082: }
1083: this_plot->token = c_token; /* save end of plot for second pass */
1084: tp_ptr = &(this_plot->next_cp);
1085:
1086: } /* !is_defn */
1087:
1088: if (equals(c_token, ","))
1089: c_token++;
1090: else
1091: break;
1092: }
1093:
1094: if (parametric && xparam)
1095: int_error("parametric function not fully specified", NO_CARET);
1096:
1097:
1098: /*** Second Pass: Evaluate the functions ***/
1099: /*
1100: * Everything is defined now, except the function data. We expect no
1101: * syntax errors, etc, since the above parsed it all. This makes the code
1102: * below simpler. If autoscale_ly, the yrange may still change.
1103: * we stored last token of each plot, so we dont need to do everything again
1104: */
1105:
1106: /* give error if xrange badly set from missing datafile error
1107: * parametric or polar fns can still affect x ranges
1108: */
1109:
1110: if (!parametric && !polar) {
1111: if (min_array[FIRST_X_AXIS] == VERYLARGE ||
1112: max_array[FIRST_X_AXIS] == -VERYLARGE)
1113: int_error("x range is invalid", c_token);
1114: /* check that xmin -> xmax is not too small */
1115: fixup_range(FIRST_X_AXIS, "x");
1116:
1117: if (uses_axis[SECOND_X_AXIS] & 1) {
1118: /* some data plots with x2 */
1119: if (min_array[SECOND_X_AXIS] == VERYLARGE ||
1120: max_array[SECOND_X_AXIS] == -VERYLARGE)
1121: int_error("x2 range is invalid", c_token);
1122: /* check that x2min -> x2max is not too small */
1123: fixup_range(SECOND_X_AXIS, "x2");
1124: } else if (auto_array[SECOND_X_AXIS]) {
1125: /* copy x1's range */
1126: if (auto_array[SECOND_X_AXIS] & 1)
1127: min_array[SECOND_X_AXIS] = min_array[FIRST_X_AXIS];
1128: if (auto_array[SECOND_X_AXIS] & 2)
1129: max_array[SECOND_X_AXIS] = max_array[FIRST_X_AXIS];
1130: }
1131: }
1132: if (some_functions) {
1133:
1134: /* call the controlled variable t, since x_min can also mean smallest x */
1135: double t_min, t_max, t_step;
1136:
1137: if (parametric || polar) {
1138: if (!(uses_axis[FIRST_X_AXIS] & 1)) {
1139: /* these have not yet been set to full width */
1140: if (auto_array[FIRST_X_AXIS] & 1)
1141: min_array[FIRST_X_AXIS] = VERYLARGE;
1142: if (auto_array[FIRST_X_AXIS] & 2)
1143: max_array[FIRST_X_AXIS] = -VERYLARGE;
1144: }
1145: if (!(uses_axis[SECOND_X_AXIS] & 1)) {
1146: if (auto_array[SECOND_X_AXIS] & 1)
1147: min_array[SECOND_X_AXIS] = VERYLARGE;
1148: if (auto_array[SECOND_X_AXIS] & 2)
1149: max_array[SECOND_X_AXIS] = -VERYLARGE;
1150: }
1151: }
1152: #define SET_DUMMY_RANGE(AXIS) \
1153: do{ assert(!polar && !parametric); \
1154: if (log_array[AXIS]) {\
1155: if (min_array[AXIS] <= 0.0 || max_array[AXIS] <= 0.0)\
1156: int_error("x/x2 range must be greater than 0 for log scale!", NO_CARET);\
1157: t_min = log(min_array[AXIS])/log_base_array[AXIS]; t_max = log(max_array[AXIS])/log_base_array[AXIS];\
1158: } else {\
1159: t_min = min_array[AXIS]; t_max = max_array[AXIS];\
1160: }\
1161: t_step = (t_max - t_min) / (samples - 1); \
1162: }while(0)
1163:
1164: if (parametric || polar) {
1165: t_min = min_array[T_AXIS];
1166: t_max = max_array[T_AXIS];
1167: t_step = (t_max - t_min) / (samples - 1);
1168: }
1169: /* else we'll do it on each plot */
1170:
1171: tp_ptr = &(first_plot);
1172: plot_num = 0;
1173: this_plot = first_plot;
1174: c_token = begin_token; /* start over */
1175:
1176: /* Read through functions */
1177: while (TRUE) {
1178: if (is_definition(c_token)) {
1179: define();
1180: } else {
1181: int x_axis = this_plot->x_axis;
1182: int y_axis = this_plot->y_axis;
1183:
1184: plot_num++;
1185: if (!isstring(c_token)) { /* function to plot */
1186: if (parametric) { /* toggle parametric axes */
1187: xparam = 1 - xparam;
1188: }
1189: dummy_func = &plot_func;
1190: plot_func.at = temp_at(); /* reparse function */
1191:
1192: if (!parametric && !polar) {
1193: SET_DUMMY_RANGE(x_axis);
1194: }
1195: for (i = 0; i < samples; i++) {
1196: double temp;
1197: struct value a;
1198: double t = t_min + i * t_step;
1199: /* parametric/polar => NOT a log quantity */
1200: double x = (!parametric && !polar &&
1201: log_array[x_axis]) ? pow(base_array[x_axis], t) : t;
1202:
1203: (void) Gcomplex(&plot_func.dummy_values[0], x, 0.0);
1204: evaluate_at(plot_func.at, &a);
1205:
1206: if (undefined || (fabs(imag(&a)) > zero)) {
1207: this_plot->points[i].type = UNDEFINED;
1208: continue;
1209: }
1210: temp = real(&a);
1211:
1212: this_plot->points[i].z = -1.0; /* width of box not specified */
1213: this_plot->points[i].type = INRANGE; /* for the moment */
1214:
1215: if (parametric) {
1216: /* we cannot do range-checking now, since for
1217: * the x function we did not know which axes
1218: * we were using
1219: * DO NOT TAKE LOGS YET - do it in parametric_fixup
1220: */
1221: this_plot->points[i].x = t; /* ignored, actually... */
1222: this_plot->points[i].y = temp;
1223: } else if (polar) {
1224: double y;
1225: if (!(autoscale_r & 2) && temp > rmax)
1226: this_plot->points[i].type = OUTRANGE;
1227: if (!(autoscale_r & 1))
1228: temp -= rmin;
1229: y = temp * sin(x * ang2rad);
1230: x = temp * cos(x * ang2rad);
1231: temp = y;
1232: STORE_WITH_LOG_AND_FIXUP_RANGE(this_plot->points[i].x, x, this_plot->points[i].type,
1233: x_axis, NOOP, goto come_here_if_undefined);
1234: STORE_WITH_LOG_AND_FIXUP_RANGE(this_plot->points[i].y, y, this_plot->points[i].type,
1235: y_axis, NOOP, goto come_here_if_undefined);
1236: } else { /* neither parametric or polar */
1237: /* If non-para, it must be INRANGE */
1238: this_plot->points[i].x = t; /* logscale ? log(x) : x */
1239:
1240: STORE_WITH_LOG_AND_FIXUP_RANGE(this_plot->points[i].y, temp, this_plot->points[i].type,
1241: y_axis + (x_axis - y_axis) * xparam, NOOP, goto come_here_if_undefined);
1242:
1243: come_here_if_undefined: /* could not use a continue in this case */
1244: ; /* ansi requires a statement after a label */
1245: }
1246:
1247: } /* loop over samples */
1248: this_plot->p_count = i; /* samples */
1249: }
1250: c_token = this_plot->token; /* skip all modifers func / whole of data plots */
1251:
1252: tp_ptr = &(this_plot->next_cp); /* used below */
1253: this_plot = this_plot->next_cp;
1254: }
1255:
1256: if (equals(c_token, ","))
1257: c_token++;
1258: else
1259: break;
1260: }
1261:
1262: if (parametric) {
1263: /* Now actually fix the plot pairs to be single plots */
1264: /* also fixes up polar&¶metric fn plots */
1265: parametric_fixup(first_plot, &plot_num);
1266: /* we omitted earlier check for range too small */
1267: fixup_range(FIRST_X_AXIS, "x");
1268: if (uses_axis[SECOND_X_AXIS]) {
1269: fixup_range(SECOND_X_AXIS, "x2");
1270: }
1271: }
1272: } /* some_functions */
1273: /* throw out all curve_points at end of list, that we don't need */
1274: cp_free(*tp_ptr);
1275: *tp_ptr = NULL;
1276:
1277:
1278: /* if first_plot is NULL, we have no functions or data at all. This can
1279: happen, if you type "plot x=5", since x=5 is a variable assignment */
1280:
1281: if (plot_num == 0 || first_plot == NULL) {
1282: int_error("no functions or data to plot", c_token);
1283: }
1.1.1.2 maekawa 1284:
1285:
1.1 maekawa 1286: if (uses_axis[FIRST_X_AXIS]) {
1287: if (max_array[FIRST_X_AXIS] == -VERYLARGE ||
1.1.1.3 ! ohara 1288: min_array[FIRST_X_AXIS] == VERYLARGE) {
! 1289: cp_free(first_plot);
! 1290: first_plot = NULL;
1.1 maekawa 1291: int_error("all points undefined!", NO_CARET);
1.1.1.3 ! ohara 1292: }
1.1 maekawa 1293: FIXUP_RANGE_FOR_LOG(FIRST_X_AXIS, x);
1294: }
1295: if (uses_axis[SECOND_X_AXIS]) {
1296: if (max_array[SECOND_X_AXIS] == -VERYLARGE ||
1.1.1.3 ! ohara 1297: min_array[SECOND_X_AXIS] == VERYLARGE) {
! 1298: cp_free(first_plot);
! 1299: first_plot = NULL;
1.1 maekawa 1300: int_error("all points undefined!", NO_CARET);
1.1.1.3 ! ohara 1301: }
1.1 maekawa 1302: FIXUP_RANGE_FOR_LOG(SECOND_X_AXIS, x2);
1303: } else {
1304: assert(uses_axis[FIRST_X_AXIS]);
1305: if (auto_array[SECOND_X_AXIS] & 1)
1306: min_array[SECOND_X_AXIS] = min_array[FIRST_X_AXIS];
1307: if (auto_array[SECOND_X_AXIS] & 2)
1308: max_array[SECOND_X_AXIS] = max_array[FIRST_X_AXIS];
1.1.1.2 maekawa 1309: if (! auto_array[SECOND_X_AXIS])
1310: FIXUP_RANGE_FOR_LOG(SECOND_X_AXIS, x2);
1.1 maekawa 1311: }
1312: if (!uses_axis[FIRST_X_AXIS]) {
1313: assert(uses_axis[SECOND_X_AXIS]);
1314: if (auto_array[FIRST_X_AXIS] & 1)
1315: min_array[FIRST_X_AXIS] = min_array[SECOND_X_AXIS];
1316: if (auto_array[FIRST_X_AXIS] & 2)
1317: max_array[FIRST_X_AXIS] = max_array[SECOND_X_AXIS];
1318: }
1.1.1.2 maekawa 1319:
1320:
1.1 maekawa 1321: if (uses_axis[FIRST_Y_AXIS]) {
1322: if (max_array[FIRST_Y_AXIS] == -VERYLARGE ||
1.1.1.3 ! ohara 1323: min_array[FIRST_Y_AXIS] == VERYLARGE) {
! 1324: cp_free(first_plot);
! 1325: first_plot = NULL;
1.1 maekawa 1326: int_error("all points undefined!", NO_CARET);
1.1.1.3 ! ohara 1327: }
1.1 maekawa 1328: fixup_range(FIRST_Y_AXIS, "y");
1329: FIXUP_RANGE_FOR_LOG(FIRST_Y_AXIS, y);
1.1.1.2 maekawa 1330: }
1.1 maekawa 1331: if (uses_axis[SECOND_Y_AXIS]) {
1332: if (max_array[SECOND_Y_AXIS] == -VERYLARGE ||
1.1.1.3 ! ohara 1333: min_array[SECOND_Y_AXIS] == VERYLARGE) {
! 1334: cp_free(first_plot);
! 1335: first_plot = NULL;
1.1 maekawa 1336: int_error("all points undefined!", NO_CARET);
1.1.1.3 ! ohara 1337: }
1.1 maekawa 1338: fixup_range(SECOND_Y_AXIS, "y2");
1339: FIXUP_RANGE_FOR_LOG(SECOND_Y_AXIS, y2);
1340: } else {
1.1.1.2 maekawa 1341: /* else we want to copy y2 range */
1.1 maekawa 1342: assert(uses_axis[FIRST_Y_AXIS]);
1343: if (auto_array[SECOND_Y_AXIS] & 1)
1344: min_array[SECOND_Y_AXIS] = min_array[FIRST_Y_AXIS];
1345: if (auto_array[SECOND_Y_AXIS] & 2)
1346: max_array[SECOND_Y_AXIS] = max_array[FIRST_Y_AXIS];
1.1.1.2 maekawa 1347: /* Log() fixup is only necessary if the range was *not* copied from
1348: * the (already logarithmized) yrange */
1349: if (! auto_array[SECOND_Y_AXIS])
1350: FIXUP_RANGE_FOR_LOG(SECOND_Y_AXIS, y2);
1.1 maekawa 1351: }
1352: if (!uses_axis[FIRST_Y_AXIS]) {
1353: assert(uses_axis[SECOND_Y_AXIS]);
1354: if (auto_array[FIRST_Y_AXIS] & 1)
1355: min_array[FIRST_Y_AXIS] = min_array[SECOND_Y_AXIS];
1356: if (auto_array[FIRST_Y_AXIS] & 2)
1357: max_array[FIRST_Y_AXIS] = max_array[SECOND_Y_AXIS];
1358: }
1.1.1.2 maekawa 1359:
1360: if (strcmp(term->name, "table") == 0)
1.1 maekawa 1361: print_table(first_plot, plot_num);
1362: else {
1363: START_LEAK_CHECK(); /* check for memory leaks in this routine */
1364:
1365: /* do_plot now uses max_array[], etc */
1366: do_plot(first_plot, plot_num);
1367:
1368: END_LEAK_CHECK();
1.1.1.3 ! ohara 1369:
! 1370: /* after do_plot(), min_array[] and max_array[]
! 1371: contain the plotting range actually used (rounded
! 1372: to tic marks, not only the min/max data values)
! 1373: --> save them now for writeback if requested */
! 1374:
! 1375: #define SAVE_WRITEBACK(axis) /* ULIG */ \
! 1376: if(range_flags[axis]&RANGE_WRITEBACK) { \
! 1377: set_writeback_min(axis,min_array[axis]); \
! 1378: set_writeback_max(axis,max_array[axis]); \
! 1379: }
! 1380: SAVE_WRITEBACK(FIRST_X_AXIS)
! 1381: SAVE_WRITEBACK(FIRST_Y_AXIS)
! 1382: SAVE_WRITEBACK(FIRST_Z_AXIS)
! 1383: SAVE_WRITEBACK(SECOND_X_AXIS)
! 1384: SAVE_WRITEBACK(SECOND_Y_AXIS)
! 1385: SAVE_WRITEBACK(SECOND_Z_AXIS)
! 1386: SAVE_WRITEBACK(T_AXIS)
! 1387: SAVE_WRITEBACK(R_AXIS)
! 1388: SAVE_WRITEBACK(U_AXIS)
! 1389: SAVE_WRITEBACK(V_AXIS)
1.1 maekawa 1390: }
1391:
1392: /* if we get here, all went well, so record this line for replot */
1393:
1394: if (plot_token != -1) {
1395: /* note that m_capture also frees the old replot_line */
1396: m_capture(&replot_line, plot_token, c_token - 1);
1397: plot_token = -1;
1398: }
1399: cp_free(first_plot);
1400: first_plot = NULL;
1401: } /* eval_plots */
1402:
1403:
1404:
1405:
1406: static void parametric_fixup(start_plot, plot_num)
1407: struct curve_points *start_plot;
1408: int *plot_num;
1409: /*
1410: * The hardest part of this routine is collapsing the FUNC plot types in the
1411: * list (which are garanteed to occur in (x,y) pairs while preserving the
1412: * non-FUNC type plots intact. This means we have to work our way through
1413: * various lists. Examples (hand checked): start_plot:F1->F2->NULL ==>
1414: * F2->NULL start_plot:F1->F2->F3->F4->F5->F6->NULL ==> F2->F4->F6->NULL
1415: * start_plot:F1->F2->D1->D2->F3->F4->D3->NULL ==> F2->D1->D2->F4->D3->NULL
1416: *
1417: */
1418: {
1419: struct curve_points *xp, *new_list = NULL, *free_list = NULL;
1420: struct curve_points **last_pointer = &new_list;
1421: int i, tlen, curve;
1422: char *new_title;
1423:
1424: /*
1425: * Ok, go through all the plots and move FUNC types together. Note: this
1426: * originally was written to look for a NULL next pointer, but gnuplot
1427: * wants to be sticky in grabbing memory and the right number of items in
1428: * the plot list is controlled by the plot_num variable.
1429: *
1430: * Since gnuplot wants to do this sticky business, a free_list of
1431: * curve_points is kept and then tagged onto the end of the plot list as
1432: * this seems more in the spirit of the original memory behavior than
1433: * simply freeing the memory. I'm personally not convinced this sort of
1434: * concern is worth it since the time spent computing points seems to
1435: * dominate any garbage collecting that might be saved here...
1436: */
1437: new_list = xp = start_plot;
1438: curve = 0;
1439:
1440: while (++curve <= *plot_num) {
1441: if (xp->plot_type == FUNC) {
1442: /* Here's a FUNC parametric function defined as two parts. */
1443: struct curve_points *yp = xp->next_cp;
1444:
1445: --(*plot_num);
1446:
1447: assert(xp->p_count == yp->p_count);
1448:
1449: /* because syntax is plot x(t), y(t) axes ..., only
1450: * the y function axes are correct
1451: */
1452:
1453:
1454: /*
1455: * Go through all the points assigning the y's from xp to be the x's
1456: * for yp. In polar mode, we need to check max's and min's as we go.
1457: */
1458:
1459: for (i = 0; i < yp->p_count; ++i) {
1460: if (polar) {
1461: double r = yp->points[i].y;
1462: double t = xp->points[i].y * ang2rad;
1463: double x, y;
1464: if (!(autoscale_r & 2) && r > rmax)
1465: yp->points[i].type = OUTRANGE;
1466: if (!(autoscale_r & 1))
1467: r -= rmin; /* store internally as if plotting r(t)-rmin */
1468: x = r * cos(t);
1469: y = r * sin(t);
1470: /* we hadn't done logs when we stored earlier */
1471: STORE_WITH_LOG_AND_FIXUP_RANGE(yp->points[i].x, x, yp->points[i].type,
1472: xp->x_axis, NOOP, NOOP);
1473: STORE_WITH_LOG_AND_FIXUP_RANGE(yp->points[i].y, y, yp->points[i].type,
1474: xp->y_axis, NOOP, NOOP);
1475: } else {
1476: double x = xp->points[i].y;
1477: double y = yp->points[i].y;
1478: STORE_WITH_LOG_AND_FIXUP_RANGE(yp->points[i].x, x,
1479: yp->points[i].type, yp->x_axis, NOOP, NOOP);
1480: STORE_WITH_LOG_AND_FIXUP_RANGE(yp->points[i].y, y,
1481: yp->points[i].type, yp->y_axis, NOOP, NOOP);
1482: }
1483: }
1484:
1485: /* Ok, fix up the title to include both the xp and yp plots. */
1486: if (xp->title && xp->title[0] != '\0' && yp->title) {
1487: tlen = strlen(yp->title) + strlen(xp->title) + 3;
1.1.1.3 ! ohara 1488: new_title = gp_alloc(tlen, "string");
1.1 maekawa 1489: strcpy(new_title, xp->title);
1490: strcat(new_title, ", "); /* + 2 */
1491: strcat(new_title, yp->title); /* + 1 = + 3 */
1492: free(yp->title);
1493: yp->title = new_title;
1494: }
1495: /* move xp to head of free list */
1496: xp->next_cp = free_list;
1497: free_list = xp;
1498:
1499: /* append yp to new_list */
1500: *last_pointer = yp;
1501: last_pointer = &(yp->next_cp);
1502: xp = yp->next_cp;
1503:
1504: } else { /* data plot */
1505: assert(*last_pointer == xp);
1506: last_pointer = &(xp->next_cp);
1507: xp = xp->next_cp;
1508: }
1509: } /* loop over plots */
1510:
1511: first_plot = new_list;
1512:
1513: /* Ok, stick the free list at the end of the curve_points plot list. */
1514: *last_pointer = free_list;
1515: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>