/* 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