diff --git a/Changes b/Changes index ad0a948..165dc12 100644 --- a/Changes +++ b/Changes @@ -1,3 +1,23 @@ +feedgnuplot (1.48) + + * --vnlog works properly with --domain + + -- Dima Kogan Sat, 24 Feb 2018 12:33:50 -0800 + +feedgnuplot (1.47) + + * Fixed typo. Everything is un-broken now + + -- Dima Kogan Fri, 23 Feb 2018 10:21:13 -0800 + +feedgnuplot (1.46) + + * Added --tuplesize and --tuplesizeall as alternatives to --rangesize + and --rangesizeall. Both forms are supported. + * Vnlog integration + + -- Dima Kogan Thu, 22 Feb 2018 23:37:54 -0800 + feedgnuplot (1.45) * zsh completion: --hardcopy, --image suggest filenames diff --git a/bin/feedgnuplot b/bin/feedgnuplot index 85baefe..f9c6570 100755 --- a/bin/feedgnuplot +++ b/bin/feedgnuplot @@ -16,7 +16,7 @@ use Pod::Usage; use Time::Piece; # Makefile.PL assumes this is in '' -my $VERSION = '1.45'; +my $VERSION = '1.48'; my %options; interpretCommandline(); @@ -102,8 +102,9 @@ sub interpretCommandline $options{with} = ''; $options{rangesize} = []; + $options{tuplesize} = []; - GetOptions(\%options, 'stream:s', 'domain!', 'dataid!', '3d!', 'colormap!', 'lines!', 'points!', + GetOptions(\%options, 'stream:s', 'domain!', 'dataid!', 'vnlog!', '3d!', 'colormap!', 'lines!', 'points!', 'circles', 'legend=s{2}', 'autolegend!', 'xlabel=s', 'ylabel=s', 'y2label=s', 'zlabel=s', 'title=s', 'xlen=f', 'ymin=f', 'ymax=f', 'xmin=s', 'xmax=s', 'y2min=f', 'y2max=f', 'zmin=f', 'zmax=f', 'y2=s@', @@ -113,7 +114,9 @@ sub interpretCommandline 'image=s', 'histogram=s@', 'binwidth=f', 'histstyle=s', 'terminal=s', - 'rangesize=s{2}', 'rangesizeall=i', 'extraValuesPerPoint=i', + 'rangesize=s{2}', 'rangesizeall=i', + 'tuplesize=s{2}', 'tuplesizeall=i', + 'extraValuesPerPoint=i', # deprecated and undocumented 'help', 'dump', 'exit', 'version', 'geometry=s') or exit 1; @@ -154,6 +157,11 @@ sub interpretCommandline delete $options{with}; } + if( $options{dataid} && $options{vnlog} ) + { + print STDERR "--dataid and --vnlog are mutually exclusive. Please just use one.\n"; + exit -1; + } # expand options that are given as comma-separated lists for my $listkey (qw(histogram y2)) @@ -161,7 +169,7 @@ sub interpretCommandline @{$options{$listkey}} = map split('\s*,\s*', $_), @{$options{$listkey}} if defined $options{$listkey}; } - for my $listkey (qw(curvestyle rangesize)) + for my $listkey (qw(curvestyle rangesize tuplesize)) { next unless defined $options{$listkey}; my @in = @{$options{$listkey}}; @@ -181,6 +189,35 @@ sub interpretCommandline } + # convert all tuplesize business to rangesize + my $domainsize = $options{'3d'} ? 2 : 1; + if (defined $options{tuplesizeall}) + { + if (defined $options{rangesizeall} ) + { + print STDERR "Only one of --rangesizeall and --tuplesizeall may be given\n"; + exit -1; + } + + $options{rangesizeall} = $options{tuplesizeall} - $domainsize; + delete $options{tuplesizeall}; + } + if (defined $options{tuplesize}) + { + $options{rangesize} //= []; + + my $N = @{$options{tuplesize}} / 2; + for my $i (0..$N-1) + { + $options{tuplesize}[2*$i + 1] -= $domainsize; + } + + push @{$options{rangesize}}, @{$options{tuplesize}}; + delete $options{tuplesize}; + } + + + # If we're plotting histograms, then set the default histogram options for # each histogram curve # @@ -756,6 +793,41 @@ sub mainThread # The domain of the current point my @domain; + # column headers from vnlog + my @vnlog_headers; + if($options{vnlog}) + { + require Vnlog::Parser; + require Vnlog::Util; + + if ( !defined $pipe_in ) + { + ($pipe_in, $selector) = openNextFile(); + } + + my $parser = Vnlog::Parser->new(); + while (defined ($_ = Vnlog::Util::get_unbuffered_line($pipe_in))) + { + if ( !$parser->parse($_) ) + { + die "Error parsing vnlog: $parser->{error}; looking at line '$_'"; + } + + my $keys = $parser->getKeys(); + if (defined $keys) + { + @vnlog_headers = @$keys; + last; + } + } + if(!@vnlog_headers) + { + die "Looked through all of the first file, and never saw a vnlog legend"; + } + } + + + # The x-axis domain represented as a number. This is exactly the same as # $domain[0] unless the x-axis domain uses a timefmt. Then this is the # number of seconds since the UNIX epoch. @@ -793,6 +865,7 @@ sub mainThread # 3d plots require $options{domain}, and dictate "x y" for the domain instead of just "x" my @fields = split; + my $i_column = 0; if($options{domain}) { @@ -804,6 +877,7 @@ sub mainThread $domain[0] = join (' ', splice( @fields, 0, $options{timefmt_Ncols}) ); $domain0_numeric = makeDomainNumeric( $domain[0] ); + $i_column += $options{timefmt_Ncols}; } elsif(!$options{'3d'}) { @@ -812,6 +886,7 @@ sub mainThread next if @fields < 1+1; $domain[0] = $domain0_numeric = shift @fields; + $i_column += 1; } else { @@ -820,6 +895,7 @@ sub mainThread next if @fields < 2+1; @domain = splice(@fields, 0, 2); + $i_column += 2; } if( $options{monotonic} ) @@ -844,11 +920,27 @@ sub mainThread } my $id = -1; - while(@fields) { - if($options{dataid}) { $id = shift @fields; } - else { $id++; } + if ($options{dataid}) + { + $id = shift @fields; + } + elsif($options{vnlog} ) + { + if( $i_column >= @vnlog_headers ) + { + # Got more columns than vnlog headers. The data is probably + # bogus, but I don't want to barf at the user, so I silently + # ignore the data + last; + } + $id = $vnlog_headers[$i_column]; + } + else + { + $id++; + } my $rangesize = getRangeSize($id); last if @fields < $rangesize; @@ -858,6 +950,8 @@ sub mainThread @domain, splice( @fields, 0, $rangesize ) ) . "\n", $domain0_numeric); + + $i_column++; } } @@ -1173,45 +1267,45 @@ Simple plotting of piped data: $ seq 5 | awk '{print 2*$1, $1*$1}' | feedgnuplot --lines --points --legend 0 "data 0" --title "Test plot" --y2 1 - --terminal 'dumb 80,40' --exit + --unset grid --terminal 'dumb 80,40' --exit Test plot - 10 ++------+--------+-------+-------+-------+--------+-------+------*A 25 - + + + + + + + + **#+ - | : : : : : : data 0+**A*** | - | : : : : : : :** # | - 9 ++.......................................................**.##....| - | : : : : : : ** :# | - | : : : : : : ** # | - | : : : : : :** ##: ++ 20 - 8 ++................................................A....#..........| - | : : : : : **: # : | - | : : : : : ** : ## : | - | : : : : : ** :# : | - | : : : : :** B : | - 7 ++......................................**......##................| - | : : : : ** : ## : : ++ 15 - | : : : : ** : # : : | - | : : : :** : ## : : | - 6 ++..............................*A.......##.......................| - | : : : ** : ##: : : | - | : : : ** : # : : : | - | : : :** : ## : : : ++ 10 - 5 ++......................**........##..............................| - | : : ** : #B : : : | - | : : ** : ## : : : : | - | : :** : ## : : : : | - 4 ++...............A.......###......................................| - | : **: ##: : : : : | - | : ** : ## : : : : : ++ 5 - | : ** : ## : : : : : | - | :** ##B# : : : : : | - 3 ++.....**..####...................................................| - | **#### : : : : : : | - | **## : : : : : : : | - B** + + + + + + + + - 2 A+------+--------+-------+-------+-------+--------+-------+------++ 0 + 10 +-----------------------------------------------------------------+ 25 + | + + + + + + + *##| + | data 0 ***A*#* | + | ** # | + 9 |-+ ** ## | + | ** # | + | ** # | + | ** ## +-| 20 + 8 |-+ A # | + | ** # | + | ** ## | + | ** # | + | ** B | + 7 |-+ ** ## | + | ** ## +-| 15 + | ** # | + | ** ## | + 6 |-+ *A ## | + | ** ## | + | ** # | + | ** ## +-| 10 + 5 |-+ ** ## | + | ** #B | + | ** ## | + | ** ## | + 4 |-+ A ### | + | ** ## | + | ** ## +-| 5 + | ** ## | + | ** ##B# | + 3 |-+ ** #### | + | **#### | + | #### | + |## + + + + + + + | + 2 +-----------------------------------------------------------------+ 0 1 1.5 2 2.5 3 3.5 4 4.5 5 @@ -1279,41 +1373,64 @@ with the I-value at the start of that line. =head3 Curve indexing -By default, each column represents a separate curve. This is fine unless sparse -data is to be plotted. With the C<--dataid> option, each point is represented by -2 values: a string identifying the curve, and the value itself. If we add -C<--dataid> to the original example: +We index the curves in one of 3 ways: sequentially, explicitly with a +C<--dataid> or by C<--vnlog> headers. + +By default, each column represents a separate curve. The first column (after any +domain) is curve C<0>. The next one is curve C<1> and so on. This is fine unless +sparse data is to be plotted. With the C<--dataid> option, each point is +represented by 2 values: a string identifying the curve, and the value itself. +If we add C<--dataid> to the original example: $ seq 5 | awk '{print 2*$1, $1*$1}' | feedgnuplot --dataid --autolegend we get 5 different curves with one point in each. The first column, as produced by C, is B<2,4,6,8,10>. These are interpreted as the IDs of the curves to -be plotted. The C<--autolegend> option adds a legend using the given IDs to +be plotted. + +If we're plotting C data (L) then we +can get the curve IDs from the vnlog header. Vnlog is a trivial data format +where lines starting with C<#> are comments and the first comment contains +column labels. If we have such data, C can interpret these +column labels if the C perl modules are available. + +The C<--autolegend> option adds a legend using the given IDs to label the curves. The IDs need not be numbers; generic strings are accepted. As many points as desired can appear on a single line. C<--domain> can be used in -conjunction with C<--dataid>. +conjunction with C<--dataid> or C<--vnlog>. =head3 Multi-value style support Depending on how gnuplot is plotting the data, more than one value may be needed to represent the range of a single point. Basic 2D plots have 2 numbers representing each point: 1 domain and 1 range. But if plotting with -C<--circles>, for instance, then there's an extra range value: the radius. A -similar situation exists with C<--colormap> where each point contains the -position I the color. There are other gnuplot styles that require more data -(such as error bars), but none of these are directly supported by the script. -They can still be used, however, by specifying the specific style with -C<--style>, and specifying how many values are needed for each point with -C<--rangesizeall> or C<--rangesize> or C<--extraValuesPerPoint>. Those options -that specify the range size are required I for styles not explicitly -supported by feedgnuplot; supported styles do the right thing automatically. +C<--circles>, for instance, then there's an extra range value: the radius. Many +other gnuplot styles require more data: errorbars, variable colors (C), variable sizes (C), labels and so on. +The feedgnuplot tool itself does not know about all these intricacies, but they +can still be used, by specifying the specific style with C<--style>, and +specifying how many values are needed for each point with any of +C<--rangesizeall, C<--tuplesizeall>, C<--rangesize>, C<--tuplesize>. These +options are required I for styles not explicitly supported by feedgnuplot; +supported styles do the right thing automatically. -More examples: if making a 2d plot of y error bars where gnuplot expects a -(x,y,ydelta) tuple for each point, you want C<--rangesizeall 2> because you have -one domain value (x) and 2 range values (y,ydelta). Gnuplot can also plot -lopsided y errorbars by giving a tuple (x,y,ylow,yhigh). This is similar as -before, but you want C<--rangesizeall 3> instead. +Specific example: if making a 2d plot of y error bars, the exact format can be +queried by running C and invoking C. This tells us +that there's a 3-column form: C and a 4-column form: C. With 2d plots feedgnuplot will always output the 1-value domain C, so +the rangesize is 2 and 3 respectively. Thus the following are equivalent: + $ echo '1 2 0.3 + 2 3 0.4 + 3 4 0.5' | feedgnuplot --domain --rangesizeall 2 --with 'yerrorbars' + + $ echo '1 2 0.3 + 2 3 0.4 + 3 4 0.5' | feedgnuplot --domain --tuplesizeall 3 --with 'yerrorbars' + + $ echo '1 2 1.7 2.3 + 2 3 2.6 3.4 + 3 4 3.5 4.5' | feedgnuplot --domain --rangesizeall 3 --with 'yerrorbars' =head3 3D data @@ -1547,6 +1664,19 @@ point in curve ID 20 =item +C<--vnlog> + +Vnlog is a trivial data format where lines starting with C<#> are comments and +the first comment contains column labels. Some tools for working with such data +are available from the C project: L. +With the C perl modules installed, we can read the vnlog column headers +with C. This replaces C<--dataid>, and we can do all the +normal things with these headers. For instance C will generate plot legends for each column in the vnlog, using the +vnlog column label in the legend. + +=item + C<--[no]3d> Do [not] plot in 3D. This only makes sense with C<--domain>. Each domain here is @@ -1563,7 +1693,8 @@ Interpret the X data as a time/date, parsed with the given format C<--colormap> Show a colormapped xy plot. Requires extra data for the color. zmin/zmax can be -used to set the extents of the colors. Automatically sets the C<--rangesize>. +used to set the extents of the colors. Automatically sets the +C<--rangesize>/C<--tuplesize>. =item @@ -1591,7 +1722,8 @@ Do [not] draw points C<--circles> Plot with circles. This requires a radius be specified for each point. -Automatically sets the C<--rangesize>. C supported for 3d plots. +Automatically sets the C<--rangesize>/C<--tuplesize>. C supported for 3d +plots. =item @@ -1632,7 +1764,10 @@ C<--xmin/xmax/ymin/ymax/y2min/y2max/zmin/zmax xxx> Set the range for the given axis. These x-axis bounds are ignored in a streaming plot. The y2-axis bound do not apply in 3d plots. The z-axis bounds apply -I to 3d plots or colormaps. +I to 3d plots or colormaps. Note that there is no C<--xrange> to set both +sides at once or C<--xinv> to flip the axis around: anything more than the +basics supported in this option is clearly obtainable by talking to gnuplot, for +instance C<--set 'xrange [20:10]'> to set the given inverted bounds. =item @@ -1774,9 +1909,9 @@ Gnuplot can plot both data and symbolic equations. C generally plots data, but with this option can plot symbolic equations I. This is generally intended to augment data plots, since for equation-only plots you don't need C. C<--equation> can be passed multiple times for -multiple equations. The given strings are passed to gnuplot directly without any -thing added or removed, so styling and such should be applied in the string. A -basic example: +multiple equations. The given strings are passed to gnuplot directly without +anything added or removed, so styling and such should be applied in the string. +A basic example: seq 100 | awk '{print $1/10, $1/100}' | feedgnuplot --with 'lines lw 3' --domain --ymax 1 @@ -1826,7 +1961,7 @@ file type is desired, use both C<--hardcopy> and C<--terminal> =item -C<--maxcurves xxx> +C<--maxcurves N> The maximum allowed number of curves. This is 100 by default, but can be reset with this option. This exists purely to prevent perl from allocating all of the @@ -1836,20 +1971,22 @@ system's memory when reading bogus data C<--monotonic> -If C<--domain> is given, checks to make sure that the x- coordinate in the input +If C<--domain> is given, checks to make sure that the x-coordinate in the input data is monotonically increasing. If a given x-variable is in the past, all data currently cached for this curve is purged. Without C<--monotonic>, all data is -kept. Does not make sense with 3d plots. No C<--monotonic> by default. The data is -replotted before being purged +kept. Does not make sense with 3d plots. No C<--monotonic> by default. The data +is replotted before being purged. This is useful in streaming plots where the +incoming data represents multiple iterations of the same process (repeated +simulations of the same period in time, for instance). =item -C<--rangesize curveID xxx> +C<--rangesize curveID N> -The options C<--rangesizeall>, C<--rangesize> and C<--extraValuesPerPoint> set -the number of values are needed to represent each point being plotted (see -L above). These options are I needed if -unknown styles are used, with C<--styleall> or C<--with> for instance. +The options C<--rangesizeall> and C<--rangesize> set the number of values are +needed to represent each point being plotted (see L above). These options are I needed if unknown styles are used, +with C<--styleall> or C<--with> for instance. C<--rangesize> is used to set how many values are needed to represent the range of a point for a particular curve. This overrides any defaults that may exist @@ -1861,19 +1998,25 @@ rangesize should apply. =item -C<--rangesizeall xxx> +C<--tuplesize curveID N> + +Very similar to C<--rangesize>, but instead of specifying the I only, +this specifies the whole tuple. For instance if we're plotting circles, the +tuplesize is 3: C. In a 2D plot there's a 1-dimensional domain: +C, so the rangesize is 2: C. This dimensionality can be given +either way. + +=item + +C<--rangesizeall N> Like C<--rangesize>, but applies to I the curves. -C<--extraValuesPerPoint xxx> +=item -Like C<--rangesizeall>, but instead of overriding the default, adds to it. For -example, if plotting non-lopsided y errorbars gnuplot wants (x,y,ydelta) tuples. -These can be specified both with C<--rangesizeall 2> (because there are 2 range -values) or C<--extraValuesPerPoint 1> (because there's 1 more value than usual). +C<--tuplesizeall N> -This option is I needed if unknown styles are used, with C<--styleall> or -C<--with> for instance. +Like C<--tuplesize>, but applies to I the curves. =item