Compare commits

...

5 Commits
v1.31 ... v1.32

Author SHA1 Message Date
Dima Kogan
d75b572875 version bump 2014-02-05 13:58:01 -08:00
Dima Kogan
6cb1574e2b tests use some of the nicer new syntax 2014-02-05 13:53:34 -08:00
Dima Kogan
4fd1c390ff added --rangesize and --rangesizeall
--rangesizeall is a different way to express --extraValuesPerPoint. --rangesize
is per-curve however
2014-02-05 13:53:16 -08:00
Dima Kogan
4163e24956 Simplified data parsing.
Instead of complicated regexes, I now simply do splits and joins. This paves the
way for per-curve extraValuesPerPoint
2014-02-05 02:54:00 -08:00
Dima Kogan
13268a1fa8 fixed typo 2014-01-26 12:13:38 -08:00
5 changed files with 234 additions and 61 deletions

View File

@@ -1,3 +1,10 @@
feedgnuplot (1.32)
* Added --rangesize and --rangesizeall. Different curves can now plot
different-size tuples
-- Dima Kogan <dima@secretsauce.net> Wed, 05 Feb 2014 13:57:58 -0800
feedgnuplot (1.31)
* Test suite requires gawk to get strftime()

View File

@@ -13,7 +13,7 @@ use Thread::Queue;
use Pod::Usage;
use Time::Piece;
my $VERSION = 1.31;
my $VERSION = 1.32;
my %options;
interpretCommandline();
@@ -121,6 +121,8 @@ sub interpretCommandline
$options{styleall} = '';
$options{with} = '';
$options{rangesize} = [];
GetOptions(\%options, 'stream:s', 'domain!', 'dataid!', '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',
@@ -129,7 +131,8 @@ sub interpretCommandline
'square!', 'square_xy!', 'hardcopy=s', 'maxcurves=i', 'monotonic!', 'timefmt=s',
'histogram=s@', 'binwidth=f', 'histstyle=s',
'terminal=s',
'extraValuesPerPoint=i', 'help', 'dump', 'exit', 'version',
'rangesize=s{2}', 'rangesizeall=i', 'extraValuesPerPoint=i',
'help', 'dump', 'exit', 'version',
'geometry=s') or pod2usage( -exitval => 1,
-verbose => 1, # synopsis and args
-output => \*STDERR );
@@ -176,7 +179,7 @@ sub interpretCommandline
# arrays in order to preserve the ordering. I parse both of these into hashes
# because those are useful to have later. After this I can access individual
# legends with $options{legend_hash}{curveid}
for my $listkey (qw(legend curvestyle))
for my $listkey (qw(legend curvestyle rangesize))
{
$options{"${listkey}_hash"} = {};
@@ -193,6 +196,12 @@ sub interpretCommandline
exit -1;
}
if ( defined $options{rangesizeall} && defined $options{extraValuesPerPoint} )
{
print STDERR "Only one of --rangesizeall and --extraValuesPerPoint may be given\n";
exit -1;
}
# parse stream option. Allowed only numbers >= 0 or 'trigger'. After this code
# $options{stream} is
# -1 for triggered replotting
@@ -347,8 +356,6 @@ sub interpretCommandline
my $Nfields = scalar split( ' ', $options{timefmt});
$options{timefmt_Ncols} = $Nfields;
my $regex_str = join( '\s+', ('\S+') x $Nfields );
$options{timefmt_regex} = qr/$regex_str/;
# make sure --xlen is an integer. With a timefmt xlen goes through strptime
# and strftime, and those are integer-only
@@ -427,10 +434,18 @@ sub makeDomainNumeric
sub mainThread
{
my $valuesPerPoint = 1;
if($options{extraValuesPerPoint}) { $valuesPerPoint += $options{extraValuesPerPoint}; }
if($options{colormap}) { $valuesPerPoint++; }
if($options{circles} ) { $valuesPerPoint++; }
my $valuesPerPoint;
if( $options{rangesizeall} )
{
$valuesPerPoint = $options{rangesizeall};
}
else
{
$valuesPerPoint = 1;
if($options{extraValuesPerPoint}) { $valuesPerPoint += $options{extraValuesPerPoint}; }
if($options{colormap}) { $valuesPerPoint++; }
if($options{circles} ) { $valuesPerPoint++; }
}
local *PIPE;
my $dopersist = '';
@@ -573,16 +588,6 @@ sub mainThread
setCurveAsHistogram( $_ ) foreach (@{$options{histogram}});
# regexp for a possibly floating point, possibly scientific notation number
my $numRE = '-?\d*\.?\d+(?:[Ee][-+]?\d+)?';
my $domainRE = $options{timefmt_regex} || $numRE;
# a point may be preceded by an id
my $pointRE = $options{dataid} ? '(\S+)\s+' : '()';
$pointRE .= '(' . join('\s+', ($numRE) x $valuesPerPoint) . ')';
$pointRE = qr/$pointRE/;
# set all the axis ranges
# If a bound isn't given I want to set it to the empty string, so I can communicate it simply to
# gnuplot
@@ -651,18 +656,37 @@ sub mainThread
# line is used)
# 3d plots require $options{domain}, and dictate "x y" for the domain instead of just "x"
my @fields = split;
if($options{domain})
{
/($domainRE)/go or next;
$domain[0] = $1;
$domain0_numeric = makeDomainNumeric( $domain[0] );
if($options{'3d'})
if( $options{timefmt} )
{
/($numRE)/go or next;
$domain[1] = $1;
# no point if doing anything unless I have at least the domain and
# 1 piece of data
next if @fields < $options{timefmt_Ncols}+1;
$domain[0] = join (' ', splice( @fields, 0, $options{timefmt_Ncols}) );
$domain0_numeric = makeDomainNumeric( $domain[0] );
}
elsif( $options{monotonic} )
elsif(!$options{'3d'})
{
# no point if doing anything unless I have at least the domain and
# 1 piece of data
next if @fields < 1+1;
$domain[0] = $domain0_numeric = shift @fields;
}
else
{
# no point if doing anything unless I have at least the domain and
# 1 piece of data
next if @fields < 2+1;
@domain = splice(@fields, 0, 2);
}
if( $options{monotonic} )
{
if( defined $latestX && $domain0_numeric < $latestX )
{
@@ -676,7 +700,6 @@ sub mainThread
else
{ $latestX = $domain0_numeric; }
}
}
else
{
@@ -684,8 +707,7 @@ sub mainThread
# $. on the data queue in that case
if(defined $dataQueue)
{
s/ ([\d]+)$//o;
$domain[0] = $1;
$domain[0] = pop @fields;
}
else
{
@@ -695,13 +717,32 @@ sub mainThread
}
my $id = -1;
while (/$pointRE/go)
{
if($1 ne '') {$id = $1;}
else {$id++; }
pushPoint(getCurve($id),
"@domain $2\n", $domain0_numeric);
while(@fields)
{
my $rangesize = $valuesPerPoint;
if($options{dataid})
{
$id = shift @fields;
}
else
{
$id++;
}
if( $options{rangesize_hash}{$id} )
{
$rangesize = $options{rangesize_hash}{$id};
}
last if @fields < $rangesize;
pushPoint(getCurve($id),
join(' ',
@domain,
splice( @fields, 0, $rangesize ) ) . "\n",
$domain0_numeric);
}
}
}
@@ -1112,17 +1153,24 @@ conjunction with C<--dataid>.
=head3 Multi-value style support
Depending on how gnuplot is plotting the data, more than one value may be needed
to represent a single point. For example, the script has support to plot all the
data with C<--circles>. This requires a radius to be specified for each point in
addition to the position of the point. Thus, when plotting with C<--circles>, 2
numbers are read for each data point instead of 1. A similar situation exists
with C<--colormap> where each point contains the position I<and> 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,
though, by specifying the specific style with C<--style>, and specifying how
many extra values are needed for each point with C<--extraValuesPerPoint extra>.
C<--extraValuesPerPoint> is ONLY needed for the styles not explicitly supported;
supported styles set that variable automatically.
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<and> 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<only> 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.
=head3 3D data
@@ -1157,7 +1205,7 @@ C<--xmin> and C<--xmax> I<must> use the format passed in to C<--timefmt>
Using this option changes both the way the input is parsed I<and> the way the
x-axis tics are labelled. Gnuplot tries to be intelligent in this labelling, but
it doesn't always to what the user wants. The labelling can be controlled with
it doesn't always do what the user wants. The labelling can be controlled with
the gnuplot C<set format> command, which takes the same type of format string as
C<--timefmt>. Example:
@@ -1365,8 +1413,7 @@ 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 increments
C<--extraValuesPerPoint>
used to set the extents of the colors. Automatically sets the C<--rangesize>.
=item
@@ -1394,8 +1441,7 @@ Do [not] draw points
C<--circles>
Plot with circles. This requires a radius be specified for each point.
Automatically increments C<--extraValuesPerPoint>). C<Not> supported for 3d
plots.
Automatically sets the C<--rangesize>. C<Not> supported for 3d plots.
=item
@@ -1599,13 +1645,32 @@ replotted before being purged
=item
C<--rangesize curveID xxx>
The options C<--rangesizeall>, C<--rangesize> and C<--extraValuesPerPoint> set
the number of values are needed to represent each point being plotted (see
L</"Multi-value style support"> above). These options are I<only> 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
for this curve only.
=item
C<--rangesizeall xxx>
Like C<--rangesize>, but applies to I<all> the curves.
C<--extraValuesPerPoint xxx>
How many extra values are given for each data point. Normally this is 0, and
does not need to be specified, but sometimes we want extra data, like for colors
or point sizes or error bars, etc. feedgnuplot options that require this
(colormap, circles) automatically set it. This option is ONLY needed if unknown
styles are used, with C<--styleall> or C<--with> for instance
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).
This option is I<only> needed if unknown styles are used, with C<--styleall> or
C<--with> for instance.
=item

View File

@@ -15,6 +15,8 @@ complete -W \
--dump \
--exit \
--extraValuesPerPoint \
--rangesizeall \
--rangesize \
--extracmds \
--set \
--unset \

View File

@@ -37,7 +37,9 @@ _arguments -S
'--hardcopy[Plot to a file]:filename' \
'--maxcurves[The maximum allowed number of curves]:number of curves' \
'(--3d)--monotonic[Resets plot if an X in the past is seen]' \
'--extraValuesPerPoint[How many extra values are given for each data point]:N'\
'(--rangesizeall)--extraValuesPerPoint[How many extra values are given for each data range]:N'\
'(--extraValuesPerPoint)--rangesizeall[How many values are given for each data range]:N'\
'*--rangesize[How many values comprise a data range in this curve]:curve id: :N:' \
'--dump[Instead of printing to gnuplot, print to STDOUT]' \
'--geometry[The X11 geometry string]:geometry string:' \
'*--curvestyle[Additional styles for a curve]:curve id: :style:' \

103
t/plots.t
View File

@@ -39,7 +39,7 @@ BEGIN {
}
}
use Test::More tests => 52;
use Test::More tests => 56;
use File::Temp 'tempfile';
use IPC::Run 'run';
use String::ShellQuote;
@@ -804,7 +804,7 @@ EOF
tryplot( testname => 'Error bars (using extraValuesPerPoint)',
cmd => q{seq 5 | gawk '{print $1,$1,$1/10}'},
options => [qw(--domain),
qw(--extraValuesPerPoint 1 --curvestyle 0), 'with errorbars'],
qw(--extraValuesPerPoint 1 --with errorbars)],
refplot => <<'EOF' );
@@ -849,6 +849,103 @@ tryplot( testname => 'Error bars (using extraValuesPerPoint)',
EOF
tryplot( testname => 'Error bars (using rangesizeall)',
cmd => q{seq 5 | gawk '{print $1,$1,$1/10}'},
options => [qw(--domain),
qw(--rangesizeall 2 --with errorbars)],
refplot => <<'EOF' );
5.5 ++---------+-----------+----------+----------+----------+-----------+----------+---------**
+ + + + + + + + *
| *
5 ++ +A
| *
| *
| *
4.5 ++ **
| *** |
| * |
4 ++ A ++
| * |
| * |
| *** |
3.5 ++ ++
| *** |
| * |
3 ++ A ++
| * |
| * |
| *** |
2.5 ++ ++
| |
| *** |
2 ++ A ++
| * |
| *** |
| |
1.5 ++ ++
| |
| |
1 A* ++
** |
| |
+ + + + + + + + +
0.5 ++---------+-----------+----------+----------+----------+-----------+----------+---------++
1 1.5 2 2.5 3 3.5 4 4.5 5
EOF
tryplot( testname => 'Error bars (using rangesize, rangesizeall)',
cmd => q{seq 5 | gawk '{print $1,"vert",$1,$1/10,"horiz",5-$1,$1-$1/5,$1+$1/20}'},
options => [qw(--domain --dataid),
qw(--rangesize vert 2 --rangesizeall 3 --with xerrorbars --style vert), 'with errorbars',
qw(--xmin 1 --xmax 5 --ymin 0.5 --ymax 5.5)],
refplot => <<'EOF' );
+-----------+----------+-----------+----------+-----------+----------+-----------+---------**
+ + + + + + + + *
| *
5 ++ +A
| *
| *
| *
| **
| *** |
## * |
4 B# A ++
## * |
| * |
| *** |
| |
| *** |
| # # * |
3 ++ #########B## A ++
| # # * |
| * |
| *** |
| |
| |
| *** # # |
2 ++ A ##############B### ++
| * # # |
| *** |
| |
| |
| |
| # # |
1 A* ##################B##### ++
** # # |
| |
+ + + + + + + + +
+-----------+----------+-----------+----------+-----------+----------+-----------+----------+
1 1.5 2 2.5 3 3.5 4 4.5 5
EOF
SKIP:
{
@@ -2183,7 +2280,7 @@ sub tryplot
my %args = @_;
my @options = ('--exit',
'--extracmds', 'unset grid',
qw(--unset grid),
'--terminal', 'dumb 100,40');
unshift @options, @{$args{options}};