[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/gnuplot/gnuplot/branch-5-5-end/src/plot2d.c [Back]  [Original]

/* GNUPLOT - plot2d.c */

/*[
 * Copyright 1986 - 1993, 1998, 2004   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 "gp_types.h"
#include "plot2d.h"

#include "alloc.h"
#include "axis.h"
#include "command.h"
#include "datafile.h"
#include "datablock.h"
#include "encoding.h"
#include "eval.h"
#include "filters.h"
#include "fit.h"
#include "graphics.h"
#include "interpol.h"
#include "misc.h"
#include "parse.h"
#include "pm3d.h"	/* for is_plot_with_palette */
#include "setshow.h"
#include "tables.h"
#include "tabulate.h"
#include "term_api.h"
#include "util.h"
#include "variable.h" /* For locale handling */
#include "watch.h"

/* minimum size of points[] in curve_points */
#define MIN_CRV_POINTS 100

/* static prototypes */

static struct curve_points * cp_alloc(int num);
static int get_data(struct curve_points *);
static void store2d_point(struct curve_points *, int i, double x, double y, double xlow, double xhigh, double ylow, double yhigh, double width);
static void eval_plots(void);
static void parametric_fixup(struct curve_points * start_plot, int *plot_num);
static void box_range_fiddling(struct curve_points *plot);
static void boxplot_range_fiddling(struct curve_points *plot);
static void spiderplot_range_fiddling(struct curve_points *plot);
static void histogram_range_fiddling(struct curve_points *plot);
static void impulse_range_fiddling(struct curve_points *plot);
static void parallel_range_fiddling(struct curve_points *plot);
static int check_or_add_boxplot_factor(struct curve_points *plot, char* string, double x);
static void add_tics_boxplot_factors(struct curve_points *plot);
static void parse_kdensity_options(struct curve_points *this_plot);
static void parse_hull_options(struct curve_points *this_plot);

#ifdef USE_POLAR_GRID
    static void grid_polar_data(struct curve_points *this_plot);
    static double polar_dist( double t_data, double r_data, double t_grid, double r_grid );
    static void store_polar_point(struct curve_points *, int i, double v[MAXDATACOLS]);

    struct pgrid default_polar_grid = {
	.mode = DGRID3D_QNORM,
	.theta_segments = 24,
	.r_segments = 10,
	.norm_q = 1,
	.kdensity = FALSE,
	.scale = 1.0,
	.rmin = 0.0,
	.rmax = VERYLARGE
    };
    struct pgrid polar_grid;
#endif /* USE_POLAR_GRID */

/* internal and external variables */

/* the curves/surfaces of the plot */
struct curve_points *first_plot = NULL;

static double histogram_rightmost = 0.0;    /* Highest x-coord of histogram so far */
static text_label histogram_title;          /* Subtitle for this histogram */
static int stack_count = 0;                 /* counter for stackheight */
static struct coordinate *stackheight = NULL; /* Scratch space for y autoscale */

static int paxis_start, paxis_end, paxis_current;	/* PARALLELPLOT bookkeeping */

/* If the user tries to plot a complex-valued function without reducing it to
 * some derived value like abs(f(z)), it generates a non-obvious error message
 * "all points y value undefined". Try to detect this case by counting complex
 * values as they are encountered so that a better error message is possible.
 */
static int n_complex_values = 0;

/* function implementations */

/*
 * cp_alloc() allocates a curve_points structure that can hold 'num'
 * points.   Initialize all fields to NULL.
 */
static struct curve_points *
cp_alloc(int num)
{
    struct curve_points *cp;
    struct lp_style_type default_lp_properties = DEFAULT_LP_STYLE_TYPE;

    cp = (struct curve_points *) gp_alloc(sizeof(struct curve_points), "curve");
    memset(cp,0,sizeof(struct curve_points));

    cp->p_max = (num >= 0 ? num : 0);
    if (num > 0)
	cp->points = (struct coordinate *)
	    gp_alloc(num * sizeof(struct coordinate), "curve points");

    /* Initialize various fields */
    cp->lp_properties = default_lp_properties;
    default_arrow_style(&(cp->arrow_properties));
    cp->fill_properties = default_fillstyle;
    cp->filledcurves_options = filledcurves_opts_data;

    return (cp);
}


/*
 * cp_extend() reallocates a curve_points structure to hold "num"
 * points. This will either expand or shrink the storage.
 */
void
cp_extend(struct curve_points *cp, int num)
{
    if (num == cp->p_max)
	return;

    if (num > 0) {
	cp->points = gp_realloc(cp->points, num * sizeof(cp->points[0]),
				"expanding 2D points");
	if (cp->varcolor)
	    cp->varcolor = gp_realloc(cp->varcolor, num * sizeof(double),
				    "expanding curve variable colors");
	cp->p_max = num;
	cp->p_max -= 1;		/* Set trigger point for reallocation ahead of	*/
				/* true end in case two slots are used at once	*/
				/* (e.g. redundant final point of closed curve)	*/
    } else {
	/* FIXME: Does this ever happen?  Should it call cp_free() instead? */
	free(cp->points);
	cp->points = NULL;
	cp->p_max = 0;
	free(cp->varcolor);
	cp->varcolor = NULL;
	if (cp->labels)
	    free_labels(cp->labels);
	cp->labels = NULL;
    }
}

/*
 * cp_free() releases any memory which was previously malloc()'d to hold
 *   curve points (and recursively down the linked list).
 */
void
cp_free(struct curve_points *cp)
{
    while (cp) {
	struct curve_points *next = cp->next;

	free(cp->title);
	cp->title = NULL;
	free(cp->title_position);
	cp->title_position = NULL;
	free(cp->points);
	cp->points = NULL;
	free(cp->varcolor);
	cp->varcolor = NULL;
	if (cp->labels)
	    free_labels(cp->labels);
	cp->labels = NULL;
	free_at(cp->plot_function.at);
	free_watchlist(cp->watchlist);
	cp->watchlist = NULL;

	free(cp);
	cp = next;
    }
}

/*
 * 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)
 */
void
plotrequest()
{
    int dummy_token = 0;
    AXIS_INDEX axis;

    if (!term)                  /* unknown */
	int_error(c_token, "use 'set term' to set terminal type first");

    is_3d_plot = FALSE;

    if (parametric && strcmp(set_dummy_var[0], "u") == 0)
	strcpy(set_dummy_var[0], "t");

    /* initialize the arrays from the 'set' scalars */
    axis_init(&axis_array[FIRST_X_AXIS], FALSE);
    axis_init(&axis_array[FIRST_Y_AXIS], TRUE);
    axis_init(&axis_array[SECOND_X_AXIS], FALSE);
    axis_init(&axis_array[SECOND_Y_AXIS], TRUE);
    axis_init(&axis_array[T_AXIS], FALSE);
    axis_init(&axis_array[U_AXIS], FALSE);
    axis_init(&axis_array[V_AXIS], FALSE);
    axis_init(&axis_array[POLAR_AXIS], TRUE);
    axis_init(&axis_array[COLOR_AXIS], TRUE);

    /* Always be prepared to restore the autoscaled values on "refresh"
     * Dima Kogan April 2018
     */
    for (axis = 0; axis < NUMBER_OF_MAIN_VISIBLE_AXES; axis++) {
	AXIS *this_axis = &axis_array[axis];
	if (this_axis->set_autoscale != AUTOSCALE_NONE)
	    this_axis->range_flags |= RANGE_WRITEBACK;
    }

    /* Nonlinear mapping of x or y via linkage to a hidden primary axis. */
    /* The user set autoscale for the visible axis; apply it also to the hidden axis. */
    for (axis = 0; axis < NUMBER_OF_MAIN_VISIBLE_AXES; axis++) {
	AXIS *secondary = &axis_array[axis];
	if (axis == SAMPLE_AXIS)
	    continue;
	if (secondary->linked_to_primary
	&&  secondary->linked_to_primary->index == -secondary->index) {
	    AXIS *primary = secondary->linked_to_primary;
	    primary->set_autoscale = secondary->set_autoscale;
	    axis_init(primary, 1);
	}
    }

    /* If we are called from a mouse zoom operation we should ignore	*/
    /* any range limits because otherwise the zoom won't zoom.		*/
    if (inside_zoom) {
	while (equals(c_token,"["))
	    parse_skip_range();
    }

    /* Range limits for the entire plot are optional but must be given	*/
    /* in a fixed order. The keyword 'sample' terminates range parsing.	*/
    if (parametric || polar) {
	dummy_token = parse_range(T_AXIS);
	parse_range(FIRST_X_AXIS);
    } else {
	dummy_token = parse_range(FIRST_X_AXIS);
    }
    parse_range(FIRST_Y_AXIS);
    parse_range(SECOND_X_AXIS);
    parse_range(SECOND_Y_AXIS);
    if (equals(c_token,"sample") && equals(c_token+1,"["))
	c_token++;

    /* Clear out any tick labels read from data files in previous plot */
    for (axis=0; axisdef.user)
	    ticdef->def.user = prune_dataticks(ticdef->def.user);
	if (!ticdef->def.user && ticdef->type == TIC_USER)
	    ticdef->type = TIC_COMPUTED;
    }
    for (axis=0; axisdef.user)
	    ticdef->def.user = prune_dataticks(ticdef->def.user);
	if (!ticdef->def.user && ticdef->type == TIC_USER)
	    ticdef->type = TIC_COMPUTED;
    }

    /* use the default dummy variable unless changed */
    if (dummy_token > 0)
	copy_str(c_dummy_var[0], dummy_token, MAX_ID_LEN);
    else
	strcpy(c_dummy_var[0], set_dummy_var[0]);

    eval_plots();
}


/* Helper function for refresh command.  Reexamine each data point and update the
 * flags for INRANGE/OUTRANGE/UNDEFINED based on the current limits for that axis.
 * Normally the axis limits are already known at this point. But if the user has
 * forced "set autoscale" since the previous plot or refresh, we need to reset the
 * axis limits and try to approximate the full auto-scaling behaviour.
 */
void
refresh_bounds(struct curve_points *first_plot, int nplots)
{
    struct curve_points *this_plot = first_plot;
    int iplot;		/* plot index */

    for (iplot = 0;  iplot < nplots; iplot++, this_plot = this_plot->next) {
	int i;		/* point index */
	struct axis *x_axis = &axis_array[this_plot->x_axis];
	struct axis *y_axis = &axis_array[this_plot->y_axis];

	/* IMAGE clipping is done elsewhere, so we don't need INRANGE/OUTRANGE checks */
	if (this_plot->plot_style == IMAGE || this_plot->plot_style == RGBIMAGE) {
	    if (x_axis->set_autoscale || y_axis->set_autoscale)
		process_image(this_plot, IMG_UPDATE_AXES);
	    continue;
	}

	for (i=0; ip_count; i++) {
	    struct coordinate *point = &this_plot->points[i];

	    if (point->type == UNDEFINED)
		continue;
	    else
		point->type = INRANGE;

	    /* This autoscaling logic is identical to that in
	     * refresh_3dbounds() in plot3d.c
	     */
	    if (!this_plot->noautoscale) {
		autoscale_one_point(x_axis, point->x);
		if (this_plot->plot_style & PLOT_STYLE_HAS_VECTOR)
		    autoscale_one_point(x_axis, point->xhigh);
	    }
	    if (!inrange(point->x, x_axis->min, x_axis->max)) {
		point->type = OUTRANGE;
		continue;
	    }
	    if (!this_plot->noautoscale) {
		autoscale_one_point(y_axis, point->y);
		if (this_plot->plot_style == VECTOR)
		    autoscale_one_point(y_axis, point->yhigh);
	    }
	    if (!inrange(point->y, y_axis->min, y_axis->max)) {
		point->type = OUTRANGE;
		continue;
	    }
	}
	if (this_plot->plot_style == BOXES || this_plot->plot_style == IMPULSES)
	    impulse_range_fiddling(this_plot);
    }

    this_plot = first_plot;
    for (iplot = 0;  iplot < nplots; iplot++, this_plot = this_plot->next) {

	/* handle 'reverse' ranges */
	axis_check_range( this_plot->x_axis );
	axis_check_range( this_plot->y_axis );

	/* Make sure the bounds are reasonable, and tweak them if they aren't */
	axis_checked_extend_empty_range(this_plot->x_axis, NULL);
	axis_checked_extend_empty_range(this_plot->y_axis, NULL);
    }
}


/* current_plot->token is after datafile spec, for error reporting
 * it will later be moved past title/with/linetype/pointtype
 */
static int
get_data(struct curve_points *current_plot)
{
    int i /* num. points ! */ , j;
    int ngood;
    int max_cols, min_cols;    /* allowed range of column numbers */
    struct coordinate *cp;
    double v[MAXDATACOLS];
    memset(v, 0, sizeof(v));

    if (current_plot->varcolor == NULL) {
	TBOOLEAN variable_color = FALSE;
	if ((current_plot->lp_properties.pm3d_color.type == TC_RGB)
	&&  (current_plot->lp_properties.pm3d_color.value < 0))
	    variable_color = TRUE;
	if (current_plot->lp_properties.pm3d_color.type == TC_Z)
	    variable_color = TRUE;
	if (current_plot->lp_properties.pm3d_color.type == TC_COLORMAP)
	    variable_color = TRUE;
	if (current_plot->lp_properties.l_type == LT_COLORFROMCOLUMN)
	    variable_color = TRUE;
	if ((current_plot->plot_style == VECTOR || current_plot->plot_style == ARROWS)
	&&  (current_plot->arrow_properties.tag == AS_VARIABLE))
	    variable_color = TRUE;
	if (current_plot->plot_smooth != SMOOTH_NONE) {
	    /* FIXME:  It would be possible to support smooth cspline lc palette */
	    /* but it would require expanding and interpolating plot->varcolor   */
	    /* in parallel with the y values.                                    */
	    variable_color = FALSE;
	}
	if (variable_color) {
	    current_plot->varcolor = gp_alloc(current_plot->p_max * sizeof(double),
		"varcolor array");
	}
    }

    /* eval_plots has already opened file */

    /* HBB 2000504: For most 2D plot styles the 'z' coordinate is unused.
     * Set it to NO_AXIS to account for that. For styles that use
     * the z coordinate as a real coordinate (i.e. not a width or
     * 'delta' component, change the setting inside the switch: */
    if ((current_plot->plot_filter == FILTER_ZSORT)
    ||  (current_plot->plot_style == SURFACEGRID)) {
	current_plot->z_axis = FIRST_Z_AXIS;
	axis_init(&axis_array[FIRST_Z_AXIS], TRUE);
    } else
	current_plot->z_axis = NO_AXIS;

    /* HBB NEW 20060427: if there's only one, explicit using column,
     * it's y data.  df_axis[] has to reflect that, so df_readline()
     * will expect time/date input. */
    if (df_no_use_specs == 1)
	df_axis[0] = df_axis[1];

    switch (current_plot->plot_style) { /* set maximum columns to scan */
    case XYERRORLINES:
    case XYERRORBARS:
    case BOXXYERROR:
	min_cols = 4;
	max_cols = 7;

	if (df_no_use_specs >= 6) {
	    /* HBB 20060427: signal 3rd and 4th column are absolute x
	     * data --- needed so time/date parsing works */
	    df_axis[2] = df_axis[3] = df_axis[0];
	    /* and 5th and 6th are absolute y data */
	    df_axis[4] = df_axis[5] = df_axis[1];
	}

	break;

    case FINANCEBARS:
	/* HBB 20000504: use 'z' coordinate for y-axis quantity */
	current_plot->z_axis = current_plot->y_axis;
	min_cols = 5;
	max_cols = 6;
	/* HBB 20060427: signal 3rd and 4th column are absolute y data
	 * --- needed so time/date parsing works */
	df_axis[2] = df_axis[3] = df_axis[4] = df_axis[1];
	break;

    case BOXPLOT:
	min_cols = 2;		/* fixed x, lots of y data points */
	max_cols = 4;		/* optional width, optional factor */
	expect_string( 4 );
	break;

    case CANDLESTICKS:
	current_plot->z_axis = current_plot->y_axis;
	min_cols = 5;
	max_cols = 7;
	df_axis[2] = df_axis[3] = df_axis[4] = df_axis[1];
	break;

    case BOXERROR:
	min_cols = 3;
	max_cols = 6;

	/* There are four possible cases: */
	/* 3 cols --> (x,y,dy), auto dx */
	/* 4 cols, boxwidth==-2 --> (x,y,ylow,yhigh), auto dx */
	/* 4 cols, boxwidth!=-2 --> (x,y,dy,dx) */
	/* 5 cols --> (x,y,ylow,yhigh,dx) */
	/* In each case an additional column may hold variable color */
	if ((df_no_use_specs == 4 && boxwidth == -2)
	    || df_no_use_specs >= 5)
	    /* HBB 20060427: signal 3rd and 4th column are absolute y
	     * data --- needed so time/date parsing works */
	    df_axis[2] = df_axis[3] = df_axis[1];
	break;

    case VECTOR:	/* x, y, dx, dy, variable arrow style and/or variable color */
    case ARROWS:	/* x, y, len, ang, variable arrow style and/or variable color */
	min_cols = 4;
	max_cols = 6;
	break;

    case XERRORLINES:
    case XERRORBARS:
	min_cols = 3;
	max_cols = 5;
	if (df_no_use_specs >= 4)
	    /* HBB 20060427: signal 3rd and 4th column are absolute x
	     * data --- needed so time/date parsing works */
	    df_axis[2] = df_axis[3] = df_axis[0];
	break;

    case YERRORLINES:
    case YERRORBARS:
	min_cols = 2;
	max_cols = 7;
	if (df_no_use_specs >= 4)
	    /* HBB 20060427: signal 3rd and 4th column are absolute y
	     * data --- needed so time/date parsing works */
	    df_axis[2] = df_axis[3] = df_axis[1];
	break;

    case HISTOGRAMS:
	min_cols = 1;
	max_cols = 3;
	/* Stacked histogram get out of sync if "missing" values in the
	 * input data are silently ignored.  require_value() forces a
	 * value of NaN to be returned in this case.
	 */
	if (histogram_opts.type == HT_STACKED_IN_TOWERS
	||  histogram_opts.type == HT_STACKED_IN_LAYERS)
	    require_value(1);
	break;

    case BOXES:
	min_cols = 1;
	max_cols = 4;

	break;

    case FILLEDCURVES:
	min_cols = 1;
	max_cols = 3;
	df_axis[2] = df_axis[1];	/* Both curves use same y axis */
	break;

    case IMPULSES:	/* 2 + possible variable color */
    case POLYGONS:
    case LINES:
    case DOTS:
	min_cols = 1;
	max_cols = 3;
	break;

    case LABELPOINTS:
	/* 3 column data: X Y Label */
	/* extra columns allow variable pointsize, pointtype, and/or rotation */
	min_cols = 3;
	max_cols = 6;
	expect_string( 3 );
	break;

    case IMAGE:
	min_cols = 3;
	max_cols = 3;
	break;

    case RGBIMAGE:
	min_cols = 3;
	max_cols = 6;
	break;

    case RGBA_IMAGE:
	min_cols = 3;
	max_cols = 6;
	break;

    case CIRCLES:	/* 3 + possible variable color, or 5 + possible variable color */
	min_cols = 2;
	max_cols = 6;
	break;

    case ELLIPSES:
	min_cols = 2; /* x, y, major axis, minor axis */
	max_cols = 6; /* + optional angle, possible variable color */
	break;

    case POINTSTYLE:
    case LINESPOINTS:
	/* 1 column: y coordinate only */
	/* 2 columns x and y coordinates */
	/* Allow 1 extra column because of 'pointsize variable' */
	/* Allow 1 extra column because of 'pointtype variable' */
	/* Allow 1 extra column because of 'lc rgb variable'    */
	min_cols = 1;
	max_cols = 5;
	break;

    case PARALLELPLOT:
    case SPIDERPLOT:
	/* 1 column: y coordinate only */
	/* extra columns for variable color, pointtype, etc */
	min_cols = 1;
	max_cols = 4;
	break;

    case TABLESTYLE:
	min_cols = 1;
	max_cols = MAXDATACOLS;
	break;

    case SURFACEGRID:
	min_cols = 3;
	max_cols = 3;
	break;

    default:
	min_cols = 1;
	max_cols = 2;
	break;
    }

    /* Restictions on plots with "smooth" option */
    switch (current_plot->plot_smooth) {
    case SMOOTH_NONE:
	break;
    case SMOOTH_ACSPLINES:
	max_cols++;
	break;
    default:
	if (df_no_use_specs > 2 && current_plot->plot_style != FILLEDCURVES
	&&  current_plot->plot_filter != FILTER_ZSORT)
	    int_warn(NO_CARET, "extra columns ignored by smoothing option");
	break;
    }
    if (current_plot->plot_smooth && current_plot->plot_style == FILLEDCURVES) {
	if (current_plot->filledcurves_options.closeto == FILLEDCURVES_CLOSED) {
	    if (current_plot->plot_smooth == SMOOTH_CSPLINES)
		current_plot->plot_smooth = SMOOTH_PATH;
	    if (current_plot->plot_smooth != SMOOTH_PATH
	    &&  current_plot->plot_smooth != SMOOTH_SMOOTH_HULL) {
		current_plot->plot_smooth = SMOOTH_NONE;
		int_warn(NO_CARET, "only 'smooth path' or 'smooth cspline' is supported for closed curves");
	    }
	}
    }

    /* May 2013 - Treating timedata columns as strings allows
     * functions column(N) and column("HEADER") to work on time data.
     * Sep 2014: But the column count is wrong for HISTOGRAMS
     */
    if (current_plot->plot_style != HISTOGRAMS) {
	if (axis_array[current_plot->x_axis].datatype == DT_TIMEDATE)
	    expect_string(1);
	if (axis_array[current_plot->y_axis].datatype == DT_TIMEDATE)
	    expect_string(2);
    }

    if (df_no_use_specs > max_cols)
	int_warn(NO_CARET, "more using specs than expected for this style");

    if (df_no_use_specs > 0 && df_no_use_specs < min_cols)
	int_error(NO_CARET, "Not enough columns for this style");

    i = 0; ngood = 0;

    /* If the user has set an explicit locale for numeric input, apply it */
    /* here so that it affects data fields read from the input file.      */
    set_numeric_locale();

    /* Initial state */
    df_warn_on_missing_columnheader = TRUE;

    while ((j = df_readline(v, max_cols)) != DF_EOF) {

	if (i >= current_plot->p_max) {
	    /* overflow about to occur. Extend size of points[]
	     * array. Double the size, and add 1000 points, to avoid
	     * needlessly small steps. */
	    cp_extend(current_plot, i + i + 1000);
	}

	/* Assume range is OK; we will check later */
	current_plot->points[i].type = (j == 0) ? UNDEFINED : INRANGE;

	/* First handle all the special cases (j token, "Bad data on line %d of file %s",
		      df_line_number, df_filename ? df_filename : "");
	    continue;

	case DF_COMPLEX_VALUE:
	    n_complex_values++;
	    fprintf(stderr,"plot2d.c:%d caught a complex value\n",__LINE__);
	    /* Fall through to normal undefined case */
	case DF_UNDEFINED:
	    /* Version 5 - We are now trying to pass back all available info even
	     * if one of the requested columns was missing or undefined.
	     */
	    current_plot->points[i].type = UNDEFINED;
	    if (missing_val && !strcmp(missing_val, "NaN")) {
		j = DF_MISSING;
		/* fall through to short-circuit for missing data */
	    } else {
		j = df_no_use_specs;
		break;
		/* continue with normal processing for this line */
	    }
	
	case DF_MISSING:
	    /* Plot type specific handling of missing points goes here. */
	    if ((current_plot->plot_style == PARALLELPLOT)
	    ||  (current_plot->plot_style == SPIDERPLOT)) {
		current_plot->points[i].type = UNDEFINED;
		j = df_no_use_specs;
		break;
	    }
	    if (current_plot->plot_style == HISTOGRAMS) {
		current_plot->points[i].type = UNDEFINED;
		i++;
	    }
	    if (current_plot->plot_style == TABLESTYLE) {
		j = df_no_use_specs;
		break;
	    }
	    continue;

	case DF_FIRST_BLANK:
	    /* The binary input routines generate DF_FIRST_BLANK at the end
	     * of scan lines, so that the data may be used for the isometric
	     * splots.  Rather than turning that off inside the binary
	     * reading routine based upon the plot mode, DF_FIRST_BLANK is
	     * ignored for certain plot types requiring 3D coordinates in
	     * MODE_PLOT.
	     */
	    if (current_plot->plot_style == IMAGE
	    ||  current_plot->plot_style == RGBIMAGE
	    ||  current_plot->plot_style == RGBA_IMAGE)
		continue;

	    /* make type of next point undefined, but recognizable */
	    current_plot->points[i] = blank_data_line;
	    i++;
	    continue;

	case DF_SECOND_BLANK:
	    /* second blank line. We dont do anything
	     * (we did everything when we got FIRST one)
	     */
	    continue;

	case DF_FOUND_KEY_TITLE:
	    df_set_key_title(current_plot);
	    continue;

	case DF_KEY_TITLE_MISSING:
	    fprintf(stderr,"get_data: key title not found in requested column\n");
	    continue;

	case DF_COLUMN_HEADERS:
	    continue;

	default:
	    if (j < 0) {
		df_close();
		int_error(c_token,
			"internal error : df_readline returned %d : datafile line %d",
			j, df_line_number);
	    }
	    break;	/* Not continue!! */
	}

	/* We now know that j > 0, i.e. there is some data on this input line */
	ngood++;

	/* "plot ... with table" bypasses all the column interpretation */
	if (current_plot->plot_style == TABLESTYLE) {
	    tabulate_one_line(v, df_strings, j);
	    continue;
	}

	/* June 2010 - New mechanism for variable color                  */
	/* If variable color is requested, take the color value from the */
	/* final column of input and decrement the column count by one.  */
	if (current_plot->varcolor) {
	    static char *errmsg = "Not enough columns for variable color";
	    switch (current_plot->plot_style) {

	    case CANDLESTICKS:
	    case FINANCEBARS:
			    if (j < 6) int_error(NO_CARET,errmsg);
			    break;
	    case XYERRORLINES:
	    case XYERRORBARS:
	    case BOXXYERROR:
			    if (j != 7 && j != 5) int_error(NO_CARET,errmsg);
			    break;
	    case VECTOR:
	    case ARROWS:
			    if (j < 5) int_error(NO_CARET,errmsg);
			    /* j can be 5 if the variable styles have constant color */
			    if (j == 5 && current_plot->arrow_properties.tag == AS_VARIABLE)
				v[j++] = 0;
			    break;
	    case LABELPOINTS:
	    case BOXERROR:
	    case XERRORLINES:
	    case XERRORBARS:
	    case YERRORLINES:
	    case YERRORBARS:
			    if (j < 4) int_error(NO_CARET,errmsg);
			    break;
	    case CIRCLES:
			    if (j == 5 || j < 3) int_error(NO_CARET,errmsg);
			    break;
	    case ELLIPSES:
	    case BOXES:
	    case POINTSTYLE:
	    case LINESPOINTS:
	    case IMPULSES:
	    case LINES:
	    case DOTS:
			    if (j < 3) int_error(NO_CARET,errmsg);
			    break;
	    case PARALLELPLOT:
	    case SPIDERPLOT:
			    if (j < 1) int_error(NO_CARET,errmsg);
			    break;
	    case BOXPLOT:
			    /* Only the key sample uses this value */
			    v[j++] = current_plot->base_linetype + 1;
			    break;
	    default:
		break;
	    }

	    current_plot->varcolor[i] = v[--j];
	}

	/* Unusual special cases */

	/* In spiderplots the implicit "x coordinate" v[0] is really the axis number. */
	/* Add this at the front and shift all other using specs to the right.        */
	if (spiderplot) {
	    int is;
	    for (is=j++; is>0; is--)
		v[is] = v[is-1];
	    v[0] = paxis_current;
	}

	/* Single data value - is it y with implicit x or something else? */
	if (j == 1 && !(current_plot->plot_style == HISTOGRAMS)) {
	    if (default_smooth_weight(current_plot->plot_smooth))
		v[1] = 1.0;
	    else if (current_plot->plot_filter == FILTER_BINS)
		v[1] = 1.0;
	    else {
		v[1] = v[0];
		v[0] = df_datum;
	    }
	    j = 2;
	}

	/* May 2018:  The huge switch statement below is now organized by plot	*/
	/* style.  Each plot style can have its own understanding of what the	*/
	/* value in a particular field of the "using" specifier represents.	*/
	/* E.g. the 3rd field might be z or radius or color.			*/
	switch (current_plot->plot_style) {

	case LINES:
	case DOTS:
	case IMPULSES:
	{   /* x y [acspline weight] */
	    coordval w;	/* only for (current_plot->plot_smooth == SMOOTH_ACSPLINES) */
	    w = (j > 2) ? v[2] : 1.0;
	    store2d_point(current_plot, i++, v[0], v[1],
				v[0], v[0], v[1], v[1], w);
	    break;
	}

	case POINTSTYLE:
	case LINESPOINTS:
	{   /* x y {z} {var_ps} {var_pt} {lc variable} */
	    /* NB: assumes CRD_PTSIZE == xlow CRD_PTTYPE == xhigh CRD_PTCHAR == ylow */
	    int var = 2; /* column number for next variable spec */
	    coordval weight = (current_plot->plot_smooth == SMOOTH_ACSPLINES) ? v[2] : 1.0;
	    coordval var_ps = current_plot->lp_properties.p_size;
	    coordval var_pt = current_plot->lp_properties.p_type;
	    coordval var_char = 0;
	    if (current_plot->plot_filter == FILTER_ZSORT)
		weight = v[var++];
	    if (var_ps == PTSZ_VARIABLE)
		var_ps = v[var++];
	    if (var_pt == PT_VARIABLE) {
		if (isnan(v[var]) && df_tokens[var]) {
		    safe_strncpy( (char *)(&var_char), df_tokens[var], sizeof(coordval));
		    truncate_to_one_utf8_char((char *)(&var_char));
		}
		var_pt = v[var++];
	    }
	    if (var > j)
		int_error(NO_CARET, "Not enough using specs");
	    if (var_pt < 0)
		var_pt = 0;
	    store2d_point(current_plot, i++, v[0], v[1],
					var_ps, var_pt, var_char, v[1], weight);
	    break;
	}

	case LABELPOINTS:
	{   /* x y string {rotate variable}
	     *            {point {pt variable} {ps variable}}
	     *            {tc|lc variable}
	     */
	    int var = 3;	/* column number for next variable spec */
	    coordval var_rotation = current_plot->labels->rotate;
	    coordval var_ps = current_plot->labels->lp_properties.p_size;
	    coordval var_pt = current_plot->labels->lp_properties.p_type;

	    if (current_plot->labels->tag == LABEL_TAG_VARIABLE_ROTATE)
		var_rotation = v[var++];
	    if (var_ps == PTSZ_VARIABLE)
		var_ps = v[var++];
	    if (var_pt == PT_VARIABLE)
		var_pt = v[var++] - 1;
	    if (var > j)
		int_error(NO_CARET, "Not enough using specs");

	    store2d_point(current_plot, i, v[0], v[1],
				var_ps, var_pt, var_rotation, v[1], 0.0);

	    /* Allocate and fill in a text_label structure to match it */
	    if (current_plot->points[i].type != UNDEFINED) {
		store_label(current_plot->labels, &(current_plot->points[i]),
			i, df_tokens[2],
			current_plot->varcolor ? current_plot->varcolor[i] : 0.0);
	    }
	    i++;
	    break;
	}

	case STEPS:
	case FSTEPS:
	case FILLSTEPS:
	case HISTEPS:
	{    /* x y */
	    store2d_point(current_plot, i++, v[0], v[1],
				v[0], v[0], v[1], v[1], -1.0);
	    break;
	}

	case CANDLESTICKS:
	case FINANCEBARS:
	{   /* x yopen ylow yhigh yclose [xhigh] */
	    coordval yopen = v[1];
	    coordval ylow = v[2];
	    coordval yhigh = v[3];
	    coordval yclose = v[4];
	    coordval xlow = v[0];
	    coordval xhigh = v[0];

	    /* NB: plot_c_bars will set xhigh = xlow + 2*(x-xlow) */
	    if (j > 5 && v[5] > 0)
		xlow = v[0] - v[5]/2.;
	    store2d_point(current_plot, i++, v[0], yopen,
			xlow, xhigh, ylow, yhigh, yclose);
	    break;
	}

	case XERRORLINES:
	case XERRORBARS:
	{   /* x y xdelta   or    x y xlow xhigh */
	    coordval xlow  = (j > 3) ? v[2] : v[0] - v[2];
	    coordval xhigh = (j > 3) ? v[3] : v[0] + v[2];
	    store2d_point(current_plot, i++, v[0], v[1],
			xlow, xhigh, v[1], v[1], 0.0);
	    break;
	}

	case YERRORLINES:
	case YERRORBARS:
	{   /* NB: assumes CRD_PTSIZE == xlow CRD_PTTYPE == xhigh CRD_PTCHAR == ylow
		   lc variable, if present, was already extracted and j reduced by 1
	     */
	    /* x y ylow yhigh {var_ps} {var_pt} {lc variable} */
	    coordval ylow  = v[2];
	    coordval yhigh = v[3];
	    coordval var_pt = current_plot->lp_properties.p_type;
	    coordval var_ps = current_plot->lp_properties.p_size;

	    if (var_pt == PT_VARIABLE) {
		if (j < 4)
		    int_error(NO_CARET, "Not enough using specs");
		var_pt = v[--j];
	    }
	    if (!(var_pt > 0)) /* Catches CRD_PTCHAR (NaN) also */
		var_pt = 0;
	    if (var_ps == PTSZ_VARIABLE) {
		if (j < 4)
		    int_error(NO_CARET, "Not enough using specs");
		var_ps = v[--j];
	    }
	    if (j == 3) {
		ylow = v[1] - v[2];
		yhigh = v[1] + v[2];
	    }
	    store2d_point(current_plot, i++, v[0], v[1],
					var_ps, var_pt, ylow, yhigh, -1.0);
	    break;
	}

	case BOXERROR:
	{   /* 3 columns:  x y ydelta
	     * 4 columns:  x y ydelta xdelta     (if xdelta 5) ? v[5] : v[1] + v[3];
	    store2d_point(current_plot, i++, v[0], v[1],
			xlow, xhigh, ylow, yhigh, 0.0);
	    if (j == 5)
		int_error(NO_CARET, "wrong number of columns for this plot style");
	    break;
	}

	case BOXES:
	{   /* 2 columns: x y (width depends on "set boxwidth")
	     * 3 columns: x y xdelta
	     * 4 columns: x y xlow xhigh
	     */
	    coordval xlow  = v[0];
	    coordval xhigh = v[0];
	    coordval width = 0.0;
	    double base = axis_array[current_plot->x_axis].base;
	    if (j == 2) {
		/* For boxwidth auto, we cannot calculate xlow/xhigh yet since they
		 * depend on both adjacent boxes.  This is signalled by storing -1
		 * in point->z to indicate xlow/xhigh must be calculated later.
		 */
		if (boxwidth > 0 && boxwidth_is_absolute) {
		    xlow = (axis_array[current_plot->x_axis].log)
			 ? v[0] * pow(base, -boxwidth/2.) : v[0] - boxwidth / 2;
		    xhigh = (axis_array[current_plot->x_axis].log)
			 ? v[0] * pow(base, boxwidth/2.) : v[0] + boxwidth / 2;
		} else {
		    width = -1.0;
		}
	    } else if (j == 3) {
		xlow  = v[0] - v[2]/2;
		xhigh = v[0] + v[2]/2;
	    } else if (j == 4) {
		xlow  = v[2];
		xhigh = v[3];
	    }
	    store2d_point(current_plot, i++, v[0], v[1],
			xlow, xhigh, v[1], v[1], width);
	    break;
	}

	case FILLEDCURVES:
	{   /* 2 columns:  x y
	     * 3 columns:  x y1 y2
	     */
	    coordval y1 = v[1];
	    coordval y2;
	    coordval w = 0.0;	/* only needed for SMOOTH_ACSPLINES) */
	    if (j==2) {
		if (current_plot->filledcurves_options.closeto == FILLEDCURVES_CLOSED
		||  current_plot->filledcurves_options.closeto == FILLEDCURVES_DEFAULT)
		    y2 = y1;
		else
		    y2 = current_plot->filledcurves_options.at;
	    } else if (current_plot->plot_smooth == SMOOTH_SMOOTH_HULL) {
		y2 = y1;
	    } else {
		y2 = v[2];
		if (current_plot->filledcurves_options.closeto == FILLEDCURVES_DEFAULT)
		    current_plot->filledcurves_options.closeto = FILLEDCURVES_BETWEEN;
		if (current_plot->filledcurves_options.closeto == FILLEDCURVES_BETWEEN
		||  current_plot->filledcurves_options.closeto == FILLEDCURVES_ABOVE
		||  current_plot->filledcurves_options.closeto == FILLEDCURVES_BELOW) {
		    switch (current_plot->plot_smooth) {
			case SMOOTH_NONE:
			case SMOOTH_CSPLINES:
			case SMOOTH_SBEZIER:
			    break;
			case SMOOTH_ACSPLINES:
			    w = (j > 3) ? v[3] : 1.0;
			    break;
			default:
			    int_warn(NO_CARET, "use csplines, acsplines or sbezier to smooth non-closed filledcurves");
			    current_plot->plot_smooth = SMOOTH_NONE;
			    break;
		    }
		}
	    }
	    store2d_point(current_plot, i++, v[0], y1,
			v[0], v[0], y1, y2, w);
	    break;
	}

	case POLYGONS:
	{   /* Nothing yet to distinguish this from filledcurves */
	    store2d_point(current_plot, i++, v[0], v[1], v[0], v[0], v[1], v[1], 0);
	    break;
	}

	case BOXPLOT:
	{   /* 2 columns:  x data
	     * 3 columns:  x data width
	     * 4 columns:  x data width factor
	     */
	    coordval extra = DEFAULT_BOXPLOT_FACTOR;
	    coordval xlow =  (j > 2) ? v[0] - v[2]/2. : v[0];
	    coordval xhigh = (j > 2) ? v[0] + v[2]/2. : v[0];
	    if (j == 4)
		extra = check_or_add_boxplot_factor(current_plot, df_tokens[3], v[0]);
	    store2d_point(current_plot, i++, v[0], v[1],
			xlow, xhigh, v[1], v[1], extra);
	    break;
	}

	case VECTOR:
	{   /* 	x y xdelta ydelta [arrowstyle variable] */
	    coordval xlow  = v[0];
	    coordval xhigh = v[0] + v[2];
	    coordval ylow  = v[1];
	    coordval yhigh = v[1] + v[3];
	    coordval arrowstyle = (j >= 5) ? v[4] : 0.0;
	    store2d_point(current_plot, i++, v[0], v[1],
			  xlow, xhigh, ylow, yhigh, arrowstyle);
	    break;
	}

	case ARROWS:
	{   /* 	x y length angle [arrowstyle variable] */
	    coordval xlow  = v[0];
	    coordval ylow  = v[1];
	    coordval len = v[2];
	    coordval ang = v[3];
	    coordval arrowstyle = (j >= 5) ? v[4] : 0.0;
	    store2d_point(current_plot, i++, v[0], v[1],
			  xlow, len, ylow, ang, arrowstyle);
	    break;
	}

	case CIRCLES:
	{   /* x y
	     * x y radius
	     * x y radius arc_begin arc_end
	     */
	    coordval x = v[0];
	    coordval y = v[1];
	    coordval xlow = x;
	    coordval xhigh = x;
	    coordval arc_begin = (j >= 5) ? v[3] : 0.0;
	    coordval arc_end = (j >= 5) ? v[4] : 360.0;
	    coordval radius = DEFAULT_RADIUS;

	    if (j >= 3 && v[2] >= 0) {
		xlow  = x - v[2];
		xhigh = x + v[2];
		radius = 0.0;
	    }
	    store2d_point(current_plot, i++, x, y,
			  xlow, xhigh, arc_begin, arc_end, radius);
	    break;
	}

	case ELLIPSES:
	{   /* x y
	     * x y diam  (used for both major and minor axis)
	     * x y major_diam minor_diam
	     * x y major_diam minor_diam orientation
	     */
	    coordval x = v[0];
	    coordval y = v[1];
	    coordval major_axis = (j >= 3) ? v[2] : 0.0;
	    coordval minor_axis = (j >= 4) ? v[3] : (j >= 3) ? v[2] : 0.0;
	    coordval orientation = (j >= 5) ? v[4] : 0.0;
	    coordval flag = (major_axis  16) & 0xff;
		v[3] = (argb >> 8) & 0xff;
		v[4] = (argb) & 0xff;
		/* The alpha channel convention is unfortunate */
		v[5] = 255 - (unsigned int)((argb >> 24) & 0xff);
	    }
	    cp = &(current_plot->points[i]);
	    cp->CRD_R = v[2];
	    cp->CRD_G = v[3];
	    cp->CRD_B = v[4];
	    cp->CRD_A = v[5];	/* Alpha channel */
	    i++;
	    break;
	}

	case HISTOGRAMS:
	{   /* 1 column:	y
	     * 2 columns:	y yerr		(set style histogram errorbars)
	     * 3 columns:	y ymin ymax	(set style histogram errorbars)
	     */
	    coordval x = df_datum;
	    coordval y = v[0];
	    coordval ylow  = v[0];
	    coordval yhigh = v[0];
	    coordval width = (boxwidth > 0) ? boxwidth : 1.0;
	    coordval xlow  = x - width / 2.;
	    coordval xhigh = x + width / 2.;

	    if (histogram_opts.type == HT_ERRORBARS) {
		if (j == 1)
		    int_error(c_token, "No column given for errorbars in using specifier");
		if (j == 2) {
		    ylow  = y - v[1];
		    yhigh = y + v[1];
		} else {
		    ylow   = v[1];
		    yhigh  = v[2];
		}
	    } else if (j > 1)
		int_error(c_token, "Too many columns in using specification");

	    if (histogram_opts.type == HT_STACKED_IN_TOWERS) {
		histogram_rightmost = current_plot->histogram_sequence
				    + current_plot->histogram->start;
		current_plot->histogram->end = histogram_rightmost;
	    } else if (x + current_plot->histogram->start > histogram_rightmost) {
		histogram_rightmost = x + current_plot->histogram->start;
		current_plot->histogram->end = histogram_rightmost;
	    }
	    store2d_point(current_plot, i++, x, y, xlow, xhigh, ylow, yhigh, 0.0);
	    break;
	}

	case PARALLELPLOT:
	{   /* Similar to histogram plots, each parallel axis gets a separate
	     * comma-separated plot element with a single "using" spec.
	     */
	    coordval x = parallel_axis_array[paxis_current-1].paxis_x;
	    coordval y = v[1];
	    store2d_point(current_plot, i++, x, y, x, x, y, y, 0.0);
	    break;
	}

	case SPIDERPLOT:
	{   /* Spider plots are essentially parallelaxis plots in polar coordinates.
	     */
	    coordval var_color = current_plot->varcolor ? current_plot->varcolor[i] : i;
	    coordval var_pt = current_plot->lp_properties.p_type;
	    coordval theta = paxis_current;
	    coordval r = v[1];
	    var_pt = (var_pt == PT_VARIABLE) ? v[2] : var_pt + 1;
	    store2d_point(current_plot, i++, theta, r, theta, var_pt, r, var_color, 0.0);
	    break;
	}

	case SURFACEGRID:
	{   /* Avoid calling store2d_point(), which would convert to Cartesian coordinates. */
	    if (!polar)
		int_error(NO_CARET, "For non-polar gridded surfaces use splot");
#ifdef USE_POLAR_GRID
	    store_polar_point(current_plot, i++, v);
#else
	    int_error(NO_CARET,
		"This copy of gnuplot was built without support for polar surfaces");
#endif
	    break;
	}

	/* These exist for 3D (splot) but not for 2D (plot) */
	case PM3DSURFACE:
	case ZERRORFILL:
	case ISOSURFACE:
	    int_error(NO_CARET, "This plot style only available for splot");
	    break;

	/* "with mask" indicates a polygon data set that is to be read in
	 * but saved for use as a mask rather than being plotted itself
	 */
	case POLYGONMASK:
	    store2d_point(current_plot, i++, v[0], v[1], v[0], v[0], v[1], v[1], 0);
	    break;

	/* If anybody hits this it is because we missed handling a plot style above.
	 * To be fixed immediately!
	 */
	default:
	    int_error(NO_CARET,
		"This plot style must have been missed in the grand code reorganization");
	    break;

	}    /* switch (plot->plot_style) */

    }	/* while more input data */

    /* This removes an extra point caused by blank lines after data. */
    if (i > 0 && current_plot->points[i-1].type == UNDEFINED)
	i--;

    current_plot->p_count = i;
    cp_extend(current_plot, i); /* shrink to fit */

    df_close();

    /* We are finished reading user input; return to C locale for internal use */
    reset_numeric_locale();

    return ngood;                   /* 0 indicates an 'empty' file */
}


/* called by get_data for each point */
static void
store2d_point(
    struct curve_points *current_plot,
    int i,                      /* point number */
    double x, double y,
    double xlow, double xhigh,
    double ylow, double yhigh,
    double width)               /* BOXES widths: -1 -> autocalc, 0 ->  use xlow/xhigh */
{
    struct coordinate *cp = &(current_plot->points[i]);
    struct axis *x_axis_ptr, *y_axis_ptr;
    coord_type *y_type_ptr;
    coord_type dummy_type = INRANGE;   /* sometimes we dont care about outranging */
    TBOOLEAN excluded_range = FALSE;

    /* FIXME this destroys any UNDEFINED flag assigned during input */
    cp->type = INRANGE;

    if (polar) {
	double theta = x;
	AXIS *theta_axis = &axis_array[T_AXIS];

	/* "x" is really the polar angle theta,	so check it against trange. */
	if (theta < theta_axis->data_min)
	    theta_axis->data_min = theta;
	if (theta > theta_axis->data_max)
	    theta_axis->data_max = theta;
	if ( theta < theta_axis->min
	&&  (theta max || theta_axis->max == -VERYLARGE)) {
	    if ((theta_axis->autoscale & AUTOSCALE_MAX) == 0)
		excluded_range = TRUE;
	}
	if ( theta > theta_axis->max
	&&  (theta >= theta_axis->min || theta_axis->min == VERYLARGE)) {
	    if ((theta_axis->autoscale & AUTOSCALE_MIN) == 0)
		excluded_range = TRUE;
	}

	/* "y" at this point is really "r", so check it against rrange.	*/
	if (y < R_AXIS.data_min)
	    R_AXIS.data_min = y;
	if (y > R_AXIS.data_max)
	    R_AXIS.data_max = y;

	/* Convert from polar to cartesian coordinates and check ranges */
	if (polar_to_xy(x, y, &x, &y, TRUE) == OUTRANGE)
	    cp->type = OUTRANGE;

	/* Some plot styles use xhigh and yhigh for other quantities, */
	/* which polar mode transforms would break		      */
	if (current_plot->plot_style == CIRCLES) {
	    double radius = (xhigh - xlow)/2.0;
	    xlow = x - radius;
	    xhigh = x + radius;

	} else {
	    /* Jan 2017 - now skipping range check on rhigh, rlow */
	    (void) polar_to_xy(xhigh, yhigh, &xhigh, &yhigh, FALSE);
	    (void) polar_to_xy(xlow, ylow, &xlow, &ylow, FALSE);
	}
    }

    /* Version 5: Allow to store Inf or NaN
     *  We used to exit immediately in this case rather than storing anything
     */
    x_axis_ptr = &axis_array[current_plot->x_axis];
    dummy_type = cp->type;	/* Save result of range check on x */
    y_axis_ptr = &axis_array[current_plot->y_axis];

    store_and_update_range(&(cp->x), x, &(cp->type), x_axis_ptr, current_plot->noautoscale);
    store_and_update_range(&(cp->y), y, &(cp->type), y_axis_ptr, current_plot->noautoscale);

    /* special cases for the "y" axes of parallel axis plots */
    if ((current_plot->plot_style == PARALLELPLOT)
    ||  (current_plot->plot_style == SPIDERPLOT)) {
	y_type_ptr = &dummy_type;	/* Use xrange test result as a start point */
	y_axis_ptr = ¶llel_axis_array[current_plot->p_axis-1];
	store_and_update_range(&(cp->y), y, y_type_ptr, y_axis_ptr, FALSE);
    } else {
	dummy_type = INRANGE;
    }

    switch (current_plot->plot_style) {
    case POINTSTYLE:		/* Only x and y are relevant to axis scaling */
    case LINES:
    case LINESPOINTS:
    case LABELPOINTS:
    case DOTS:
    case IMPULSES:
    case STEPS:
    case FSTEPS:
    case HISTEPS:
    case ARROWS:
    case PARALLELPLOT:
    case SPIDERPLOT:
	cp->xlow = xlow;
	cp->xhigh = xhigh;
	cp->ylow = ylow;
	cp->yhigh = yhigh;
	break;
    case YERRORBARS:		/* auto-scale ylow yhigh */
    case YERRORLINES:		/* auto-scale ylow yhigh */
	cp->CRD_PTSIZE = xlow;
	cp->CRD_PTTYPE = xhigh;
	STORE_AND_UPDATE_RANGE(cp->ylow, ylow, dummy_type, current_plot->y_axis,
				current_plot->noautoscale, cp->ylow = -VERYLARGE);
	STORE_AND_UPDATE_RANGE(cp->yhigh, yhigh, dummy_type, current_plot->y_axis,
				current_plot->noautoscale, cp->yhigh = -VERYLARGE);
	break;
    case BOXES:			/* auto-scale to xlow xhigh */
    case BOXPLOT:		/* auto-scale to xlow xhigh, factor is already in z */
	cp->ylow = ylow;	/* ylow yhigh not really needed but store them anyway */
	cp->yhigh = yhigh;
	STORE_AND_UPDATE_RANGE(cp->xlow, xlow, dummy_type, current_plot->x_axis, 
				current_plot->noautoscale, cp->xlow = -VERYLARGE);
	STORE_AND_UPDATE_RANGE(cp->xhigh, xhigh, dummy_type, current_plot->x_axis,
				current_plot->noautoscale, cp->xhigh = -VERYLARGE);
	break;
    case CIRCLES:
	cp->yhigh = yhigh;
	STORE_AND_UPDATE_RANGE(cp->xlow, xlow, dummy_type, current_plot->x_axis, 
				current_plot->noautoscale, cp->xlow = -VERYLARGE);
	STORE_AND_UPDATE_RANGE(cp->xhigh, xhigh, dummy_type, current_plot->x_axis,
				current_plot->noautoscale, cp->xhigh = -VERYLARGE);
	cp->ylow = ylow;	/* arc begin */
	cp->xhigh = yhigh;	/* arc end */
	if (fabs(ylow) > 1000. || fabs(yhigh) > 1000.) /* safety check for insane arc angles */
	    cp->type = UNDEFINED;
	break;
    case ELLIPSES:
	/* We want to pass the parameters to the ellipse drawing routine as they are, 
	 * so we have to calculate the extent of the ellipses for autoscaling here. 
	 * Properly calculating the correct extent of a rotated ellipse, respecting 
	 * axis scales and all would be very hard. 
	 * So we just use the larger of the two axes, multiplied by some empirical factors 
	 * to ensure^Whope that all parts of the ellipses will be in the auto-scaled area. */
	/* xlow = major axis, xhigh = minor axis, ylow = orientation */
#define YRANGE_FACTOR ((current_plot->ellipseaxes_units == ELLIPSEAXES_YY) ? 1.0 : 1.4)
#define XRANGE_FACTOR ((current_plot->ellipseaxes_units == ELLIPSEAXES_XX) ? 1.1 : 1.0)
	STORE_AND_UPDATE_RANGE(cp->xlow, x-0.5*GPMAX(xlow, xhigh)*XRANGE_FACTOR, 
				dummy_type, current_plot->x_axis, 
				current_plot->noautoscale, 
				cp->xlow = -VERYLARGE);
	STORE_AND_UPDATE_RANGE(cp->xhigh, x+0.5*GPMAX(xlow, xhigh)*XRANGE_FACTOR, 
				dummy_type, current_plot->x_axis, 
				current_plot->noautoscale, 
				cp->xhigh = -VERYLARGE);
	STORE_AND_UPDATE_RANGE(cp->ylow, y-0.5*GPMAX(xlow, xhigh)*YRANGE_FACTOR, 
				dummy_type, current_plot->y_axis, 
				current_plot->noautoscale, 
				cp->ylow = -VERYLARGE);
	STORE_AND_UPDATE_RANGE(cp->yhigh, y+0.5*GPMAX(xlow, xhigh)*YRANGE_FACTOR, 
				dummy_type, current_plot->y_axis, 
				current_plot->noautoscale,
				cp->yhigh = -VERYLARGE);
	/* So after updating the axes we re-store the parameters */
	cp->xlow = xlow;    /* major axis */
	cp->xhigh = xhigh;  /* minor axis */
	cp->ylow = ylow;    /* orientation */
	break;

    case IMAGE:
	STORE_AND_UPDATE_RANGE(cp->CRD_COLOR, width, dummy_type,
				COLOR_AXIS, current_plot->noautoscale, NOOP);
	break;

    default:			/* auto-scale to xlow xhigh ylow yhigh */
	STORE_AND_UPDATE_RANGE(cp->xlow, xlow, dummy_type, current_plot->x_axis, 
				current_plot->noautoscale, cp->xlow = -VERYLARGE);
	STORE_AND_UPDATE_RANGE(cp->xhigh, xhigh, dummy_type, current_plot->x_axis,
				current_plot->noautoscale, cp->xhigh = -VERYLARGE);
	STORE_AND_UPDATE_RANGE(cp->ylow, ylow, dummy_type, current_plot->y_axis,
				current_plot->noautoscale, cp->ylow = -VERYLARGE);
	STORE_AND_UPDATE_RANGE(cp->yhigh, yhigh, dummy_type, current_plot->y_axis,
					current_plot->noautoscale, cp->yhigh = -VERYLARGE);
	break;
    }

    /* HBB 20010214: if z is not used for some actual value, just
     * store 'width' to that axis and be done with it */
    if ((int)current_plot->z_axis == NO_AXIS)
	cp->z = width;
    else
	STORE_AND_UPDATE_RANGE(cp->z, width, dummy_type, current_plot->z_axis, 
				current_plot->noautoscale, cp->z = -VERYLARGE);

    /* If we have variable color corresponding to a z-axis value, use it to autoscale */
    if (current_plot->lp_properties.pm3d_color.type == TC_Z && current_plot->varcolor)
	STORE_AND_UPDATE_RANGE(current_plot->varcolor[i], current_plot->varcolor[i],
		dummy_type, COLOR_AXIS, current_plot->noautoscale, NOOP);

    /* Same thing for colormap z-values */
    if (current_plot->lp_properties.pm3d_color.type == TC_COLORMAP
	&& current_plot->varcolor && current_plot->lp_properties.colormap)
	STORE_AND_UPDATE_RANGE(current_plot->varcolor[i], current_plot->varcolor[i],
		dummy_type, COLOR_AXIS, current_plot->noautoscale, NOOP);

    /* July 2014 - Some points are excluded because they fall outside of trange	*/
    /* even though they would be inside the plot if drawn.			*/
    if (excluded_range)
	cp->type = EXCLUDEDRANGE;

}                               /* store2d_point */


/*
 * We abuse the labels structure to store a list of boxplot labels ("factors").
 * Check if  is already among the known factors, if not, add it to the list.
 */
static int
check_or_add_boxplot_factor(struct curve_points *plot, char* string, double x)
{
    char * trimmed_string;
    struct text_label *label, *prev_label, *new_label;
    int index = DEFAULT_BOXPLOT_FACTOR;

    /* If there is no factor column (4th using spec) fall back to a single boxplot */
    if (!string)
	return index;

    /* Remove the trailing garbage, quotes etc. from the string */ 
    trimmed_string = df_parse_string_field(string);

    if (strlen(trimmed_string) > 0) {
	TBOOLEAN new = FALSE;
	prev_label = plot->labels;
	if (!prev_label)
	    int_error(NO_CARET, "boxplot labels not initialized");
	for (label = prev_label->next; label; label = label->next, prev_label = prev_label->next) {
	    /* check if string is already stored */
	    if (!strcmp(trimmed_string, label->text))
		break;
	    /* If we are keeping a sorted list, test against current entry */
	    /* (insertion sort).					   */
	    if (boxplot_opts.sort_factors) {
		if (strcmp(trimmed_string, label->text) < 0) {
		    new = TRUE;
		    break;
		}
	    }
	}
	/* not found, so we add it now */
	if (!label || new) {
	    new_label = gp_alloc(sizeof(text_label),"boxplot label");
	    memcpy(new_label,plot->labels,sizeof(text_label));
	    new_label->next = label;
	    new_label->tag = plot->boxplot_factors++;
	    new_label->text = gp_strdup(trimmed_string);
	    new_label->place.x = plot->points[0].x;
	    prev_label->next = new_label;
	    label = new_label;
	}
	index = label->tag;
    }

    free(trimmed_string);
    return index;
}

/* Add tic labels to the boxplots, 
 * showing which level of the factor variable they represent */ 
static void
add_tics_boxplot_factors(struct curve_points *plot)
{
    AXIS_INDEX boxplot_labels_axis;
    text_label *this_label;
    int i = 0;

    boxplot_labels_axis = 
	boxplot_opts.labels == BOXPLOT_FACTOR_LABELS_X  ? FIRST_X_AXIS  :
	boxplot_opts.labels == BOXPLOT_FACTOR_LABELS_X2 ? SECOND_X_AXIS : 
	x_axis;
    for (this_label = plot->labels->next; this_label;
	 this_label = this_label->next) {
	    add_tic_user( &axis_array[boxplot_labels_axis], this_label->text,
		plot->points->x + i * boxplot_opts.separation, -1);
	    i++;
    }
}

/* Autoscaling of box plots cuts off half of the box on each end. */
/* Add a half-boxwidth to the range in this case.  EAM Aug 2007   */
static void
box_range_fiddling(struct curve_points *plot)
{
    double xlow, xhigh;
    int i = plot->p_count - 1;

    if (i x_axis].autoscale & AUTOSCALE_MIN) {
	if (plot->points[0].type != UNDEFINED && plot->points[1].type != UNDEFINED) {
	    if (boxwidth_is_absolute)
		xlow = plot->points[0].x - boxwidth;
	    else
		xlow = plot->points[0].x - (plot->points[1].x - plot->points[0].x) / 2.;
	    if (axis_array[plot->x_axis].min > xlow)
		axis_array[plot->x_axis].min = xlow;
	}
    }
    if (axis_array[plot->x_axis].autoscale & AUTOSCALE_MAX) {
	if (plot->points[i].type != UNDEFINED && plot->points[i-1].type != UNDEFINED) {
	    if (boxwidth_is_absolute)
		xhigh = plot->points[i].x + boxwidth;
	    else
		xhigh = plot->points[i].x + (plot->points[i].x - plot->points[i-1].x) / 2.;
	    if (axis_array[plot->x_axis].max < xhigh)
		axis_array[plot->x_axis].max = xhigh;
	}
    }
}

/* Autoscaling of boxplots with no explicit width cuts off the outer edges of the box */
static void
boxplot_range_fiddling(struct curve_points *plot)
{
    double extra_width;
    int N;

    if (plot->p_count boxplot_factors > 0) {
	if (boxplot_opts.labels != BOXPLOT_FACTOR_LABELS_OFF)
	    add_tics_boxplot_factors(plot);
    }

    /* Sort the points and removed any that are undefined */
    N = filter_boxplot(plot);
    plot->p_count = N;

    if (plot->points[0].type == UNDEFINED)
	int_error(NO_CARET,"boxplot has undefined x coordinate");

    /* If outliers were processed, that has taken care of autoscaling on y.
     * If not, we need to calculate the whisker bar ends to determine yrange.
     */
    if (boxplot_opts.outliers)
	restore_autoscaled_ranges(&axis_array[plot->x_axis], NULL);
    else
	restore_autoscaled_ranges(&axis_array[plot->x_axis], &axis_array[plot->y_axis]);
    autoscale_boxplot(plot);

    extra_width = plot->points[0].xhigh - plot->points[0].xlow;
    if (extra_width == 0)
	extra_width = (boxwidth > 0 && boxwidth_is_absolute) ? boxwidth : 0.5;
    if (extra_width < 0)
	extra_width = -extra_width;

    if (axis_array[plot->x_axis].autoscale & AUTOSCALE_MIN) {
	if (axis_array[plot->x_axis].min >= plot->points[0].x)
	    axis_array[plot->x_axis].min -= 1.5 * extra_width;
	else if (axis_array[plot->x_axis].min >= plot->points[0].x - extra_width)
	    axis_array[plot->x_axis].min -= 1 * extra_width;
    }
    if (axis_array[plot->x_axis].autoscale & AUTOSCALE_MAX) {
	double nfactors = GPMAX( 0, plot->boxplot_factors - 1 );
	double plot_max = plot->points[0].x + nfactors * boxplot_opts.separation;
	if (axis_array[plot->x_axis].max x_axis].max = plot_max + 1.5 * extra_width;
	else if (axis_array[plot->x_axis].max x_axis].max += extra_width;
    }

}

/* Since the stored x values for histogrammed data do not correspond exactly */
/* to the eventual x coordinates, we need to modify the x axis range bounds. */
/* Also the two stacked histogram modes need adjustment of the y axis bounds.*/
static void
histogram_range_fiddling(struct curve_points *plot)
{
    double xlow, xhigh;
    int i;
    /*
     * EAM FIXME - HT_STACKED_IN_TOWERS forcibly resets xmin, which is only
     *   correct if no other plot came first.
     */
    switch (histogram_opts.type) {
	case HT_STACKED_IN_LAYERS:
	    if (axis_array[plot->y_axis].autoscale & AUTOSCALE_MAX) {
		if (plot->histogram_sequence == 0) {
		    if (stackheight)
			free(stackheight);
		    stackheight = gp_alloc( plot->p_count * sizeof(struct coordinate),
					    "stackheight array");
		    for (stack_count=0; stack_count < plot->p_count; stack_count++) {
			stackheight[stack_count].yhigh = 0;
			stackheight[stack_count].ylow = 0;
		    }
		} else if (plot->p_count > stack_count) {
		    stackheight = gp_realloc( stackheight,
					    plot->p_count * sizeof(struct coordinate),
					    "stackheight array");
		    for ( ; stack_count < plot->p_count; stack_count++) {
			stackheight[stack_count].yhigh = 0;
			stackheight[stack_count].ylow = 0;
		    }
		}
		for (i=0; ipoints[i].type == UNDEFINED)
			continue;
		    if (plot->points[i].y >= 0)
			stackheight[i].yhigh += plot->points[i].y;
		    else
			stackheight[i].ylow += plot->points[i].y;

		    if (axis_array[plot->y_axis].max < stackheight[i].yhigh)
			axis_array[plot->y_axis].max = stackheight[i].yhigh;
		    if (axis_array[plot->y_axis].min > stackheight[i].ylow)
			axis_array[plot->y_axis].min = stackheight[i].ylow;

		}
	    }
		/* fall through to checks on x range */
	case HT_CLUSTERED:
	case HT_ERRORBARS:
		if (!axis_array[FIRST_X_AXIS].autoscale)
		    break;
		if (axis_array[FIRST_X_AXIS].autoscale & AUTOSCALE_MIN) {
		    xlow = plot->histogram->start - 1.0;
		    if (axis_array[FIRST_X_AXIS].min > xlow)
			axis_array[FIRST_X_AXIS].min = xlow;
		}
		if (axis_array[FIRST_X_AXIS].autoscale & AUTOSCALE_MAX) {
		    /* FIXME - why did we increment p_count on UNDEFINED points? */
		    while (plot->p_count > 0
			&& plot->points[plot->p_count-1].type == UNDEFINED) {
			plot->p_count--;
		    }
		    if (plot->p_count == 0)
			int_error(NO_CARET,"No valid points in histogram");
		    xhigh = plot->points[plot->p_count-1].x;
		    xhigh += plot->histogram->start + 1.0;
		    if (axis_array[FIRST_X_AXIS].max < xhigh)
			axis_array[FIRST_X_AXIS].max = xhigh;
		}
		break;
	case HT_STACKED_IN_TOWERS:
		/* FIXME: Rather than trying to reproduce the layout along X */
		/* we should just track the actual xmin/xmax as we go.       */
		if (axis_array[FIRST_X_AXIS].set_autoscale) {
		    if ((axis_array[FIRST_X_AXIS].set_autoscale & AUTOSCALE_MIN)) {
			xlow = -1.0;
			if (axis_array[FIRST_X_AXIS].min > xlow)
			    axis_array[FIRST_X_AXIS].min = xlow;
		    }
		    xhigh = plot->histogram_sequence;
		    xhigh += plot->histogram->start + 1.0;
		    if (axis_array[FIRST_X_AXIS].max != xhigh)
			axis_array[FIRST_X_AXIS].max  = xhigh;
		}
		if (axis_array[FIRST_Y_AXIS].set_autoscale) {
		    double ylow, yhigh;
		    for (i=0, yhigh=ylow=0.0; ip_count; i++)
			if (plot->points[i].type != UNDEFINED) {
			    if (plot->points[i].y >= 0)
				yhigh += plot->points[i].y;
			    else
				ylow += plot->points[i].y;
			}
		    if (axis_array[FIRST_Y_AXIS].set_autoscale & AUTOSCALE_MAX)
			if (axis_array[plot->y_axis].max < yhigh)
			    axis_array[plot->y_axis].max = yhigh;
		    if (axis_array[FIRST_Y_AXIS].set_autoscale & AUTOSCALE_MIN)
			if (axis_array[plot->y_axis].min > ylow)
			    axis_array[plot->y_axis].min = ylow;
		}
		break;
	default:
		break;
    }
}

/* If the plot is in polar coordinates and the r axis range is autoscaled,
 * we need to apply the maximum radius found to both x and y.
 * Otherwise the autoscaling will be done separately for x and y and the 
 * resulting plot will not be centered at the origin.
 */
void
polar_range_fiddling(struct axis *xaxis, struct axis *yaxis)
{
    if (axis_array[POLAR_AXIS].set_autoscale & AUTOSCALE_MAX) {
	double plotmax_x, plotmax_y, plotmax_r, plotmax;
	plotmax_x = GPMAX(xaxis->max, -xaxis->min);
	plotmax_y = GPMAX(yaxis->max, -yaxis->min);
	plotmax = GPMAX(plotmax_x, plotmax_y);

	plotmax_r = (axis_array[POLAR_AXIS].log)
		  ? axis_array[POLAR_AXIS].linked_to_primary->max
		  : axis_array[POLAR_AXIS].max;
	plotmax = GPMAX(plotmax, plotmax_r);

	if ((xaxis->set_autoscale & AUTOSCALE_BOTH) == AUTOSCALE_BOTH) {
	    xaxis->max = plotmax;
	    xaxis->min = -plotmax;
	}
	if ((yaxis->set_autoscale & AUTOSCALE_BOTH) == AUTOSCALE_BOTH) {
	    yaxis->max = plotmax;
	    yaxis->min = -plotmax;
	}
    }
}

/* Extend auto-scaling of y-axis to include zero */
static void
impulse_range_fiddling(struct curve_points *plot)
{
    if (axis_array[plot->y_axis].log)
	return;

    if (axis_array[plot->y_axis].autoscale & AUTOSCALE_MIN) {
	if (axis_array[plot->y_axis].min > 0)
	    axis_array[plot->y_axis].min = 0;
    }
    if (axis_array[plot->y_axis].autoscale & AUTOSCALE_MAX) {
	if (axis_array[plot->y_axis].max < 0)
	    axis_array[plot->y_axis].max = 0;
    }
}

/* Clean up x and y axis bounds for parallel plots */
static void
parallel_range_fiddling(struct curve_points *plot)
{
    int num_parallelplots = 0;

    while (plot) {
	if (plot->plot_style == PARALLELPLOT) {
	    double x = parallel_axis_array[plot->p_axis-1].paxis_x;
	    autoscale_one_point( (&axis_array[plot->x_axis]), x-1.0 );
	    autoscale_one_point( (&axis_array[plot->x_axis]), x+1.0 );
	    num_parallelplots++;
	}
	plot = plot->next;
    }

    /* The normal y axis is not used by parallel plots, so if no */
    /* range is established then we get lots of warning messages */
    if (num_parallelplots > 0) {
	if (axis_array[FIRST_Y_AXIS].min == VERYLARGE)
	    axis_array[FIRST_Y_AXIS].min = 0.0;
	if (axis_array[FIRST_Y_AXIS].max == -VERYLARGE)
	    axis_array[FIRST_Y_AXIS].max = 1.0;
    }
}

/* Clean up x and y axis bounds for spider plots */
static void
spiderplot_range_fiddling(struct curve_points *plot)
{
    while (plot) {
	if (plot->plot_style == SPIDERPLOT) {
	    /* The normal x and y axes are not used by spider plots, so if no */
	    /* range is established then we get lots of warning messages */
	    if (axis_array[plot->x_axis].autoscale & AUTOSCALE_MIN)
		axis_array[FIRST_X_AXIS].min = -1.0;
	    if (axis_array[plot->x_axis].autoscale & AUTOSCALE_MAX)
		axis_array[FIRST_X_AXIS].max =  1.0;
	    if (axis_array[plot->y_axis].autoscale & AUTOSCALE_MIN)
		axis_array[FIRST_Y_AXIS].min = -1.0;
	    if (axis_array[plot->y_axis].autoscale & AUTOSCALE_MAX)
		axis_array[FIRST_Y_AXIS].max =  1.0;
	    return;
	}
	plot = plot->next;
    }
}

/* store_label() is called by get_data for each point */
/* This routine is exported so it can be shared by plot3d */
struct text_label *
store_label(
    struct text_label *listhead,
    struct coordinate *cp,
    int i,                      /* point number */
    char *string,               /* start of label string */
    double colorval)            /* used if text color derived from palette */
{
    static struct text_label *tl = NULL;
    int textlen;

    if (!listhead)
	int_error(NO_CARET,"text_label list was not initialized");

    /* If listhead->next is NULL, the list is currently empty and we will */
    /* insert this label at the head.  Otherwise tl already points to the */
    /* tail (previous insertion) and we will add the new label there.     */
    if (listhead->next == NULL)
	tl = listhead;

    /* Allocate a new label structure and fill it in */
    tl->next = gp_alloc(sizeof(struct text_label),"labelpoint label");
    memcpy(tl->next,tl,sizeof(text_label));
    tl = tl->next;
    tl->next = (text_label *)NULL;
    tl->tag = i;
    tl->place.x = cp->x;
    tl->place.y = cp->y;
    tl->place.z = cp->z;

    /* optional variables from user spec */
    tl->rotate = cp->CRD_ROTATE;
    tl->lp_properties.p_type = cp->CRD_PTTYPE;
    tl->lp_properties.p_size = cp->CRD_PTSIZE;

    /* Check for optional (textcolor palette ...) */
    if (tl->textcolor.type == TC_Z)
	tl->textcolor.value = colorval;
    /* Check for optional (textcolor rgb variable) */
    else if (listhead->textcolor.type == TC_RGB && listhead->textcolor.value < 0)
	tl->textcolor.lt = colorval;
    /* Check for optional (textcolor variable) */
    else if (listhead->textcolor.type == TC_VARIABLE) {
	struct lp_style_type lptmp;
	if (prefer_line_styles)
	    lp_use_properties(&lptmp, (int)colorval);
	else
	    load_linetype(&lptmp, (int)colorval);
	tl->textcolor = lptmp.pm3d_color;
    }

    if ((listhead->lp_properties.flags & LP_SHOW_POINTS)) {
	/* Check for optional (point linecolor palette ...) */
	if (tl->lp_properties.pm3d_color.type == TC_Z)
	    tl->lp_properties.pm3d_color.value = colorval;
	/* Check for optional (point linecolor rgb variable) */
	else if (listhead->lp_properties.pm3d_color.type == TC_RGB 
		&& listhead->lp_properties.pm3d_color.value < 0)
	    tl->lp_properties.pm3d_color.lt = colorval;
	/* Check for optional (point linecolor variable) */
	else if (listhead->lp_properties.l_type == LT_COLORFROMCOLUMN) {
	    struct lp_style_type lptmp;
	    if (prefer_line_styles)
		lp_use_properties(&lptmp, (int)colorval);
	    else
		load_linetype(&lptmp, (int)colorval);
	    tl->lp_properties.pm3d_color = lptmp.pm3d_color;
	}
    }

    /* Check for null string (no label) */
    if (!string)
	string = "";

    textlen = 0;
    /* Handle quoted separators and quoted quotes */
    if (df_separators) {
	TBOOLEAN in_quote = FALSE;
	while (string[textlen]) {
	    if (string[textlen] == '"')
		in_quote = !in_quote;
	    else if (strchr(df_separators,string[textlen]) && !in_quote)
		break;
	    textlen++;
	}
	while (textlen > 0 && isspace((unsigned char)string[textlen-1]))
	    textlen--;
    } else {
    /* This is the normal case (no special separator character) */
	if (*string == '"') {
	    for (textlen=1; string[textlen] && string[textlen] != '"'; textlen++);
	}
	while (string[textlen] && !isspace((unsigned char)string[textlen]))
	    textlen++;
    }

    /* Strip double quote from both ends */
    if (string[0] == '"' && textlen > 1 && string[textlen-1] == '"')
	textlen -= 2, string++;

    tl->text = gp_alloc(textlen+1,"labelpoint text");
    strncpy( tl->text, string, textlen );
    tl->text[textlen] = '\0';
    parse_esc(tl->text);

    FPRINTF((stderr,"LABELPOINT %f %f \"%s\" \n", tl->place.x, tl->place.y, tl->text));
    FPRINTF((stderr,"           %g %g %g %g %g %g %g\n",
		cp->x, cp->y, cp->xlow, cp->xhigh, cp->ylow, cp->yhigh, cp->z));

    return tl;
}

/* HBB 20010610: mnemonic names for the bits stored in 'uses_axis' */
typedef enum e_uses_axis {
    USES_AXIS_FOR_DATA = 1,
    USES_AXIS_FOR_FUNC = 2
} t_uses_axis;

/*
 * This parses the plot command after any global 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.
 */
static void
eval_plots()
{
    int i;
    struct curve_points *this_plot = NULL;
    struct curve_points **tp_ptr;
    t_uses_axis uses_axis[AXIS_ARRAY_SIZE];
    TBOOLEAN some_functions = FALSE;
    TBOOLEAN some_tables = FALSE;
    int plot_num, line_num;
    TBOOLEAN was_definition = FALSE;
    int pattern_num;
    char *xtitle = NULL;
    int begin_token = c_token;  /* so we can rewind for second pass */
    int start_token=0, end_token;
    legend_key *key = &keyT;
    char orig_dummy_var[MAX_ID_LEN+1];

    int nbins = 0;
    double binlow = 0, binhigh = 0, binwidth = 0;
    int binopt = 0;

    /* Histogram bookkeeping */
    double newhist_start = 0.0;
    int histogram_sequence = -1;
    int newhist_color = 1;
    int newhist_pattern = LT_UNDEFINED;
    histogram_rightmost = 0.0;
    free_histlist(&histogram_opts);
    init_histogram(NULL,NULL);

    /* Parallel plot bookkeeping */
    paxis_start = -1;
    paxis_end = -1;
    paxis_current = -1;

    /* Watch condition bookkeeping */
    reset_watches();

    uses_axis[FIRST_X_AXIS] =
	uses_axis[FIRST_Y_AXIS] =
	uses_axis[SECOND_X_AXIS] =
	uses_axis[SECOND_Y_AXIS] = 0;

    /* Original Comment follows: */
    /* 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 valid requires some rewriting */
    /* EAM Apr 2007 - but we need to keep the previous structures around in 
     * order to be able to refresh/zoom them without re-reading all the data.
     */
    if (first_plot)
	cp_free(first_plot);
    first_plot = NULL;

    tp_ptr = &(first_plot);
    plot_num = 0;
    line_num = 0;               /* default line type */
    pattern_num = default_fillstyle.fillpattern;        /* default fill pattern */
    strcpy(orig_dummy_var, c_dummy_var[0]);
    in_parametric = FALSE;
    xtitle = NULL;

    /* Assume that the input data can be re-read later */
    volatile_data = FALSE;

    /* Track complex values so that we can warn about trying to plot them */
    n_complex_values = 0;

    /* No mask active */
    construct_2D_mask_set(NULL, 0);

    /* ** 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.
     */
    plot_iterator = check_for_iteration();
    while (TRUE) {

	/* Forgive trailing comma on a multi-element plot command */
	if (END_OF_COMMAND) {
	    if (plot_num == 0)
		int_error(c_token, "function to plot expected");
	    break;
	}

	this_plot = NULL;
	if (!in_parametric && !was_definition)
	    start_token = c_token;

	if (almost_equals(c_token,"newhist$ogram")) {
	    struct lp_style_type lp = DEFAULT_LP_STYLE_TYPE;
	    struct fill_style_type fs;
	    int previous_token;
	    c_token++;
	    histogram_sequence = -1;
	    memset(&histogram_title, 0, sizeof(text_label));

	    if (histogram_rightmost > 0)
		newhist_start = histogram_rightmost + 2;

	    lp.l_type = line_num;
	    newhist_color = lp.l_type + 1;
	    fs.fillpattern = LT_UNDEFINED;

	    do {
		previous_token = c_token;

		if (equals(c_token,"at")) {
		    c_token++;
		    newhist_start = real_expression();
		}

		/* Store title in temporary variable and then copy into the */
		/* new histogram structure when it is allocated.            */
		if (!histogram_title.text && isstringvalue(c_token)) {
		    histogram_title.textcolor = histogram_opts.title.textcolor;
		    histogram_title.boxed = histogram_opts.title.boxed;
		    histogram_title.pos = histogram_opts.title.pos;
		    histogram_title.text = try_to_get_string();
		    histogram_title.font = gp_strdup(histogram_opts.title.font);
		    parse_label_options(&histogram_title, 2);
		}

		/* Allow explicit starting color or pattern for this histogram */
		if (equals(c_token,"lt") || almost_equals(c_token,"linet$ype")) {
		    c_token++;
		    newhist_color = int_expression();
		}
		fs.fillstyle = FS_SOLID;
		fs.filldensity = 100;
		fs.border_color = default_fillstyle.border_color;
		parse_fillstyle(&fs);

	    } while (c_token != previous_token);

	    newhist_pattern = fs.fillpattern;
	    if (!equals(c_token,","))
		int_error(c_token,"syntax error");
	    was_definition = FALSE;

	} else if (almost_equals(c_token, "newspider$plot")) {
	    c_token++;
	    paxis_current = 0;
	    if (!equals(c_token,","))
		int_error(c_token,"syntax error (missing comma)");
	    was_definition = FALSE;

	} else if (is_definition(c_token)) {
	    define();
	    if (equals(c_token,","))
		c_token++;
	    was_definition = TRUE;
	    continue;

	} else {
	    int specs = 0;

	    /* for datafile plot, record datafile spec for title */
	    char* name_str;

	    TBOOLEAN duplication = FALSE;
	    TBOOLEAN set_smooth = FALSE, set_axes = FALSE, set_title = FALSE;
	    TBOOLEAN set_with = FALSE, set_lpstyle = FALSE;
	    TBOOLEAN set_fillstyle = FALSE;
	    TBOOLEAN set_fillcolor = FALSE;
	    TBOOLEAN set_labelstyle = FALSE;
	    TBOOLEAN set_ellipseaxes_units = FALSE;
	    double paxis_x = -VERYLARGE;
	    t_colorspec fillcolor = DEFAULT_COLORSPEC;

	    /* CHANGE: Aug 2017
	     * Allow sampling both u and v so that it is possible to do
	     * plot sample [u=min:max:inc] [v=min:max:inc] '++' ... with image
	     */
	    t_value original_value_sample_var, original_value_sample_var2;
	    int sample_range_token, v_range_token;

	    plot_num++;

	    /* Check for a sampling range. */
	    init_sample_range(axis_array + FIRST_X_AXIS, DATA);
	    sample_range_token = parse_range(SAMPLE_AXIS);
	    v_range_token = 0;
	    if (sample_range_token != 0) {
		axis_array[SAMPLE_AXIS].range_flags |= RANGE_SAMPLED;
		/* If the sample was specifically on u we need to check v also */
		if (equals(sample_range_token, "u")) {
		    axis_array[U_AXIS].min = axis_array[SAMPLE_AXIS].min;
		    axis_array[U_AXIS].max = axis_array[SAMPLE_AXIS].max;
		    axis_array[U_AXIS].autoscale = axis_array[SAMPLE_AXIS].autoscale;
		    axis_array[U_AXIS].SAMPLE_INTERVAL = axis_array[SAMPLE_AXIS].SAMPLE_INTERVAL;
		    axis_array[U_AXIS].range_flags = axis_array[SAMPLE_AXIS].range_flags;
		    v_range_token = parse_range(V_AXIS);
		    if (v_range_token != 0)
			axis_array[V_AXIS].range_flags |= RANGE_SAMPLED;
		}
	    }

	    was_definition = FALSE;

	    /* Allow replacement of the dummy variable in a function */
	    if (sample_range_token > 0)
		copy_str(c_dummy_var[0], sample_range_token, MAX_ID_LEN);
	    else if (sample_range_token < 0)
		strcpy(c_dummy_var[0], set_dummy_var[0]);
	    else
		strcpy(c_dummy_var[0], orig_dummy_var);

	    /* If string_or_express finds a function, it will construct an
	     * action table for this plot.  Later we will store dummy variable
	     * values for it prior to function evaluation.
	     */
	    dummy_func = &(this_plot->plot_function);
	    name_str = string_or_express(NULL);
	    dummy_func = NULL;

	    if (name_str) { /* data file to plot */
		if (parametric && in_parametric)
		    int_error(c_token, "previous parametric function not fully specified");
		if (sample_range_token !=0 && *name_str != '+')
		    int_warn(sample_range_token, "Ignoring sample range in non-sampled data plot");
		if (*name_str == '$' && !get_datablock(name_str))
		    int_error(c_token-1, "cannot plot voxel data");

		if (*tp_ptr) {
		    this_plot = *tp_ptr;
		    cp_extend(this_plot, MIN_CRV_POINTS);
		} else {
		    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 = SMOOTH_NONE;
		this_plot->plot_filter = FILTER_NONE;
		this_plot->filledcurves_options = filledcurves_opts_data;

		/* Only relevant to "with table" */
		free_at(table_filter_at);
		table_filter_at = NULL;

		/* Mechanism for deferred evaluation of plot title */
		free_at(df_plot_title_at);

		/* up to MAXDATACOLS cols */
		df_set_plot_mode(MODE_PLOT);    /* Needed for binary datafiles */
		specs = df_open(name_str, MAXDATACOLS, this_plot);

		/* Store a pointer to the named variable used for sampling */
		if (sample_range_token > 0)
		    this_plot->sample_var = add_udv(sample_range_token);
		else
		    this_plot->sample_var = add_udv_by_name(c_dummy_var[0]);
		if (v_range_token > 0)
		    this_plot->sample_var2 = add_udv(v_range_token);
		else
		    this_plot->sample_var2 = add_udv_by_name(c_dummy_var[1]);

		if (this_plot->sample_var->udv_value.type == ARRAY)
		    int_error(NO_CARET, "name conflict: dummy variable is an array");

		/* Save prior value of sample variables so we can restore them later */
		original_value_sample_var = this_plot->sample_var->udv_value;
		original_value_sample_var2 = this_plot->sample_var2->udv_value;
		this_plot->sample_var->udv_value.type = NOTDEFINED;
		this_plot->sample_var2->udv_value.type = NOTDEFINED;

		/* Not sure this is necessary */
		Gcomplex(&(this_plot->sample_var->udv_value), 0.0, 0.0);

		/* include modifiers in default title */
		this_plot->token = end_token = c_token - 1;

	    } else if (equals(c_token, "keyentry")) {
		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 = KEYENTRY;
		this_plot->plot_style = LABELPOINTS;
		this_plot->token = end_token = c_token - 1;

	    } else { /* function to plot */

		some_functions = TRUE;
		if (parametric) /* working on x parametric function */
		    in_parametric = !in_parametric;
		if (spiderplot)
		    int_error(NO_CARET, "spiderplot is not possible for functions");
		if (*tp_ptr) {
		    this_plot = *tp_ptr;
		    cp_extend(this_plot, samples_1 + 1);
		} else {        /* no memory malloc()'d there yet */
		    this_plot = cp_alloc(samples_1 + 1);
		    *tp_ptr = this_plot;
		}
		this_plot->plot_type = FUNC;
		this_plot->plot_style = func_style;
		this_plot->filledcurves_options = filledcurves_opts_func;
		end_token = c_token - 1;
	    }                   /* end of IS THIS A FILE OR A FUNC block */

	    /* axis defaults */
	    x_axis = FIRST_X_AXIS;
	    y_axis = FIRST_Y_AXIS;

	    /*  Set this before parsing any modifying options */
	    this_plot->base_linetype = line_num;

	    /* pm 25.11.2001 allow any order of options */
	    while (!END_OF_COMMAND) {
		int save_token = c_token;

		/* Previous keyword was problematic */
		if (duplication)
		    break;

		/* bin the data if requested */
		if (equals(c_token, "bins")) {
		    if (set_smooth) {
			duplication = TRUE;
			break;
		    }
		    c_token++;
		    this_plot->plot_filter = FILTER_BINS;
		    nbins = samples_1;
		    if (equals(c_token, "=")) {
			c_token++;
			nbins = int_expression();
			if (nbins 

Web Proxy Viewer  |  New URL  |  Original Page