package ReportGen::Plot; use strict; # # Wrapper for gnuplot-like control files # use ReportGen::Line; require FileHandle; $ReportGen::Plot::Plotter = undef(); my @labelCmds = map { ("x$_", "y$_", "x2$_", "y2$_") } qw( label ); my @ticCmds = map { ("x$_", "y$_", "x2$_", "y2$_") } qw( tics ); my @KnownCmds = ( @labelCmds, map { ("$_", "no$_") } (@ticCmds, qw( title grid timestamp )), qw( data size ), ); sub new { my ($proto, $name) = @_; my $type = ref($proto) || $proto; my $this = bless({}, $type); $this->{theName} = $name; $this->{thetitle} = undef; $this->{theTerm} = ''; $this->{theOFName} = ''; $this->{theOpt} = []; $this->{theLines} = []; $this->{theSets} = {}; return $this; } sub clone { my ($this) = @_; my $clone = bless({}, ref($this)); $clone->{theName} = $this->{theName}; $clone->{theTitle} = $this->{theTitle}; $clone->{theTerm} = $this->{theTerm};; $clone->{theOFName} = $this->{theOFName}; $clone->{theOpt} = [ @{$this->{theOpt}} ]; $clone->{theLines} = [ map { $_-> clone() } @{$this->{theLines}} ]; $clone->{theSets} = { map { $_ => $this->{theSets}->{$_} } keys %{$this->{theSets}} }; return $clone; } sub ofile { return shift->{theOFName}; } sub name { my ($this, $name) = @_; $this->{theName} = $name if defined $name; return $this->{theName}; } sub title { my ($this, $title) = @_; $this->{theTitle} = $title if defined $title; return $this->{theTitle}; } sub opt { my ($this, $cmd_name, $opt_name, @flds) = @_; die() unless $cmd_name; if ($cmd_name =~ /^set$/i) { $this->{theSets}->{$opt_name} = join(' ', @flds); } push @{$this->{theOpt}}, join(' ', $cmd_name, $opt_name, @flds); } sub set { my $this = shift; $this->opt('set', @_); } sub isSet { my ($this, $name) = @_; my $opt = $this->{theSets}->{$name}; return (defined $opt) ? $opt || ' ' : undef(); } sub line { my ($this, @lines) = @_; foreach (@lines) { push @{$this->{theLines}}, $_; } } sub lines { my $this = shift; return ( @{$this->{theLines}} ); } sub gif { my ($this, @opt) = @_; $this->terminal('gif', @opt); } sub postscript { my ($this, @opt) = @_; $this->terminal('postscript', @opt); } sub terminal { my ($this, @opt) = @_; $this->{theTerm} = join(' ', @opt); } sub output { my ($this, $ofname) = @_; $this->{theOFName} = $ofname; } sub tics { my ($this, $axis, @tics) = @_; my $tics = ' '; while (@tics) { my $label = shift @tics; my $val = shift @tics; $tics .= sprintf('"%s" %s,', $label, $val); } chop($tics); $this->set("${axis}tics (", $tics, ')'); } sub range { my ($this, $axis, $lo, $hi) = @_; $lo = '*' unless defined $lo; $hi = '*' unless defined $hi; $this->set("${axis}range [ $lo : $hi ]"); } sub term2ext { my ($this) = @_; return '' unless $this->{theTerm}; return '.gif' if $this->{theTerm} =~ /^gif/; return '.eps' if $this->{theTerm} =~ /^postscript/; return '.png' if $this->{theTerm} =~ /^png/; return '.txt' if $this->{theTerm} =~ /^dumb/; return ".$1" if $this->{theTerm} =~ /^(\w+)/; return ''; } sub lineCount { my ($this) = @_; my @counts = $this->forEach('lineCount'); my $sum = 0; foreach (@counts) { $sum += $_; } return wantarray ? @counts : $sum; } sub forEach { my ($this, $method, @params) = @_; my @res = (); foreach (@{$this->{theLines}}) { push @res, $_->$method(@params); } return wantarray ? @res : scalar @res; } sub plot { my ($this) = @_; my $plotter = $ReportGen::Plot::Plotter; my $f = new FileHandle("|$plotter") or die("cannot start $plotter, $!; stopped"); $f->print($this->image()); undef $f; } sub image { my ($this) = @_; my $image = '# ' . ($this->{theName} || ''). "\n" . '# ' . $this->{theOFName} . "\n"; '# ' . localtime() . "\n"; $image .= sprintf('set title "%s"', $this->{theTitle}) if $this->{theTitle}; $image .= join("\n", '', @{$this->{theOpt}}); $image .= "\nset term " . $this->{theTerm} if $this->{theTerm}; $image .= "\n" . sprintf('set output "%s"', $this->{theOFName}) if $this->{theOFName}; if (@{$this->{theLines}}) { $image .= "\nplot \\\n" . join(",\\\n", map { "\t" . $_->image() } @{$this->{theLines}}); } $image .= "\n"; return $image; } use vars qw($AUTOLOAD); sub AUTOLOAD { my $this = shift; my $A = $AUTOLOAD; $A =~ s/ReportGen::Plot:://; return undef if $A eq 'DESTROY'; my $opt = lc $A; my $quoted = $opt =~ s/_qw$//; die("unknown plot command '$A', stopped") unless grep { /^$opt$/ } @KnownCmds; if ($quoted) { $this->set($opt, sprintf('"%s"', join(' ', @_))); } else { $this->set($opt, @_); } } 1;