From 71922741dffdf34d6230d95be26ffa7e7405d300 Mon Sep 17 00:00:00 2001 From: Ethan A Merritt Date: Mon, 22 Jul 2024 13:53:57 -0700 Subject: [PATCH 01/40] contours: allow contour to follow edge of z-clipped surface Since gnuplot 4.0, contour lines were not generated for surface tiles that were clipped against z because this could result in contour lines outside the surface tiling. See commmit 6e6c67e9 Now that we can do smooth clipping against z it makes more sense to generate the full bounding contour line. Maybe the previous treatment is still a better match for "set pm3d clip1in" or "set pm3d clip4in" but IMHO those modes are rarely desirable. The change is visible in demos contours.dem (parametric mode plot "contour of data grid plotting", not pm3d), vector.demi (no surface), and contourfill.dem (pm3d). --- src/contour.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/contour.c b/src/contour.c index 68740c66b..d68f3b48b 100644 --- a/src/contour.c +++ b/src/contour.c @@ -786,12 +786,13 @@ add_edge( { edge_struct *pe_temp = NULL; -#if 1 - if (point0->type == INRANGE && point1->type == INRANGE) -#else - if (point0->type != UNDEFINED && point1->type != UNDEFINED) -#endif - { + /* version 6: now that we can do smooth clipping on z, + * outrange points are not such a problem. + * Previously the test was + * if (point0->type == INRANGE && point1->type == INRANGE) + * If necessary, the test could be contingent on (pm3d.clip == PM3D_CLIP_Z) + */ + if (point0->type != UNDEFINED && point1->type != UNDEFINED) { pe_temp = gp_alloc(sizeof(edge_struct), "contour edge"); pe_temp->poly[0] = NULL; /* clear links */ @@ -807,7 +808,6 @@ add_edge( (*p_edge) = pe_temp; /* start new list if empty */ } (*pe_tail) = pe_temp; /* continue to last record. */ - } return pe_temp; /* returns NULL, if no edge allocated */ } From 1ffd58257337fbdfd6d64fa1f7e9c7086fdec5d0 Mon Sep 17 00:00:00 2001 From: Ethan A Merritt Date: Thu, 18 Jul 2024 22:22:54 -0700 Subject: [PATCH 02/40] move action table pointer for "plot with table if ()" to plot header set table plot FOO using ... if () This feature (marked EXPERIMENTAL) has been using a global action table. Move this into the plot header for both 2D and 3D in preparation for extending the "plot .. if" syntax to plot styles other than "with table". --- src/graph3d.h | 5 +++-- src/graphics.h | 5 +++-- src/plot2d.c | 11 ++++------- src/plot3d.c | 1 + src/tabulate.c | 7 +++---- src/tabulate.h | 3 +-- 6 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/graph3d.h b/src/graph3d.h index 93faf60cf..c4a7fd837 100644 --- a/src/graph3d.h +++ b/src/graph3d.h @@ -104,8 +104,9 @@ typedef struct surface_points { struct udvt_entry *sample_var; /* used by '+' if plot has private sampling range */ struct udvt_entry *sample_var2; /* used by '++' if plot has private sampling range */ struct udft_entry plot_function; /* action table and dummy variables for function plot */ - enum PLOT_FILTER plot_filter; /* currently only "mask" */ - enum PLOT_SMOOTH plot_smooth; /* smooth lines in 3D */ + enum PLOT_FILTER plot_filter; /* currently only "mask" */ + enum PLOT_SMOOTH plot_smooth; /* smooth lines in 3D */ + struct at_type *if_filter_at; /* plot ... if () */ /* 2D and 3D plot structure fields overlay only to this point */ diff --git a/src/graphics.h b/src/graphics.h index bb7f637d9..4eacce0a8 100644 --- a/src/graphics.h +++ b/src/graphics.h @@ -64,8 +64,9 @@ typedef struct curve_points { struct udvt_entry *sample_var; /* used by '+' if plot has private sampling range */ struct udvt_entry *sample_var2; /* used by '++'if plot has private sampling range */ struct udft_entry plot_function; /* action table and dummy variables for function plot */ - enum PLOT_FILTER plot_filter; /* which filter was specified? */ - enum PLOT_SMOOTH plot_smooth; /* which "smooth" method to be used? */ + enum PLOT_FILTER plot_filter; /* which filter was specified? */ + enum PLOT_SMOOTH plot_smooth; /* which "smooth" method to be used? */ + struct at_type *if_filter_at; /* plot ... if () */ /* 2D and 3D plot structure fields overlay only to this point */ diff --git a/src/plot2d.c b/src/plot2d.c index 6eb17a759..9b82315c9 100644 --- a/src/plot2d.c +++ b/src/plot2d.c @@ -201,6 +201,7 @@ cp_free(struct curve_points *cp) free_labels(cp->labels); cp->labels = NULL; free_at(cp->plot_function.at); + free_at(cp->if_filter_at); free_watchlist(cp->watchlist); cp->watchlist = NULL; @@ -812,7 +813,7 @@ get_data(struct curve_points *current_plot) /* "plot ... with table" bypasses all the column interpretation */ if (current_plot->plot_style == TABLESTYLE) { - tabulate_one_line(v, df_strings, j); + tabulate_one_line(current_plot, v, df_strings, j); continue; } @@ -2458,10 +2459,6 @@ eval_plots() this_plot->filledcurves_options = filledcurves_opts_data; this_plot->marks_options = (struct marks_options) DEFAULT_MARKS_OPTS; - /* 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); @@ -2806,12 +2803,12 @@ eval_plots() if (this_plot->plot_style == TABLESTYLE) { if (equals(c_token,"if")) { - if (table_filter_at) { + if (this_plot->if_filter_at) { duplication = TRUE; break; } c_token++; - table_filter_at = perm_at(); + this_plot->if_filter_at = perm_at(); continue; } } diff --git a/src/plot3d.c b/src/plot3d.c index c87dbe7d4..1e93a7785 100644 --- a/src/plot3d.c +++ b/src/plot3d.c @@ -232,6 +232,7 @@ sp_free(struct surface_points *sp) } free_at(sp->plot_function.at); + free_at(sp->if_filter_at); free(sp->zclip); free(sp); diff --git a/src/tabulate.c b/src/tabulate.c index 83470e978..3a3d937a8 100644 --- a/src/tabulate.c +++ b/src/tabulate.c @@ -59,7 +59,6 @@ FILE *table_outfile = NULL; udvt_entry *table_var = NULL; TBOOLEAN table_mode = FALSE; char *table_sep = NULL; -struct at_type *table_filter_at = NULL; static char *expand_newline(const char *in); static TBOOLEAN blanks_needed(curve_points *this_plot); @@ -603,15 +602,15 @@ blanks_needed(curve_points *this_plot) * Called from plot2d.c (get_data) for "plot with table" */ TBOOLEAN -tabulate_one_line(double v[], struct value str[], int ncols) +tabulate_one_line(struct curve_points *plot, double v[], struct value str[], int ncols) { int col; FILE *outfile = (table_outfile) ? table_outfile : gpoutfile; struct value keep; - if (table_filter_at) { + if (plot->if_filter_at) { evaluate_inside_using = TRUE; - evaluate_at(table_filter_at, &keep); + evaluate_at(plot->if_filter_at, &keep); evaluate_inside_using = FALSE; if (undefined || isnan(real(&keep)) || real(&keep) == 0) return FALSE; diff --git a/src/tabulate.h b/src/tabulate.h index 7adfd323d..ecee51bcf 100644 --- a/src/tabulate.h +++ b/src/tabulate.h @@ -9,12 +9,11 @@ void print_table(struct curve_points * first_plot, int plot_num); void print_3dtable(int pcount); -TBOOLEAN tabulate_one_line(double v[], struct value str[], int ncols); +TBOOLEAN tabulate_one_line(struct curve_points *plot, double v[], struct value str[], int ncols); extern FILE *table_outfile; extern udvt_entry *table_var; extern TBOOLEAN table_mode; extern char *table_sep; -extern struct at_type *table_filter_at; #endif /* GNUPLOT_TABULATE_H */ From c26256999890fdfcbc2f91431a8117b461802957 Mon Sep 17 00:00:00 2001 From: Ethan A Merritt Date: Fri, 19 Jul 2024 09:20:42 -0700 Subject: [PATCH 03/40] "plot ... if ()" Move the check for an if condition out of table-specific code into the main parsing loop for "plot" commands. The "if" restriction is accepted only for data plots. Input lines that satisfy the if condition are processed as usual. Lines that fail are essentially ignored, equivalent to matching the "set datafile missing" condition". TODO: It might be useful in some cases to have the rejects treated as NaN/ undefined rather than missing. Perhaps this could be indicated by the filter expression returning NaN rather than 0 or 1. It's not obvious how to do this while preserving the actual input content however. --- src/plot2d.c | 45 ++++++++++++++++++++++++++++----------------- src/tabulate.c | 9 --------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/plot2d.c b/src/plot2d.c index 9b82315c9..1074d20a0 100644 --- a/src/plot2d.c +++ b/src/plot2d.c @@ -757,10 +757,6 @@ get_data(struct curve_points *current_plot) current_plot->points[i].type = UNDEFINED; i++; } - if (current_plot->plot_style == TABLESTYLE) { - j = df_no_use_specs; - break; - } continue; case DF_FIRST_BLANK: @@ -808,7 +804,21 @@ get_data(struct curve_points *current_plot) break; /* Not continue!! */ } - /* We now know that j > 0, i.e. there is some data on this input line */ + /* We now know that j > 0, i.e. there is some data on this input line. + * However it is still possible that the user wants to skip this line + * because it fails to satisfy the "plot .. if ()" condition. + * FIXME: This treats filtered points as if they were missing; + * alternatively we could keep them but mark as undefined. + */ + if (current_plot->if_filter_at) { + struct value keep; + evaluate_inside_using = TRUE; + evaluate_at(current_plot->if_filter_at, &keep); + evaluate_inside_using = FALSE; + if (undefined || isnan(real(&keep)) || real(&keep) == 0) + continue; + } + ngood++; /* "plot ... with table" bypasses all the column interpretation */ @@ -2801,18 +2811,6 @@ eval_plots() continue; } - if (this_plot->plot_style == TABLESTYLE) { - if (equals(c_token,"if")) { - if (this_plot->if_filter_at) { - duplication = TRUE; - break; - } - c_token++; - this_plot->if_filter_at = perm_at(); - continue; - } - } - /* pick up line/point specs and other style-specific keywords * - point spec allowed if style uses points, ie style&2 != 0 * - keywords for lt and pt are optional @@ -3019,6 +3017,19 @@ eval_plots() } #endif + /* EXPERIMENTAL filter plot ... if () */ + if (equals(c_token,"if")) { + if (this_plot->plot_type != DATA) + int_error(c_token, "'if' restriction not possible here"); + if (this_plot->if_filter_at) { + duplication = TRUE; + break; + } + c_token++; + this_plot->if_filter_at = perm_at(); + continue; + } + break; /* unknown option */ } /* while (!END_OF_COMMAND) */ diff --git a/src/tabulate.c b/src/tabulate.c index 3a3d937a8..c7f01c45a 100644 --- a/src/tabulate.c +++ b/src/tabulate.c @@ -606,15 +606,6 @@ tabulate_one_line(struct curve_points *plot, double v[], struct value str[], int { int col; FILE *outfile = (table_outfile) ? table_outfile : gpoutfile; - struct value keep; - - if (plot->if_filter_at) { - evaluate_inside_using = TRUE; - evaluate_at(plot->if_filter_at, &keep); - evaluate_inside_using = FALSE; - if (undefined || isnan(real(&keep)) || real(&keep) == 0) - return FALSE; - } if (table_var == NULL) { char sep = (table_sep && *table_sep) ? *table_sep : '\t'; From 06c2af67a23cf63b92f168f799d44c2a04b9ac01 Mon Sep 17 00:00:00 2001 From: Ethan A Merritt Date: Fri, 19 Jul 2024 12:10:46 -0700 Subject: [PATCH 04/40] "splot ... if (type = INRANGE; /* unless we find out different */ - /* EAM Oct 2004 - Substantially rework this section */ - /* now that there are many more plot types. */ + /* EXPERIMENTAL "splot .. if ()" */ + if (this_plot->if_filter_at) { + struct value keep; + evaluate_inside_using = TRUE; + evaluate_at(this_plot->if_filter_at, &keep); + evaluate_inside_using = FALSE; + if (undefined || isnan(real(&keep)) || real(&keep) == 0) { + cp->type = UNDEFINED; + goto come_here_if_undefined; + } + } x = y = z = 0.0; xtail = ytail = ztail = 0.0; + cp->type = INRANGE; /* unless we find out otherwise */ /* The x, y, z coordinates depend on the mapping type */ switch (mapping3d) { @@ -2180,6 +2189,19 @@ eval_3dplots() continue; } + /* EXPERIMENTAL filter splot ... if () */ + if (equals(c_token,"if")) { + if (this_plot->plot_type != DATA3D) + int_error(c_token, "'if' restriction not possible here"); + if (this_plot->if_filter_at) { + duplication = TRUE; + break; + } + c_token++; + this_plot->if_filter_at = perm_at(); + continue; + } + break; /* unknown option */ } /* while (!END_OF_COMMAND)*/ From de4dff37dc9a73c433ed6a545b5ac33637ad0de8 Mon Sep 17 00:00:00 2001 From: Ethan A Merritt Date: Fri, 19 Jul 2024 13:27:12 -0700 Subject: [PATCH 05/40] document filter "if ()" for plot and splot --- docs/gnuplot.doc | 80 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 59 insertions(+), 21 deletions(-) diff --git a/docs/gnuplot.doc b/docs/gnuplot.doc index 4776ce89a..b15e25ee4 100644 --- a/docs/gnuplot.doc +++ b/docs/gnuplot.doc @@ -540,6 +540,8 @@ D epi_data 1 #b The position of the key box can be manually tweaked by specifying an ## offset to be added to whatever position the program would otherwise use. ## See `set key offset`. +#b `plot .. if ()` New filter for plot and splot commands to select +## only those input lines that match a target expression. See `filters if`. #end 3 Brief summary of features introduced in version 5 @@ -8352,31 +8354,36 @@ Ffigure_zerror {bins } {mask} {volatile} {zsort} {noautoscale} + {if ()} The modifiers `binary`, `index`, `every`, `skip`, `using`, `smooth`, `bins`, - `mask`, `convexhull`, `concavehull`, and `zsort` are discussed separately. - In brief + `mask`, `convexhull`, `concavehull`, `zsort`, and `if` are discussed + separately. + In brief: #start #b `skip N` tells the program to ignore N lines at the start of the input file #b `binary` indicates that the file contains binary data rather than text -#b `index` selects which data sets in a multi-data-set file are to be plotted +#b `bins` sorts individual input points into equal-sized intervals along x and +## plots a single accumulated value per interval #b `every` specifies which points within a single data set are to be plotted -#b `using` specifies which columns in the file are to be used in which order +#b `if ()` filters the input data to accept only lines that +## satisfy a certain condition. +#b `index` selects which data sets in a multi-data-set file are to be plotted #b `smooth` performs simple filtering, interpolation, or curve-fitting of the ## data prior to plotting -#b `convexhull` either alone or in combination with `smooth` replaces the -## points in the input data set with a new set of points that constitute the -## vertices of a bounding polygon. -#b `bins` sorts individual input points into equal-sized intervals along x and -## plots a single accumulated value per interval +#b `convexhull` and `concavehull`, either alone or in combination with `smooth`, +## replaces the points in the input data set with a new set of points that +## constitute the vertices of a bounding polygon. #b `mask` filters the data through a previously defined mask to plot only a ## selected subset of pixels in an image or a selected region of a pm3d surface. +#b `using` specifies which columns in the file are to be used in which order #b `volatile` indicates that the content of the file may not be available to ## reread later and therefore it should be retained internally for re-use. +#b `zsort` sorts each block of input data on z #end - `splot` has a similar syntax but does not support `bins` and supports only a - few `smooth` options. + `splot` has a similar syntax but supports only some of the filter and smoothing + options. The `noautoscale` keyword means that the points making up this plot will be ignored when automatically determining axis range limits. @@ -8583,7 +8590,7 @@ Ffigure_zerror In general the purpose of a filter is to replace the original full set of input points with a selected subset of points, possibly modified, regrouped, or reordered, - The filters currently supported are `bins`, `convexhull`, `concavehull`, + The filters currently supported are `bins`, `convexhull`, `concavehull`, `if`, `mask`, `sharpen`, and `zsort`. 5 bins ?commands plot datafile filters bins @@ -8764,6 +8771,41 @@ Ffigure_concave_hull_2 the function with periodic sharp minima that reach y=0. D sharpen 1 +5 if +?commands plot datafile filters if +?plot datafile filters if +?plot filters if +?data-file if +?datafile if +?filters if + plot ... if () + splot ... if () + For each line of input data the expression in the `if` filter is evaluated + and the line is accepted only if the resulting value is true (non-zero). + Any function or variable that would be valid inside a `using` specifier is + also valid in the filter expression, including data columns that are not + otherwise used for plotting. + + Data lines for which the expression evaluates to false (zero) are treated + as if they were not present in the file (see `missing`). This provides a + more readable equivalent to the previously supported method that inserted + a logical test inside one of the `using` specifiers. + + Old syntax (still accepted): + set datafile missing NaN + plot FOO using (strcol(1) eq "ABC" ? $2 : NaN):3 with linespoints + plot $DATA using 1:($2 < 999. ? $2 : NaN) + New syntax: + plot FOO using 2:3 with linespoints if (strcol(1) eq "ABC") + plot $DATA using 1:2 if ($2 < 999.) + Both the old and new examples select only the lines in input file FOO + that have `ABC` in the first column, and only lines in $DATA where the + value in column 2 is less than 999. + + This filter is also accepted for splot commands, with the slight difference + that when the filter expression returns 0 (false) the point is treated as + undefined rather than missing. + 5 zsort ?commands plot datafile filters zsort ?plot datafile filters zsort @@ -16357,11 +16399,8 @@ Ffigure_polar_grid set table "tab.csv" separator comma plot using 1:2:3:4 with table - [EXPERIMENTAL] To select only a subset of the data points for tabulation you - can provide an input filter condition (`if `) at the end of the - command. Note that the input filter may reference data columns that are not - part of the output. This feature may change substantially before appearing in - a released version of gnuplot. + To select only a subset of the data points for tabulation you can provide an + input filter condition (`if `) as part of the plot command. plot using 1:2:($4+$5) with table if (strcol(3) eq "Red") plot using 1:2:($4+$5) with table if (10. < $1 && $1 < 100.) @@ -18222,11 +18261,10 @@ Ffigure_walls `Splot`, like `plot`, can display from a file. Syntax: - splot '' {binary } - {{nonuniform|sparse} matrix} - {index } - {every } + splot '' {binary } {{nonuniform|sparse} matrix} + {index } {every } {using } + {smooth