File: [local] / OpenXM_contrib / gnuplot / Attic / plot2d.c (download)
Revision 1.1.1.2 (vendor branch), Sat Jan 22 14:15:59 2000 UTC (24 years, 8 months ago) by maekawa
Branch: GNUPLOT
CVS Tags: maekawa-ipv6, VERSION_3_7_1, RELEASE_20000124, RELEASE_1_2_2, RELEASE_1_2_1, RELEASE_1_1_3, RELEASE_1_1_2 Changes since 1.1.1.1: +44 -19
lines
Import gnuplot 3.7.1
|
#ifndef lint
static char *RCSid = "$Id: plot2d.c,v 1.16.2.4 1999/10/11 12:18:56 lhecking Exp $";
#endif
/* GNUPLOT - plot2d.c */
/*[
* Copyright 1986 - 1993, 1998 Thomas Williams, Colin Kelley
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose with or without fee is hereby granted,
* provided that the above copyright notice appear in all copies and
* that both that copyright notice and this permission notice appear
* in supporting documentation.
*
* Permission to modify the software is granted, but not the right to
* distribute the complete modified source code. Modifications are to
* be distributed as patches to the released version. Permission to
* distribute binaries produced by compiling modified sources is granted,
* provided you
* 1. distribute the corresponding source modifications from the
* released version in the form of a patch file along with the binaries,
* 2. add special version identification to distinguish your version
* in addition to the base release version number,
* 3. provide your name and address as the primary contact for the
* support of your modified version, and
* 4. retain our contact information in regard to use of the base
* software.
* Permission to distribute the released version of the source code along
* with corresponding source modifications in the form of a patch file is
* granted with same provisions 2 through 4 for binary distributions.
*
* This software is provided "as is" without express or implied warranty
* to the extent permitted by applicable law.
]*/
#include "plot.h"
#include "setshow.h"
#include "fit.h"
#include "binary.h"
#ifndef _Windows
# include "help.h"
#endif
#ifndef STDOUT
#define STDOUT 1
#endif
/* static prototypes */
void plotrequest __PROTO((void));
void plot3drequest __PROTO((void));
void define __PROTO((void));
static void get_data __PROTO((struct curve_points * this_plot));
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));
static void print_table __PROTO((struct curve_points * first_plot, int plot_num));
static void eval_plots __PROTO((void));
static void parametric_fixup __PROTO((struct curve_points * start_plot, int *plot_num));
/* the curves/surfaces of the plot */
struct curve_points *first_plot = NULL;
static struct udft_entry plot_func;
extern struct udft_entry *dummy_func;
/* jev -- for passing data thru user-defined function */
/* Needed by datafile.c */
struct udft_entry ydata_func;
extern int datatype[];
extern char timefmt[];
extern TBOOLEAN is_3d_plot;
extern int plot_token;
/* in order to support multiple axes, and to
* simplify ranging in parametric plots, we use
* arrays to store some things.
* Elements are z = 0, y1 = 1, x1 = 2, [z2 =4 ], y2 = 5, x2 = 6
* these are given symbolic names in plot.h
*/
/* Were declared in command.c */
double min_array[AXIS_ARRAY_SIZE], max_array[AXIS_ARRAY_SIZE];
int auto_array[AXIS_ARRAY_SIZE];
TBOOLEAN log_array[AXIS_ARRAY_SIZE];
double base_array[AXIS_ARRAY_SIZE];
double log_base_array[AXIS_ARRAY_SIZE];
/* Deleted from setshow.h and renamed */
extern FILE *gpoutfile;
/* if user specifies [10:-10] we use [-10:10] internally, and swap at end */
int reverse_range[AXIS_ARRAY_SIZE];
/* info from datafile module */
extern int df_datum;
extern int df_line_number;
extern int df_no_use_specs;
extern int df_eof;
extern int df_timecol[];
extern TBOOLEAN df_binary;
#define Inc_c_token if (++c_token >= num_tokens) \
int_error ("Syntax error", c_token);
/*
* IMHO, code is getting too cluttered with repeated chunks of
* code. Some macros to simplify, I hope.
*
* do { } while(0) is comp.lang.c recommendation for complex macros
* also means that break can be specified as an action, and it will
*
*/
/* copy scalar data to arrays
* optimiser should optimise infinite away
* dont know we have to support ranges [10:-10] - lets reverse
* it for now, then fix it at the end.
*/
#define INIT_ARRAYS(axis, min, max, auto, is_log, base, log_base, infinite) \
do{auto_array[axis] = auto; \
min_array[axis] = (infinite && (auto&1)) ? VERYLARGE : min; \
max_array[axis] = (infinite && (auto&2)) ? -VERYLARGE : max; \
log_array[axis] = is_log; base_array[axis] = base; log_base_array[axis] = log_base;\
}while(0)
/* handle reversed ranges */
#define CHECK_REVERSE(axis) \
do{\
if (auto_array[axis] == 0 && max_array[axis] < min_array[axis]) {\
double temp = min_array[axis]; min_array[axis] = max_array[axis]; max_array[axis] = temp;\
reverse_range[axis] = 1; \
} else reverse_range[axis] = (range_flags[axis]&RANGE_REVERSE); \
}while(0)
/* get optional [min:max] */
#define LOAD_RANGE(axis) \
do {\
if (equals(c_token, "[")) { \
c_token++; \
auto_array[axis] = load_range(axis,&min_array[axis], &max_array[axis], auto_array[axis]);\
if (!equals(c_token, "]"))\
int_error("']' expected", c_token);\
c_token++;\
}\
} while (0)
/* store VALUE or log(VALUE) in STORE, set TYPE as appropriate
* Do OUT_ACTION or UNDEF_ACTION as appropriate
* adjust range provided type is INRANGE (ie dont adjust y if x is outrange
* VALUE must not be same as STORE
*/
#define STORE_WITH_LOG_AND_FIXUP_RANGE(STORE, VALUE, TYPE, AXIS, OUT_ACTION, UNDEF_ACTION)\
do { if (log_array[AXIS]) { if (VALUE<0.0) {TYPE = UNDEFINED; UNDEF_ACTION; break;} \
else if (VALUE == 0.0){STORE = -VERYLARGE; TYPE = OUTRANGE; OUT_ACTION; break;} \
else { STORE = log(VALUE)/log_base_array[AXIS]; } \
} else STORE = VALUE; \
if (TYPE != INRANGE) break; /* dont set y range if x is outrange, for example */ \
if ( VALUE<min_array[AXIS] ) { \
if (auto_array[AXIS] & 1) min_array[AXIS] = VALUE; else { TYPE = OUTRANGE; OUT_ACTION; break; } \
} \
if ( VALUE>max_array[AXIS] ) { \
if (auto_array[AXIS] & 2) max_array[AXIS] = VALUE; else { TYPE = OUTRANGE; OUT_ACTION; } \
} \
} while(0)
/* use this instead empty macro arguments to work around NeXT cpp bug */
/* if this fails on any system, we might use ((void)0) */
#define NOOP /* */
/* check range and take logs of min and max if logscale
* this also restores min and max for ranges like [10:-10]
*/
#ifdef HAVE_STRINGIZE
# define LOG_MSG(x) #x " range must be greater than 0 for log scale!"
#else
# define LOG_MSG(x) "x range must be greater than 0 for log scale!"
#endif
#define FIXUP_RANGE_FOR_LOG(AXIS, WHICH) \
do { if (reverse_range[AXIS]) { \
double temp = min_array[AXIS]; \
min_array[AXIS] = max_array[AXIS]; \
max_array[AXIS] = temp; \
}\
if (log_array[AXIS]) { \
if (min_array[AXIS] <= 0.0 || max_array[AXIS] <= 0.0) \
int_error(LOG_MSG(WHICH), NO_CARET); \
min_array[AXIS] = log(min_array[AXIS])/log_base_array[AXIS]; \
max_array[AXIS] = log(max_array[AXIS])/log_base_array[AXIS]; \
} \
} while(0)
void plotrequest()
/*
* In the parametric case we can say plot [a= -4:4] [-2:2] [-1:1] sin(a),a**2
* while in the non-parametric case we would say only plot [b= -2:2] [-1:1]
* sin(b)
*/
{
int dummy_token = -1;
if (!term) /* unknown */
int_error("use 'set term' to set terminal type first", c_token);
is_3d_plot = FALSE;
if (parametric && strcmp(dummy_var[0], "u") == 0)
strcpy(dummy_var[0], "t");
/* initialise the arrays from the 'set' scalars */
INIT_ARRAYS(FIRST_X_AXIS, xmin, xmax, autoscale_x, is_log_x, base_log_x, log_base_log_x, 0);
INIT_ARRAYS(FIRST_Y_AXIS, ymin, ymax, autoscale_y, is_log_y, base_log_y, log_base_log_y, 1);
INIT_ARRAYS(SECOND_X_AXIS, x2min, x2max, autoscale_x2, is_log_x2, base_log_x2, log_base_log_x2, 0);
INIT_ARRAYS(SECOND_Y_AXIS, y2min, y2max, autoscale_y2, is_log_y2, base_log_y2, log_base_log_y2, 1);
min_array[T_AXIS] = tmin;
max_array[T_AXIS] = tmax;
if (equals(c_token, "[")) {
c_token++;
if (isletter(c_token)) {
if (equals(c_token + 1, "=")) {
dummy_token = c_token;
c_token += 2;
} else {
/* oops; probably an expression with a variable. */
/* Parse it as an xmin expression. */
/* used to be: int_error("'=' expected",c_token); */
}
} {
int axis = (parametric || polar) ? T_AXIS : FIRST_X_AXIS;
auto_array[axis] = load_range(axis, &min_array[axis], &max_array[axis], auto_array[axis]);
if (!equals(c_token, "]"))
int_error("']' expected", c_token);
c_token++;
} /* end of scope of 'axis' */
} /* first '[' */
if (parametric || polar) /* set optional x ranges */
LOAD_RANGE(FIRST_X_AXIS);
else {
/* order of t doesn't matter, but x does */
CHECK_REVERSE(FIRST_X_AXIS);
}
LOAD_RANGE(FIRST_Y_AXIS);
CHECK_REVERSE(FIRST_Y_AXIS);
LOAD_RANGE(SECOND_X_AXIS);
CHECK_REVERSE(SECOND_X_AXIS);
LOAD_RANGE(SECOND_Y_AXIS);
CHECK_REVERSE(SECOND_Y_AXIS);
/* use the default dummy variable unless changed */
if (dummy_token >= 0)
copy_str(c_dummy_var[0], dummy_token, MAX_ID_LEN);
else
(void) strcpy(c_dummy_var[0], dummy_var[0]);
eval_plots();
}
/* Use up to 7 columns in data file at once -- originally it was 5 */
#define NCOL 7
/* A quick note about boxes style. For boxwidth auto, we cannot
* calculate widths yet, since it may be sorted, etc. But if
* width is set, we must do it now, before logs of xmin/xmax
* are taken.
* We store -1 in point->z as a marker to mean width needs to be
* calculated, or 0 to mean that xmin/xmax are set correctly
*/
static void get_data(this_plot)
struct curve_points *this_plot;
/* this_plot->token is after datafile spec, for error reporting
* it will later be moved passed title/with/linetype/pointtype
*/
{
int i /* num. points ! */ , j;
int max_cols, min_cols; /* allowed range of column numbers */
double v[NCOL];
int storetoken = this_plot->token;
/* eval_plots has already opened file */
switch (this_plot->plot_style) { /* set maximum columns to scan */
case XYERRORBARS:
case BOXXYERROR:
max_cols = 7;
min_cols = 4;
break;
case FINANCEBARS:
case CANDLESTICKS:
min_cols = max_cols = 5;
break;
case BOXERROR:
max_cols = 5;
min_cols = 4;
break;
case VECTOR:
min_cols = max_cols = 4;
break;
case XERRORBARS:
case YERRORBARS:
max_cols = 4;
min_cols = 3;
break;
case BOXES:
min_cols = 2;
max_cols = 4;
default:
min_cols = 1;
max_cols = 2;
}
if (this_plot->plot_smooth == ACSPLINES)
max_cols = 3;
if (df_no_use_specs > max_cols)
int_error("Too many using specs for this style", NO_CARET);
if (df_no_use_specs >0 && df_no_use_specs < min_cols)
int_error("Not enough columns for this style", NO_CARET);
i = 0;
while ((j = df_readline(v, max_cols)) != DF_EOF) {
/* j <= max_cols */
if (i >= this_plot->p_max) {
/*
* overflow about to occur. Extend size of points[] array. We
* either double the size, or add 1000 points, whichever is a
* smaller increment. Note i = p_max.
*/
cp_extend(this_plot, i + (i < 1000 ? i : 1000));
}
/* Limitation: No xerrorbars with boxes */
switch (j) {
default:
{
char message[80];
sprintf(message, "internal error : df_readline returned %d : datafile line %d", j, df_line_number);
df_close();
int_error(message, c_token);
}
case DF_UNDEFINED:
/* bad result from extended using expression */
this_plot->points[i].type = UNDEFINED;
i++;
continue;
case DF_FIRST_BLANK:
/* break in data, make next point undefined */
this_plot->points[i].type = UNDEFINED;
i++;
continue;
case DF_SECOND_BLANK:
/* second blank line. We dont do anything
* (we did everything when we got FIRST one)
*/
continue;
case 0: /* not blank line, but df_readline couldn't parse it */
{
char message[80];
sprintf(message, "Bad data on line %d", df_line_number);
df_close();
int_error(message, this_plot->token);
}
case 1:
{ /* only one number */
/* x is index, assign number to y */
v[1] = v[0];
v[0] = df_datum;
/* nobreak */
}
case 2:
/* x, y */
/* ylow and yhigh are same as y */
if (this_plot->plot_style == BOXES && boxwidth > 0) {
/* calc width now */
store2d_point(this_plot, i++, v[0], v[1], v[0] - boxwidth / 2, v[0] + boxwidth / 2, v[1], v[1], 0.0);
} else {
/* xlow and xhigh are same as x */
store2d_point(this_plot, i++, v[0], v[1], v[0], v[0], v[1], v[1],
-1.0); /* auto width if boxes, else ignored */
}
break;
case 3:
/* x, y, ydelta OR x, y, xdelta OR x, y, width */
if (this_plot->plot_smooth == ACSPLINES)
store2d_point(this_plot, i++, v[0], v[1], v[0], v[0], v[1], v[1], v[2]);
else
switch (this_plot->plot_style) {
default:
int_warn("This plot style not work with 3 cols. Setting to yerrorbars", storetoken);
this_plot->plot_style = YERRORBARS;
/* fall through */
case YERRORBARS:
case BOXERROR: /* x, y, dy */
store2d_point(this_plot, i++, v[0], v[1], v[0], v[0], v[1] - v[2], v[1] + v[2],
-1.0); /* auto width if boxes, else ignored */
break;
case XERRORBARS:
store2d_point(this_plot, i++, v[0], v[1], v[0] - v[2], v[0] + v[2], v[1], v[1], 0.0);
break;
case BOXES:
/* calculate xmin and xmax here, so that logs are taken if
* if necessary
*/
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);
break;
} /*inner switch */
break;
case 4:
/* x, y, ylow, yhigh OR
* x, y, xlow, xhigh OR
* x, y, xdelta, ydelta OR
* x, y, ydelta, width
*/
switch (this_plot->plot_style) {
default:
int_warn("This plot style does not work with 4 cols. Setting to yerrorbars",
storetoken);
this_plot->plot_style = YERRORBARS;
/* fall through */
case YERRORBARS:
store2d_point(this_plot, i++, v[0], v[1], v[0], v[0], v[2], v[3], -1.0);
break;
case BOXXYERROR: /* x, y, dx, dy */
case XYERRORBARS:
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);
break;
case BOXES: /* x, y, xmin, xmax */
store2d_point(this_plot, i++, v[0], v[1], v[2], v[3], v[1], v[1], 0.0);
break;
case XERRORBARS:
store2d_point(this_plot, i++, v[0], v[1], v[2], v[3], v[1], v[1], 0.0);
break;
case BOXERROR:
/* x,y, xleft, xright */
store2d_point(this_plot, i++, v[0], v[1], v[0], v[0], v[1] - v[2], v[1] + v[2], 0.0);
break;
case VECTOR:
/* x,y,dx,dy */
store2d_point(this_plot, i++, v[0], v[1], v[0], v[0] + v[2], v[1], v[1] + v[3], -1.0);
break;
} /*inner switch */
break;
case 5:
{ /* x, y, ylow, yhigh, width or x open low high close */
switch (this_plot->plot_style) {
default:
int_warn("Five col. plot style must be boxerrorbars, financebars or candlesticks. Setting to boxerrorbars", storetoken);
this_plot->plot_style = BOXERROR;
/*fall through */
case BOXERROR: /* x, y, ylow, yhigh, width */
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);
break;
case FINANCEBARS:
case CANDLESTICKS:
store2d_point(this_plot, i++, v[0], v[1], v[0], v[0], v[2], v[3], v[4]);
break;
}
break;
}
case 7:
/* same as six columns. Width ignored */
/* eh ? - fall through */
case 6:
/* x, y, xlow, xhigh, ylow, yhigh */
switch (this_plot->plot_style) {
default:
int_warn("This plot style not work with 6 cols. Setting to xyerrorbars", storetoken);
this_plot->plot_style = XYERRORBARS;
/*fall through */
case XYERRORBARS:
case BOXXYERROR:
store2d_point(this_plot, i++, v[0], v[1], v[2], v[3], v[4], v[5], 0.0);
break;
}
} /*switch */
} /*while */
this_plot->p_count = i;
cp_extend(this_plot, i); /* shrink to fit */
df_close();
}
/* called by get_data for each point */
static void store2d_point(this_plot, i, x, y, xlow, xhigh, ylow, yhigh, width)
struct curve_points *this_plot;
int i; /* point number */
double x, y;
double ylow, yhigh;
double xlow, xhigh;
double width; /* -1 means autocalc, 0 means use xmin/xmax */
{
struct coordinate GPHUGE *cp = &(this_plot->points[i]);
int dummy_type = INRANGE; /* sometimes we dont care about outranging */
/* jev -- pass data values thru user-defined function */
/* div -- y is dummy variable 2 - copy value there */
if (ydata_func.at) {
struct value val;
(void) Gcomplex(&ydata_func.dummy_values[0], y, 0.0);
ydata_func.dummy_values[2] = ydata_func.dummy_values[0];
evaluate_at(ydata_func.at, &val);
y = undefined ? 0.0 : real(&val);
(void) Gcomplex(&ydata_func.dummy_values[0], ylow, 0.0);
ydata_func.dummy_values[2] = ydata_func.dummy_values[0];
evaluate_at(ydata_func.at, &val);
ylow = undefined ? 0 : real(&val);
(void) Gcomplex(&ydata_func.dummy_values[0], yhigh, 0.0);
ydata_func.dummy_values[2] = ydata_func.dummy_values[0];
evaluate_at(ydata_func.at, &val);
yhigh = undefined ? 0 : real(&val);
}
dummy_type = cp->type = INRANGE;
if (polar) {
double newx, newy;
if (!(autoscale_r & 2) && y > rmax) {
cp->type = OUTRANGE;
}
if (!(autoscale_r & 1)) {
/* we store internally as if plotting r(t)-rmin */
y -= rmin;
}
newx = y * cos(x * ang2rad);
newy = y * sin(x * ang2rad);
#if 0 /* HBB 981118: added polar errorbars */
/* only lines and points supported with polar */
y = ylow = yhigh = newy;
x = xlow = xhigh = newx;
#else
y = newy;
x = newx;
if (!(autoscale_r & 2) && yhigh > rmax) {
cp->type = OUTRANGE;
}
if (!(autoscale_r & 1)) {
/* we store internally as if plotting r(t)-rmin */
yhigh -= rmin;
}
newx = yhigh * cos(xhigh * ang2rad);
newy = yhigh * sin(xhigh * ang2rad);
yhigh = newy;
xhigh = newx;
if (!(autoscale_r & 2) && ylow > rmax) {
cp->type = OUTRANGE;
}
if (!(autoscale_r & 1)) {
/* we store internally as if plotting r(t)-rmin */
ylow -= rmin;
}
newx = ylow * cos(xlow * ang2rad);
newy = ylow * sin(xlow * ang2rad);
ylow = newy;
xlow = newx;
#endif
}
/* return immediately if x or y are undefined
* we dont care if outrange for high/low.
* BUT if high/low undefined (ie log( < 0 ), no number is stored,
* but graphics.c doesn't know.
* explicitly store -VERYLARGE;
*/
STORE_WITH_LOG_AND_FIXUP_RANGE(cp->x, x, cp->type, this_plot->x_axis, NOOP, return);
STORE_WITH_LOG_AND_FIXUP_RANGE(cp->xlow, xlow, dummy_type, this_plot->x_axis, NOOP, cp->xlow = -VERYLARGE);
STORE_WITH_LOG_AND_FIXUP_RANGE(cp->xhigh, xhigh, dummy_type, this_plot->x_axis, NOOP, cp->xhigh = -VERYLARGE);
STORE_WITH_LOG_AND_FIXUP_RANGE(cp->y, y, cp->type, this_plot->y_axis, NOOP, return);
STORE_WITH_LOG_AND_FIXUP_RANGE(cp->ylow, ylow, dummy_type, this_plot->y_axis, NOOP, cp->ylow = -VERYLARGE);
STORE_WITH_LOG_AND_FIXUP_RANGE(cp->yhigh, yhigh, dummy_type, this_plot->y_axis, NOOP, cp->yhigh = -VERYLARGE);
cp->z = width;
} /* store2d_point */
/*
* print_points: a debugging routine to print out the points of a curve, and
* the curve structure. If curve<0, then we print the list of curves.
*/
#if 0 /* not used */
static char *plot_type_names[4] =
{
"Function", "Data", "3D Function", "3d data"
};
static char *plot_style_names[14] =
{
"Lines", "Points", "Impulses", "LinesPoints", "Dots", "XErrorbars",
"YErrorbars", "XYErrorbars", "BoxXYError", "Boxes", "Boxerror", "Steps",
"FSteps", "Vector"
};
static char *plot_smooth_names[5] =
{
"None", "Unique", "CSplines", "ACSplines", "Bezier", "SBezier"
};
static void print_points(curve)
int curve; /* which curve to print */
{
register struct curve_points *this_plot;
int i;
if (curve < 0) {
for (this_plot = first_plot, i = 0;
this_plot != NULL;
i++, this_plot = this_plot->next_cp) {
printf("Curve %d:\n", i);
if ((int) this_plot->plot_type >= 0 && (int) (this_plot->plot_type) < 4)
printf("Plot type %d: %s\n", (int) (this_plot->plot_type),
plot_type_names[(int) (this_plot->plot_type)]);
else
printf("Plot type %d: BAD\n", (int) (this_plot->plot_type));
if ((int) this_plot->plot_style >= 0 && (int) (this_plot->plot_style) < 14)
printf("Plot style %d: %s\n", (int) (this_plot->plot_style),
plot_style_names[(int) (this_plot->plot_style)]);
else
printf("Plot style %d: BAD\n", (int) (this_plot->plot_style));
if ((int) this_plot->plot_smooth >= 0 && (int) (this_plot->plot_smooth) < 6)
printf("Plot smooth style %d: %s\n", (int) (this_plot->plot_style),
plot_smooth_names[(int) (this_plot->plot_smooth)]);
else
printf("Plot smooth style %d: BAD\n", (int) (this_plot->plot_smooth));
printf("Plot title: '%s'\n", this_plot->title);
printf("Line type %d\n", this_plot->line_type);
printf("Point type %d\n", this_plot->point_type);
printf("max points %d\n", this_plot->p_max);
printf("current points %d\n", this_plot->p_count);
printf("\n");
}
} else {
for (this_plot = first_plot, i = 0;
i < curve && this_plot != NULL;
i++, this_plot = this_plot->next_cp);
if (this_plot == NULL)
printf("Curve %d does not exist; list has %d curves\n", curve, i);
else {
printf("Curve %d, %d points\n", curve, this_plot->p_count);
for (i = 0; i < this_plot->p_count; i++) {
printf("%c x=%g y=%g z=%g xlow=%g xhigh=%g ylow=%g yhigh=%g\n",
this_plot->points[i].type == INRANGE ? 'i'
: this_plot->points[i].type == OUTRANGE ? 'o'
: 'u',
this_plot->points[i].x,
this_plot->points[i].y,
this_plot->points[i].z,
this_plot->points[i].xlow,
this_plot->points[i].xhigh,
this_plot->points[i].ylow,
this_plot->points[i].yhigh);
}
printf("\n");
}
}
}
#endif /* not used */
static void print_table(this_plot, plot_num)
struct curve_points *this_plot;
int plot_num;
{
int i, curve;
for (curve = 0; curve < plot_num;
curve++, this_plot = this_plot->next_cp) {
fprintf(gpoutfile, "#Curve %d, %d points\n#x y type\n", curve, this_plot->p_count);
for (i = 0; i < this_plot->p_count; i++) {
fprintf(gpoutfile, "%g %g %c\n",
this_plot->points[i].x,
this_plot->points[i].y,
this_plot->points[i].type == INRANGE ? 'i'
: this_plot->points[i].type == OUTRANGE ? 'o'
: 'u');
}
fputc('\n', gpoutfile);
}
/* two blank lines between plots in table output */
fputc('\n', gpoutfile);
fflush(gpoutfile);
}
/*
* This parses the plot command after any range specifications. To support
* autoscaling on the x axis, we want any data files to define the x range,
* then to plot any functions using that range. We thus parse the input
* twice, once to pick up the data files, and again to pick up the functions.
* Definitions are processed twice, but that won't hurt.
* div - okay, it doesn't hurt, but every time an option as added for
* datafiles, code to parse it has to be added here. Change so that
* we store starting-token in the plot structure.
*/
static void eval_plots()
{
register int i;
register struct curve_points *this_plot, **tp_ptr;
int some_functions = 0;
int plot_num, line_num, point_num, xparam = 0;
char *xtitle;
int begin_token = c_token; /* so we can rewind for second pass */
int uses_axis[AXIS_ARRAY_SIZE];
uses_axis[FIRST_X_AXIS] =
uses_axis[FIRST_Y_AXIS] =
uses_axis[SECOND_X_AXIS] =
uses_axis[SECOND_Y_AXIS] = 0;
/* Reset first_plot. This is usually done at the end of this function.
If there is an error within this function, the memory is left allocated,
since we cannot call cp_free if the list is incomplete. Making sure that
the list structure is always vaild requires some rewriting */
first_plot = NULL;
tp_ptr = &(first_plot);
plot_num = 0;
line_num = 0; /* default line type */
point_num = 0; /* default point type */
xtitle = NULL;
/*** First Pass: Read through data files ***
* This pass serves to set the xrange and to parse the command, as well
* as filling in every thing except the function data. That is done after
* the xrange is defined.
*/
while (TRUE) {
if (END_OF_COMMAND)
int_error("function to plot expected", c_token);
if (is_definition(c_token)) {
define();
} else {
int x_axis = 0, y_axis = 0;
int specs;
/* for datafile plot, record datafile spec for title */
int start_token = c_token, end_token;
plot_num++;
if (isstring(c_token)) { /* data file to plot */
if (parametric && xparam)
int_error("previous parametric function not fully specified", c_token);
if (*tp_ptr)
this_plot = *tp_ptr;
else { /* no memory malloc()'d there yet */
this_plot = cp_alloc(MIN_CRV_POINTS);
*tp_ptr = this_plot;
}
this_plot->plot_type = DATA;
this_plot->plot_style = data_style;
this_plot->plot_smooth = NONE;
specs = df_open(NCOL); /* up to NCOL cols */
/* this parses data-file-specific modifiers only */
/* we'll sort points when we know style, if necessary */
if (df_binary)
int_error("2d binary files not yet supported", c_token);
this_plot->token = end_token = c_token - 1; /* include modifiers in default title */
} else {
/* function to plot */
some_functions = 1;
if (parametric) /* working on x parametric function */
xparam = 1 - xparam;
if (*tp_ptr) {
this_plot = *tp_ptr;
cp_extend(this_plot, samples + 1);
} else { /* no memory malloc()'d there yet */
this_plot = cp_alloc(samples + 1);
*tp_ptr = this_plot;
}
this_plot->plot_type = FUNC;
this_plot->plot_style = func_style;
dummy_func = &plot_func;
plot_func.at = temp_at();
dummy_func = NULL;
/* ignore it for now */
end_token = c_token - 1;
} /* end of IS THIS A FILE OR A FUNC block */
/* deal with smooth */
if (almost_equals(c_token, "s$mooth")) {
if (END_OF_COMMAND)
int_error("expecting smooth parameter", c_token);
else {
c_token++;
if (almost_equals(c_token, "u$nique"))
this_plot->plot_smooth = UNIQUE;
else if (almost_equals(c_token, "a$csplines"))
this_plot->plot_smooth = ACSPLINES;
else if (almost_equals(c_token, "c$splines"))
this_plot->plot_smooth = CSPLINES;
else if (almost_equals(c_token, "b$ezier"))
this_plot->plot_smooth = BEZIER;
else if (almost_equals(c_token, "s$bezier"))
this_plot->plot_smooth = SBEZIER;
else
int_error("expecting 'unique', 'acsplines', 'csplines', 'bezier' or 'sbezier'", c_token);
}
this_plot->plot_style = LINES;
c_token++; /* skip format */
}
/* look for axes/axis */
if (almost_equals(c_token, "ax$es") || almost_equals(c_token, "ax$is")) {
if (parametric && xparam)
int_error("previous parametric function not fully specified", c_token);
if (equals(++c_token, "x1y1")) {
x_axis = FIRST_X_AXIS;
y_axis = FIRST_Y_AXIS;
++c_token;
} else if (equals(c_token, "x2y2")) {
x_axis = SECOND_X_AXIS;
y_axis = SECOND_Y_AXIS;
++c_token;
} else if (equals(c_token, "x1y2")) {
x_axis = FIRST_X_AXIS;
y_axis = SECOND_Y_AXIS;
++c_token;
} else if (equals(c_token, "x2y1")) {
x_axis = SECOND_X_AXIS;
y_axis = FIRST_Y_AXIS;
++c_token;
} else
int_error("axes must be x1y1, x1y2, x2y1 or x2y2", c_token);
} else {
x_axis = FIRST_X_AXIS;
y_axis = FIRST_Y_AXIS;
}
this_plot->x_axis = x_axis;
this_plot->y_axis = y_axis;
/* we can now do some checks that we deferred earlier */
if (this_plot->plot_type == DATA) {
if (!(uses_axis[x_axis] & 1) && autoscale_lx) {
if (auto_array[x_axis] & 1)
min_array[x_axis] = VERYLARGE;
if (auto_array[x_axis] & 2)
max_array[x_axis] = -VERYLARGE;
}
if (datatype[x_axis] == TIME) {
if (specs < 2)
int_error("Need full using spec for x time data", c_token);
df_timecol[0] = 1;
}
if (datatype[y_axis] == TIME) {
if (specs < 1)
int_error("Need using spec for y time data", c_token);
df_timecol[y_axis] = 1; /* need other cols, but I'm lazy */
}
uses_axis[x_axis] |= 1; /* separate record of datafile and func */
uses_axis[y_axis] |= 1;
} else if (!parametric || !xparam) {
/* for x part of a parametric function, axes are possibly wrong */
uses_axis[x_axis] |= 2; /* separate record of data and func */
uses_axis[y_axis] |= 2;
}
if (almost_equals(c_token, "t$itle")) {
if (parametric) {
if (xparam)
int_error("\"title\" allowed only after parametric function fully specified", c_token);
else if (xtitle != NULL)
xtitle[0] = '\0'; /* Remove default title . */
}
c_token++;
if (isstring(c_token)) {
m_quote_capture(&(this_plot->title), c_token, c_token);
} else {
int_error("expecting \"title\" for plot", c_token);
}
c_token++;
} else if (almost_equals(c_token, "not$itle")) {
if (xtitle != NULL)
xtitle[0] = '\0';
c_token++;
} else {
m_capture(&(this_plot->title), start_token, end_token);
if (xparam)
xtitle = this_plot->title;
}
if (almost_equals(c_token, "w$ith")) {
if (parametric && xparam)
int_error("\"with\" allowed only after parametric function fully specified", c_token);
this_plot->plot_style = get_style();
}
/* pick up line/point specs
* - point spec allowed if style uses points, ie style&2 != 0
* - keywords for lt and pt are optional
*/
LP_PARSE(this_plot->lp_properties, 1, this_plot->plot_style & 2,
line_num, point_num);
/* allow old-style syntax too - ignore case lt 3 4 for example */
if (!equals(c_token, ",") && !END_OF_COMMAND) {
struct value t;
this_plot->lp_properties.l_type =
this_plot->lp_properties.p_type = (int) real(const_express(&t)) - 1;
if (!equals(c_token, ",") && !END_OF_COMMAND)
this_plot->lp_properties.p_type = (int) real(const_express(&t)) - 1;
}
if (!xparam) {
if (this_plot->plot_style & 2) /* style includes points */
++point_num;
++line_num;
}
if (this_plot->plot_type == DATA) {
/* actually get the data now */
get_data(this_plot);
/* sort */
switch (this_plot->plot_smooth) { /* sort and average, if */
case UNIQUE: /* the style requires */
case CSPLINES:
case ACSPLINES:
case SBEZIER:
sort_points(this_plot);
cp_implode(this_plot);
break;
default:
; /* keep gcc -Wall happy */
}
switch (this_plot->plot_smooth) { /* create new data set */
case SBEZIER: /* by evaluation of */
case BEZIER: /* interpolation routines */
case ACSPLINES:
case CSPLINES:
gen_interp(this_plot);
break;
default:
; /* keep gcc -Wall happy */
}
/* now that we know the plot style, adjust the x- and yrange */
/* adjust_range(this_plot); no longer needed */
}
this_plot->token = c_token; /* save end of plot for second pass */
tp_ptr = &(this_plot->next_cp);
} /* !is_defn */
if (equals(c_token, ","))
c_token++;
else
break;
}
if (parametric && xparam)
int_error("parametric function not fully specified", NO_CARET);
/*** Second Pass: Evaluate the functions ***/
/*
* Everything is defined now, except the function data. We expect no
* syntax errors, etc, since the above parsed it all. This makes the code
* below simpler. If autoscale_ly, the yrange may still change.
* we stored last token of each plot, so we dont need to do everything again
*/
/* give error if xrange badly set from missing datafile error
* parametric or polar fns can still affect x ranges
*/
if (!parametric && !polar) {
if (min_array[FIRST_X_AXIS] == VERYLARGE ||
max_array[FIRST_X_AXIS] == -VERYLARGE)
int_error("x range is invalid", c_token);
/* check that xmin -> xmax is not too small */
fixup_range(FIRST_X_AXIS, "x");
if (uses_axis[SECOND_X_AXIS] & 1) {
/* some data plots with x2 */
if (min_array[SECOND_X_AXIS] == VERYLARGE ||
max_array[SECOND_X_AXIS] == -VERYLARGE)
int_error("x2 range is invalid", c_token);
/* check that x2min -> x2max is not too small */
fixup_range(SECOND_X_AXIS, "x2");
} else if (auto_array[SECOND_X_AXIS]) {
/* copy x1's range */
if (auto_array[SECOND_X_AXIS] & 1)
min_array[SECOND_X_AXIS] = min_array[FIRST_X_AXIS];
if (auto_array[SECOND_X_AXIS] & 2)
max_array[SECOND_X_AXIS] = max_array[FIRST_X_AXIS];
}
}
if (some_functions) {
/* call the controlled variable t, since x_min can also mean smallest x */
double t_min, t_max, t_step;
if (parametric || polar) {
if (!(uses_axis[FIRST_X_AXIS] & 1)) {
/* these have not yet been set to full width */
if (auto_array[FIRST_X_AXIS] & 1)
min_array[FIRST_X_AXIS] = VERYLARGE;
if (auto_array[FIRST_X_AXIS] & 2)
max_array[FIRST_X_AXIS] = -VERYLARGE;
}
if (!(uses_axis[SECOND_X_AXIS] & 1)) {
if (auto_array[SECOND_X_AXIS] & 1)
min_array[SECOND_X_AXIS] = VERYLARGE;
if (auto_array[SECOND_X_AXIS] & 2)
max_array[SECOND_X_AXIS] = -VERYLARGE;
}
}
#define SET_DUMMY_RANGE(AXIS) \
do{ assert(!polar && !parametric); \
if (log_array[AXIS]) {\
if (min_array[AXIS] <= 0.0 || max_array[AXIS] <= 0.0)\
int_error("x/x2 range must be greater than 0 for log scale!", NO_CARET);\
t_min = log(min_array[AXIS])/log_base_array[AXIS]; t_max = log(max_array[AXIS])/log_base_array[AXIS];\
} else {\
t_min = min_array[AXIS]; t_max = max_array[AXIS];\
}\
t_step = (t_max - t_min) / (samples - 1); \
}while(0)
if (parametric || polar) {
t_min = min_array[T_AXIS];
t_max = max_array[T_AXIS];
t_step = (t_max - t_min) / (samples - 1);
}
/* else we'll do it on each plot */
tp_ptr = &(first_plot);
plot_num = 0;
this_plot = first_plot;
c_token = begin_token; /* start over */
/* Read through functions */
while (TRUE) {
if (is_definition(c_token)) {
define();
} else {
int x_axis = this_plot->x_axis;
int y_axis = this_plot->y_axis;
plot_num++;
if (!isstring(c_token)) { /* function to plot */
if (parametric) { /* toggle parametric axes */
xparam = 1 - xparam;
}
dummy_func = &plot_func;
plot_func.at = temp_at(); /* reparse function */
if (!parametric && !polar) {
SET_DUMMY_RANGE(x_axis);
}
for (i = 0; i < samples; i++) {
double temp;
struct value a;
double t = t_min + i * t_step;
/* parametric/polar => NOT a log quantity */
double x = (!parametric && !polar &&
log_array[x_axis]) ? pow(base_array[x_axis], t) : t;
(void) Gcomplex(&plot_func.dummy_values[0], x, 0.0);
evaluate_at(plot_func.at, &a);
if (undefined || (fabs(imag(&a)) > zero)) {
this_plot->points[i].type = UNDEFINED;
continue;
}
temp = real(&a);
this_plot->points[i].z = -1.0; /* width of box not specified */
this_plot->points[i].type = INRANGE; /* for the moment */
if (parametric) {
/* we cannot do range-checking now, since for
* the x function we did not know which axes
* we were using
* DO NOT TAKE LOGS YET - do it in parametric_fixup
*/
this_plot->points[i].x = t; /* ignored, actually... */
this_plot->points[i].y = temp;
} else if (polar) {
double y;
if (!(autoscale_r & 2) && temp > rmax)
this_plot->points[i].type = OUTRANGE;
if (!(autoscale_r & 1))
temp -= rmin;
y = temp * sin(x * ang2rad);
x = temp * cos(x * ang2rad);
temp = y;
STORE_WITH_LOG_AND_FIXUP_RANGE(this_plot->points[i].x, x, this_plot->points[i].type,
x_axis, NOOP, goto come_here_if_undefined);
STORE_WITH_LOG_AND_FIXUP_RANGE(this_plot->points[i].y, y, this_plot->points[i].type,
y_axis, NOOP, goto come_here_if_undefined);
} else { /* neither parametric or polar */
/* If non-para, it must be INRANGE */
this_plot->points[i].x = t; /* logscale ? log(x) : x */
STORE_WITH_LOG_AND_FIXUP_RANGE(this_plot->points[i].y, temp, this_plot->points[i].type,
y_axis + (x_axis - y_axis) * xparam, NOOP, goto come_here_if_undefined);
come_here_if_undefined: /* could not use a continue in this case */
; /* ansi requires a statement after a label */
}
} /* loop over samples */
this_plot->p_count = i; /* samples */
}
c_token = this_plot->token; /* skip all modifers func / whole of data plots */
tp_ptr = &(this_plot->next_cp); /* used below */
this_plot = this_plot->next_cp;
}
if (equals(c_token, ","))
c_token++;
else
break;
}
if (parametric) {
/* Now actually fix the plot pairs to be single plots */
/* also fixes up polar&¶metric fn plots */
parametric_fixup(first_plot, &plot_num);
/* we omitted earlier check for range too small */
fixup_range(FIRST_X_AXIS, "x");
if (uses_axis[SECOND_X_AXIS]) {
fixup_range(SECOND_X_AXIS, "x2");
}
}
} /* some_functions */
/* throw out all curve_points at end of list, that we don't need */
cp_free(*tp_ptr);
*tp_ptr = NULL;
/* if first_plot is NULL, we have no functions or data at all. This can
happen, if you type "plot x=5", since x=5 is a variable assignment */
if (plot_num == 0 || first_plot == NULL) {
int_error("no functions or data to plot", c_token);
}
if (uses_axis[FIRST_X_AXIS]) {
if (max_array[FIRST_X_AXIS] == -VERYLARGE ||
min_array[FIRST_X_AXIS] == VERYLARGE)
int_error("all points undefined!", NO_CARET);
FIXUP_RANGE_FOR_LOG(FIRST_X_AXIS, x);
}
if (uses_axis[SECOND_X_AXIS]) {
if (max_array[SECOND_X_AXIS] == -VERYLARGE ||
min_array[SECOND_X_AXIS] == VERYLARGE)
int_error("all points undefined!", NO_CARET);
FIXUP_RANGE_FOR_LOG(SECOND_X_AXIS, x2);
} else {
assert(uses_axis[FIRST_X_AXIS]);
if (auto_array[SECOND_X_AXIS] & 1)
min_array[SECOND_X_AXIS] = min_array[FIRST_X_AXIS];
if (auto_array[SECOND_X_AXIS] & 2)
max_array[SECOND_X_AXIS] = max_array[FIRST_X_AXIS];
if (! auto_array[SECOND_X_AXIS])
FIXUP_RANGE_FOR_LOG(SECOND_X_AXIS, x2);
}
if (!uses_axis[FIRST_X_AXIS]) {
assert(uses_axis[SECOND_X_AXIS]);
if (auto_array[FIRST_X_AXIS] & 1)
min_array[FIRST_X_AXIS] = min_array[SECOND_X_AXIS];
if (auto_array[FIRST_X_AXIS] & 2)
max_array[FIRST_X_AXIS] = max_array[SECOND_X_AXIS];
}
if (uses_axis[FIRST_Y_AXIS]) {
if (max_array[FIRST_Y_AXIS] == -VERYLARGE ||
min_array[FIRST_Y_AXIS] == VERYLARGE)
int_error("all points undefined!", NO_CARET);
fixup_range(FIRST_Y_AXIS, "y");
FIXUP_RANGE_FOR_LOG(FIRST_Y_AXIS, y);
}
if (uses_axis[SECOND_Y_AXIS]) {
if (max_array[SECOND_Y_AXIS] == -VERYLARGE ||
min_array[SECOND_Y_AXIS] == VERYLARGE)
int_error("all points undefined!", NO_CARET);
fixup_range(SECOND_Y_AXIS, "y2");
FIXUP_RANGE_FOR_LOG(SECOND_Y_AXIS, y2);
} else {
/* else we want to copy y2 range */
assert(uses_axis[FIRST_Y_AXIS]);
if (auto_array[SECOND_Y_AXIS] & 1)
min_array[SECOND_Y_AXIS] = min_array[FIRST_Y_AXIS];
if (auto_array[SECOND_Y_AXIS] & 2)
max_array[SECOND_Y_AXIS] = max_array[FIRST_Y_AXIS];
/* Log() fixup is only necessary if the range was *not* copied from
* the (already logarithmized) yrange */
if (! auto_array[SECOND_Y_AXIS])
FIXUP_RANGE_FOR_LOG(SECOND_Y_AXIS, y2);
}
if (!uses_axis[FIRST_Y_AXIS]) {
assert(uses_axis[SECOND_Y_AXIS]);
if (auto_array[FIRST_Y_AXIS] & 1)
min_array[FIRST_Y_AXIS] = min_array[SECOND_Y_AXIS];
if (auto_array[FIRST_Y_AXIS] & 2)
max_array[FIRST_Y_AXIS] = max_array[SECOND_Y_AXIS];
}
#define WRITEBACK(axis,min,max) \
if(range_flags[axis]&RANGE_WRITEBACK) \
{if (auto_array[axis]&1) min = min_array[axis]; \
if (auto_array[axis]&2) max = max_array[axis]; \
}
WRITEBACK(FIRST_X_AXIS, xmin, xmax)
WRITEBACK(FIRST_Y_AXIS, ymin, ymax)
WRITEBACK(SECOND_X_AXIS, x2min, x2max)
WRITEBACK(SECOND_Y_AXIS, y2min, y2max)
if (strcmp(term->name, "table") == 0)
print_table(first_plot, plot_num);
else {
START_LEAK_CHECK(); /* check for memory leaks in this routine */
/* do_plot now uses max_array[], etc */
do_plot(first_plot, plot_num);
END_LEAK_CHECK();
}
/* if we get here, all went well, so record this line for replot */
if (plot_token != -1) {
/* note that m_capture also frees the old replot_line */
m_capture(&replot_line, plot_token, c_token - 1);
plot_token = -1;
}
cp_free(first_plot);
first_plot = NULL;
} /* eval_plots */
static void parametric_fixup(start_plot, plot_num)
struct curve_points *start_plot;
int *plot_num;
/*
* The hardest part of this routine is collapsing the FUNC plot types in the
* list (which are garanteed to occur in (x,y) pairs while preserving the
* non-FUNC type plots intact. This means we have to work our way through
* various lists. Examples (hand checked): start_plot:F1->F2->NULL ==>
* F2->NULL start_plot:F1->F2->F3->F4->F5->F6->NULL ==> F2->F4->F6->NULL
* start_plot:F1->F2->D1->D2->F3->F4->D3->NULL ==> F2->D1->D2->F4->D3->NULL
*
*/
{
struct curve_points *xp, *new_list = NULL, *free_list = NULL;
struct curve_points **last_pointer = &new_list;
int i, tlen, curve;
char *new_title;
/*
* Ok, go through all the plots and move FUNC types together. Note: this
* originally was written to look for a NULL next pointer, but gnuplot
* wants to be sticky in grabbing memory and the right number of items in
* the plot list is controlled by the plot_num variable.
*
* Since gnuplot wants to do this sticky business, a free_list of
* curve_points is kept and then tagged onto the end of the plot list as
* this seems more in the spirit of the original memory behavior than
* simply freeing the memory. I'm personally not convinced this sort of
* concern is worth it since the time spent computing points seems to
* dominate any garbage collecting that might be saved here...
*/
new_list = xp = start_plot;
curve = 0;
while (++curve <= *plot_num) {
if (xp->plot_type == FUNC) {
/* Here's a FUNC parametric function defined as two parts. */
struct curve_points *yp = xp->next_cp;
--(*plot_num);
assert(xp->p_count == yp->p_count);
/* because syntax is plot x(t), y(t) axes ..., only
* the y function axes are correct
*/
/*
* Go through all the points assigning the y's from xp to be the x's
* for yp. In polar mode, we need to check max's and min's as we go.
*/
for (i = 0; i < yp->p_count; ++i) {
if (polar) {
double r = yp->points[i].y;
double t = xp->points[i].y * ang2rad;
double x, y;
if (!(autoscale_r & 2) && r > rmax)
yp->points[i].type = OUTRANGE;
if (!(autoscale_r & 1))
r -= rmin; /* store internally as if plotting r(t)-rmin */
x = r * cos(t);
y = r * sin(t);
/* we hadn't done logs when we stored earlier */
STORE_WITH_LOG_AND_FIXUP_RANGE(yp->points[i].x, x, yp->points[i].type,
xp->x_axis, NOOP, NOOP);
STORE_WITH_LOG_AND_FIXUP_RANGE(yp->points[i].y, y, yp->points[i].type,
xp->y_axis, NOOP, NOOP);
} else {
double x = xp->points[i].y;
double y = yp->points[i].y;
STORE_WITH_LOG_AND_FIXUP_RANGE(yp->points[i].x, x,
yp->points[i].type, yp->x_axis, NOOP, NOOP);
STORE_WITH_LOG_AND_FIXUP_RANGE(yp->points[i].y, y,
yp->points[i].type, yp->y_axis, NOOP, NOOP);
}
}
/* Ok, fix up the title to include both the xp and yp plots. */
if (xp->title && xp->title[0] != '\0' && yp->title) {
tlen = strlen(yp->title) + strlen(xp->title) + 3;
new_title = gp_alloc((unsigned long) tlen, "string");
strcpy(new_title, xp->title);
strcat(new_title, ", "); /* + 2 */
strcat(new_title, yp->title); /* + 1 = + 3 */
free(yp->title);
yp->title = new_title;
}
/* move xp to head of free list */
xp->next_cp = free_list;
free_list = xp;
/* append yp to new_list */
*last_pointer = yp;
last_pointer = &(yp->next_cp);
xp = yp->next_cp;
} else { /* data plot */
assert(*last_pointer == xp);
last_pointer = &(xp->next_cp);
xp = xp->next_cp;
}
} /* loop over plots */
first_plot = new_list;
/* Ok, stick the free list at the end of the curve_points plot list. */
*last_pointer = free_list;
}