From: <de...@de...> - 2011-05-14 06:32:02
|
Author: PeterThoeny Date: 2011-05-14 01:31:50 -0500 (Sat, 14 May 2011) New Revision: 21235 Trac url: http://develop.twiki.org/trac/changeset/21235 Modified: twiki/branches/TWikiRelease05x00/ChartPlugin/lib/TWiki/Plugins/ChartPlugin.pm twiki/branches/TWikiRelease05x00/ChartPlugin/lib/TWiki/Plugins/ChartPlugin/Chart.pm twiki/branches/TWikiRelease05x00/ChartPlugin/lib/TWiki/Plugins/ChartPlugin/MANIFEST twiki/branches/TWikiRelease05x00/ChartPlugin/lib/TWiki/Plugins/ChartPlugin/Parameters.pm twiki/branches/TWikiRelease05x00/ChartPlugin/lib/TWiki/Plugins/ChartPlugin/Table.pm twiki/branches/TWikiRelease05x00/ChartPlugin/lib/TWiki/Plugins/ChartPlugin/build.pl Log: Item6701: Sync from trunk Modified: twiki/branches/TWikiRelease05x00/ChartPlugin/lib/TWiki/Plugins/ChartPlugin/Chart.pm =================================================================== --- twiki/branches/TWikiRelease05x00/ChartPlugin/lib/TWiki/Plugins/ChartPlugin/Chart.pm 2011-05-14 06:31:41 UTC (rev 21234) +++ twiki/branches/TWikiRelease05x00/ChartPlugin/lib/TWiki/Plugins/ChartPlugin/Chart.pm 2011-05-14 06:31:50 UTC (rev 21235) @@ -1,6 +1,7 @@ # ChartPlugin for TWiki Enterprise Collaboration Platform, http://TWiki.org/ # -# Copyright (C) 2002-2010 Peter Thoeny, Peter@Thoeny.org +# Copyright (C) 2002-2011 Peter Thoeny, Peter[at]Thoeny.org +# Copyright (C) 2008-2011 TWiki Contributors # Plugin written by http://TWiki.org/cgi-bin/view/Main/TaitCyrus # # For licensing info read LICENSE file in the TWiki root. @@ -35,24 +36,68 @@ # setXlabel(@labels) - Set the label under the X axis - default is none # getXlabel - Get the X label # -# setYlabel($flag) - Set the label under the Y axis - default is none -# getYlabel - Get the Y label +# setYlabel($flag) - Set the label for Y axis #1- default is none +# getYlabel - Get Y label #1 # -# setData(@data) - Set the the data (array) to chart -# getData - Get the data (array) -# getNumDataSets - Get the number of data sets found in the data. -# getNumDataPoints - Get the number of data points in a data set +# setYlabel1($flag) - Set the label for Y axis #1 - default is none +# getYlabel1 - Get the Y label #1 # -# setYmin($min) - Set the minimum Y value to display on the chart -# getYmin - Get the minimum Y value. If no user specified +# setYlabel2($flag) - Set the label for Y axis #2 - default is none +# getYlabel2 - Get the Y label #2 +# +# setData(@data) - Set the the data (array) for $LEFT yaxis +# setData1(@data) - Set the the data (array) for $LEFT yaxis +# setData2(@data) - Set the the data (array) for $RIGHT yaxis +# getData - Get the data (array) for $LEFT yaxis +# getData1 - Get the data (array) for $LEFT yaxis +# getData2 - Get the data (array) for $RIGHT yaxis +# +# getNumDataSets - Get the number of data sets found in data for $LEFT yaxis +# getNumDataSets1 - Get the number of data sets found in data for $LEFT yaxis +# getNumDataSets2 - Get the number of data sets found in data for $RIGHT yaxis +# +# getNumDataPoints - Get the number of data points in data set=1. +# getNumDataPoints1 - Get the number of data points in data set=1. +# getNumDataPoints2 - Get the number of data points in data set=2. +# +# setYmin($min) - Set the minimum Y value to use for $LEFT yaxis +# setYmin1($min) - Set the minimum Y value to use for $LEFT yaxis +# setYmin2($min) - Set the minimum Y value to use for $RIGHT yaxis +# getYmin - Get the minimum Y value for $LEFT yaxis. If no user specified # value via setYmin(), then return the minimum # value actually seen in the data sets +# getYmin1 - Get the minimum Y value for $LEFT yaxis. If no user specified +# value via setYmin(), then return the minimum +# value actually seen in the data sets +# getYmin2 - Get the minimum Y value for $RIGHT yaxis. If no user specified +# value via setYmin(), then return the minimum +# value actually seen in the data sets # -# setYmax($max) - Set the maximum Y value to display on the chart -# getYmax - Get the maximum Y value. If no user specified +# setYmax($max) - Set the maximum Y value to use for $LEFT yaxis +# setYmax1($max) - Set the maximum Y value to use for $LEFT yaxis +# setYmax2($max) - Set the maximum Y value to use for $RIGHT yaxis +# getYmax - Get the maximum Y value for $LEFT yaxis. If no user specified # value via setYmax(), then return the maximum # value actually seen in the data sets +# getYmax1 - Get the maximum Y value for $LEFT yaxis. If no user specified +# value via setYmax(), then return the maximum +# value actually seen in the data sets +# getYmax2 - Get the maximum Y value for $RIGHT yaxis. If no user specified +# value via setYmax(), then return the maximum +# value actually seen in the data sets # +# setXmin($min) - Set the minimum X value to display on the chart +# (only applicable for scatter charts) +# getXmin - Get the minimum X value. If no user specified +# value via setXmin(), then return the minimum +# value actually seen in the data sets +# +# setXmax($max) - Set the maximum X value to display on the chart +# (only applicable for scatter charts) +# getXmax - Get the maximum X value. If no user specified +# value via setXmax(), then return the maximum +# value actually seen in the data sets +# # setSubTypes(@types)- Set array describing the subtypes for each data set. # Values can be area or line and corresponds to the # associated data set. @@ -65,15 +110,32 @@ # - Set the angle of the X axis labels # getXaxisAngle - Get the angle of the X axis labels # -# setYaxis(@yaxis) - Set Y axis draw flag ("on" or "off") -# getYaxis - Get the value of the Y axis draw flag +# setYaxis(@yaxis) - Set Y axis draw flag ("on" or "off") for $LEFT yaxis +# getYaxis - Get the value of the Y axis draw flag for $LEFT yaxis # -# setNumYGrids($num) - Set the number of Y axes to draw -# getNumYGrids - Get the number of Y axes to draw +# setYaxis1(@yaxis) - Set Y axis draw flag ("on" or "off") for $LEFT yaxis +# getYaxis1 - Get the value of the Y axis draw flag for $LEFT yaxis # -# setNumYTics($num) - Set the number of tic marks to draw between Y grids -# getNumYTics - Get the number of tic marks to draw between Y grids +# setYaxis2(@yaxis) - Set Y axis draw flag ("on" or "off") for $RIGHT yaxis +# getYaxis2 - Get the value of the Y axis draw flag for $RIGHT yaxis # +# setDefNumYGrids($num)- Set the defaultnumber of Y axes to draw +# getDefNumYGrids - Get the defaultnumber of Y axes to draw +# +# setNumYGrids($num) - Set the number of Y axes to draw for $LEFT yaxis +# setNumYGrids1($num)- Set the number of Y axes to draw for $LEFT yaxis +# setNumYGrids2($num)- Set the number of Y axes to draw for $RIGHT yaxis +# getNumYGrids - Get the number of Y axes to draw for $LEFT yaxis +# getNumYGrids1 - Get the number of Y axes to draw for $LEFT yaxis +# getNumYGrids2 - Get the number of Y axes to draw for $RIGHT yaxis +# +# setNumYTics($num) - Set the number of tic marks to draw between Y grids for $LEFT yaxis +# setNumYTics1($num) - Set the number of tic marks to draw between Y grids for $LEFT yaxis +# setNumYTics2($num) - Set the number of tic marks to draw between Y grids for $RIGHT yaxis +# getNumYTics - Get the number of tic marks to draw between Y grids for $LEFT yaxis +# getNumYTics1 - Get the number of tic marks to draw between Y grids for $LEFT yaxis +# getNumYTics2 - Get the number of tic marks to draw between Y grids for $RIGHT yaxis +# # setNumXGrids($num) - Set the number of X axis to draw # getNumXGrids - Get the number of X axis to draw # @@ -132,6 +194,8 @@ # getBGcolor() - Get the background color of the chart # setGridColor($color)- Set the color of the grid # getGridColor() - Get the color of the grid +# setBorderColor($color)- Set the color of the chart border +# getBorderColor() - Get the color of the gridchart border # # computeFinalColors - Computes the final colors to be used by each data # set taking colors from either the user specified @@ -150,43 +214,81 @@ # setLineWidth($width)- Set the width, in pixels, lines are drawn with # getLineWidth() - Get the width of drawn lines # -# setBarLeadingSpace($pixels) -# - Set the leading space (in pixels) before the first drawn bar graph -# getBarLeadingSpace() +# setBarLeadingSpaceUnits($units) +# - Set the leading space (in units) before the first drawn bar graph +# getBarLeadingSpaceUnits() # - Get the space before the first drawn bar graph -# setBarTrailingSpace($pixels) -# - Set the trailing space (in pixels) after the last drawn bar graph -# getBarTrailingSpace() +# setBarTrailingSpaceUnits($units) +# - Set the trailing space (in units) after the last drawn bar graph +# getBarTrailingSpaceUnits() # - Get the space before the last drawn bar graph -# setBarSpace($pixels) -# - Set the space (in pixels) between bar graphs -# getBarSpace() +# setBarSpaceUnits($units) +# - Set the space (in units) between bar graphs +# getBarSpaceUnits() # - Get the space between bar graphs +# setBarWidthUnits($units) +# - Set the width (in units) of bars +# getBarWidthUnits() +# - Get the width of bars +# setShowBarBorder($flag) +# - Set whether a black line is drawn around bars +# getShowBarBorder() +# - Get the bar border flag # ========================= package TWiki::Plugins::ChartPlugin::Chart; use Exporter; use GD; -gdBrushed; gdDashSize; gdMaxColors; gdStyled; gdStyledBrushed; gdTiled; gdTransparent; -gdTinyFont; gdSmallFont; gdMediumBoldFont; gdLargeFont; gdGiantFont; +gdBrushed; +gdDashSize; +gdMaxColors; +gdStyled; +gdStyledBrushed; +gdTiled; +gdTransparent; +gdTinyFont; +gdSmallFont; +gdMediumBoldFont; +gdLargeFont; +gdGiantFont; use POSIX; -@ISA = (); +@ISA = (); @EXPORT = qw( setType getType setTitle getTitle - setXlabel getXlabel - setYlabel getYlabel - setData getData getNumDataSets + setXlabel getXlabel + setYlabel getYlabel + setYlabel1 getYlabel1 + setYlabel2 getYlabel2 + setData getData + setData1 getData1 + setData2 getData2 + getNumDataSets + getNumDataSets1 + getNumDataSets2 setYmin getYmin + setYmin1 getYmin1 + setYmin2 getYmin2 setYmax getYmax + setYmax1 getYmax1 + setYmax2 getYmax2 + setXmin getXmin + setXmax getXmax setSubTypes getSubTypes setXaxis getXaxis setXaxisAngle getXaxisAngle setNumXGrids getNumXGrids setYaxis getYaxis + setYaxis1 getYaxis1 + setYaxis2 getYaxis2 setNumYGrids getNumYGrids + setNumYGrids1 getNumYGrids1 + setNumYGrids2 getNumYGrids2 + setDefNumYGrids getDefNumYGrids setNumYTics getNumYTics + setNumYTics1 getNumYTics1 + setNumYTics2 getNumYTics2 setXgrid getXgrid setYgrid getYgrid setScale getScale @@ -198,260 +300,391 @@ setLineColors getLineColors setColors getColors setGridColor getGridColor + setBorderColor getBorderColor setFileDir getFileDir setFileName getFileName setMargin getMargin setPointSize getPointSize setLineWidth getLineWidth - setBarLeadingSpace getBarLeadingSpace - setBarTrailingSpace getBarTrailingSpace - setBarSpace getBarSpace -); + setBarLeadingSpaceUnits getBarLeadingSpaceUnits + setBarTrailingSpaceUnits getBarTrailingSpaceUnits + setBarSpaceUnits getBarSpaceUnits + setBarWidthUnits getBarWidthUnits + setShowBarBorder getShowBarBorder + ); use strict; -sub new -{ +# Define values for left and right Y axises. Ideally we would use +# 'use constant', but this requires a newer version of Perl which all +# people might not have. +my $LEFT = 1; +my $RIGHT = 2; +my $transparentColorValue = "#010101"; +my $whiteColorValue = "#FFFFFF"; +my $blackColorValue = "#000000"; +my $redColorValue = "#FF0000"; +my %colorCache; + +sub new { my ($class) = @_; my $this = {}; bless $this, $class; $this->setMargin(10); $this->setColors(); - $this->setGridColor("#FFFFFF"); + $this->setGridColor($whiteColorValue); + $this->setBorderColor($whiteColorValue); $this->setLegend(); $this->setYaxis("off"); + $this->setYaxis2("off"); + $this->setNumYTics1(0); + $this->setNumYTics2(0); $this->setXaxis(); $this->setXaxisAngle(0); $this->setNumXGrids(10); $this->setSubTypes(); - $this->setFont("title", GD::gdGiantFont()); # Set title font - $this->setFont("xaxis", GD::gdSmallFont()); # Set X axis font - $this->setFont("yaxis", GD::gdSmallFont()); # Set Y axis font - $this->setFont("xlabel", GD::gdSmallFont());# Set X label font - $this->setFont("ylabel", GD::gdSmallFont());# Set Y label font - $this->setFont("legend", GD::gdSmallFont());# Set legend font - $this->setFont("data", GD::gdSmallFont()); # Set data values font - $this->setNumYGrids(10); - $this->setNumYTics(-1); + $this->setFont("title", GD::gdGiantFont()); # Set title font + $this->setFont("xaxis", GD::gdSmallFont()); # Set X axis font + $this->setFont("yaxis", GD::gdSmallFont()); # Set Y axis font + $this->setFont("xlabel", GD::gdSmallFont()); # Set X label font + $this->setFont("ylabel", GD::gdSmallFont()); # Set Y label font + $this->setFont("legend", GD::gdSmallFont()); # Set legend font + $this->setFont("data", GD::gdSmallFont()); # Set data values font $this->setScale("linear"); + $this->_setData($LEFT, ([])); + $this->_setData($RIGHT, ([])); + $this->setNumDataSets($LEFT, 0); + $this->setNumDataSets($RIGHT, 0); $this->setNumYDigits(0); $this->setNumXDigits(0); return $this; -} +} ## end sub new -sub setType { my ($this, $type) = @_; $$this{TYPE} = $type; } -sub getType { my ($this) = @_; return $$this{TYPE}; } +sub setType {my ($this, $type) = @_; $$this{TYPE} = $type} +sub getType {my ($this) = @_; return $$this{TYPE}} -sub setTitle { my ($this, $title) = @_; $$this{TITLE} = $title; } -sub getTitle { my ($this) = @_; return $$this{TITLE}; } +sub setTitle {my ($this, $title) = @_; $$this{TITLE} = $title} +sub getTitle {my ($this) = @_; return $$this{TITLE}} -sub setXlabel { my ($this, $Xlabel) = @_; $$this{X_LABEL} = $Xlabel; } -sub getXlabel { my ($this) = @_; return $$this{X_LABEL}; } +sub setXlabel {my ($this, $Xlabel) = @_; $$this{X_LABEL} = $Xlabel} +sub getXlabel {my ($this) = @_; return $$this{X_LABEL}} -sub setYlabel { my ($this, $Ylabel) = @_; $$this{Y_LABEL} = $Ylabel; } -sub getYlabel { my ($this) = @_; return $$this{Y_LABEL}; } +sub _setYlabel {my ($this, $yAxisLoc, $Ylabel) = @_; $$this{"Y_LABEL$yAxisLoc"} = $Ylabel} +sub setYlabel {my ($this, $Ylabel) = @_; _setYlabel($this, $LEFT, $Ylabel)} +sub setYlabel1 {my ($this, $Ylabel) = @_; _setYlabel($this, $LEFT, $Ylabel)} +sub setYlabel2 {my ($this, $Ylabel) = @_; _setYlabel($this, $RIGHT, $Ylabel)} -sub setDefaultDataValue { my ($this, $defaultValue) = @_; $$this{DEFAULT_VALUE} = $defaultValue; } -sub getDefaultDataValue { my ($this) = @_; return $$this{DEFAULT_VALUE}; } +sub _getYlabel {my ($this, $yAxisLoc) = @_; return $$this{"Y_LABEL$yAxisLoc"}} +sub getYlabel {my ($this) = @_; return _getYlabel($this, $LEFT)} +sub getYlabel1 {my ($this) = @_; return _getYlabel($this, $LEFT)} +sub getYlabel2 {my ($this) = @_; return _getYlabel($this, $RIGHT)} +sub setDefaultDataValue {my ($this, $defaultValue) = @_; $$this{DEFAULT_VALUE} = $defaultValue} +sub getDefaultDataValue {my ($this) = @_; return $$this{DEFAULT_VALUE}} + # Return the minimum data value seen so the caller can decide if special # action is needed (as would be the case if scale=semilog and yMin <= 0 -sub setData -{ - my ($this, @data) = @_; +sub _setData { + my ($this, $yAxisLoc, @data) = @_; # Create clean data values and calculate the min/max values to be charted. - my $yMin = 9e+40; # Initialize with some very large value. - my $yMax = -9e+40; # Initialize with some very small value. - my $value = 0; - my $maxRow = @data - 1; - my $maxCol = 0; + my $yMinData = 9e+40; # Initialize with some very large value. + my $yMaxData = -9e+40; # Initialize with some very small value. + my $value = 0; + my $maxRow = @data - 1; + my $maxCol = 0; my $defaultDataValue = $this->getDefaultDataValue(); - for my $r ( 0..$maxRow ) { + for my $r (0 .. $maxRow) { $maxCol = @{$data[$r]} - 1; - for my $c (0..$maxCol) { + for my $c (0 .. $maxCol) { $value = $data[$r][$c]; - # Check to see if the value is non-empty. + # Check to see if the value is non-empty. if ($value !~ /^\s*$/) { - # If there is a non-empty value, then look for the number part - if ($value =~ m/([\-]?[0-9.]+[eE]?[+-]?\d*)/) { - $value = $1; - } else { - # If a non-number value in a column (like text for a - # column header) assume the user defined default data - # value - $value = $defaultDataValue; - } - } else { - # Value is empty to use the user defined default value - $value = $defaultDataValue; - } + # If there is a non-empty value, then look for the number part + if ($value =~ m/([\-]?[0-9.]+[eE]?[+-]?\d*)/) { + $value = $1; + } else { + # If a non-number value in a column (like text for a + # column header) assume the user defined default data + # value + $value = $defaultDataValue; + } + } else { + # Value is empty to use the user defined default value + $value = $defaultDataValue; + } $data[$r][$c] = $value; - # Since we allow a default value which can be empty, we only - # want to calculate min/max if the current value is a number. - if ($value =~ m/([\-]?[0-9.]+[eE]?[+-]?\d*)/) { - $yMin = $1 if( $1 < $yMin ); - $yMax = $1 if( $1 > $yMax ); - } - } - } + # Since we allow a default value which can be empty, we only + # want to calculate min/max if the current value is a number. + if ($value =~ m/([\-]?[0-9.]+[eE]?[+-]?\d*)/) { + $yMinData = $1 if ($1 < $yMinData); + $yMaxData = $1 if ($1 > $yMaxData); + } + } ## end for my $c (0 .. $maxCol) + } ## end for my $r (0 .. $maxRow) # Save the min/max data set values. - $this->setYminOfData($yMin); - $this->setYmaxOfData($yMax); + $this->_setYminOfData($yAxisLoc, $yMinData); + $this->_setYmaxOfData($yAxisLoc, $yMaxData); - $$this{DATA} = \@data; - $$this{NUM_DATA_SETS} = @data; - $$this{NUM_DATA_POINTS} = @{$data[0]}; - return $yMin; + $$this{"DATA$yAxisLoc"} = \@data; + $this->setNumDataSets($yAxisLoc, scalar @data); + $this->setNumDataPoints($yAxisLoc, scalar @{$data[0]}); + return $yMinData; +} ## end sub _setData + +sub setData {my ($this, @data) = @_; return _setData($this, $LEFT, @data)} +sub setData1 {my ($this, @data) = @_; return _setData($this, $LEFT, @data)} +sub setData2 {my ($this, @data) = @_; return _setData($this, $RIGHT, @data)} + +sub _getData { + my ($this, $yAxisLoc) = @_; + if (defined($$this{"DATA$yAxisLoc"})) { + return @{$$this{"DATA$yAxisLoc"}}; + } else { + return (); + } } -sub getData { my ($this) = @_; return @{$$this{DATA}}; } -sub getNumDataSets { my ($this) = @_; return $$this{NUM_DATA_SETS}; } -sub getNumDataPoints { my ($this) = @_; return $$this{NUM_DATA_POINTS}; } +sub getData {my ($this) = @_; return _getData($this, $LEFT)} +sub getData1 {my ($this) = @_; return _getData($this, $LEFT)} +sub getData2 {my ($this) = @_; return _getData($this, $RIGHT)} -sub getXminOfData { my ($this) = @_; return $$this{X_DATA_MIN}; } -sub setXminOfData { my ($this, $xmin) = @_; $$this{X_DATA_MIN} = $xmin; } +sub setNumDataSets {my ($this, $yAxisLoc, $num) = @_; $$this{"NUM_DATA_SETS$yAxisLoc"} = $num}; -sub setXmaxOfData { my ($this, $xmax) = @_; $$this{X_DATA_MAX} = $xmax; } -sub getXmaxOfData { my ($this) = @_; return $$this{X_DATA_MAX}; } +sub _getNumDataSets {my ($this, $yAxisLoc) = @_; return $$this{"NUM_DATA_SETS$yAxisLoc"}} +sub getNumDataSets {my ($this) = @_; return _getNumDataSets($this, $LEFT)} +sub getNumDataSets1 {my ($this) = @_; return _getNumDataSets($this, $LEFT)} +sub getNumDataSets2 {my ($this) = @_; return _getNumDataSets($this, $RIGHT)} -sub setYmin { my( $this, $yMin ) = @_; $$this{Y_MIN} = $yMin; } -sub getYmin { my ($this) = @_; return $$this{Y_MIN}; } +sub setNumDataPoints {my ($this, $yAxisLoc, $num) = @_; $$this{"NUM_DATA_POINTS$yAxisLoc"} = $num}; -sub getYminOfData { my ($this) = @_; return $$this{Y_DATA_MIN}; } -sub setYminOfData { my ($this, $ymin) = @_; $$this{Y_DATA_MIN} = $ymin; } +sub _getNumDataPoints {my ($this, $yAxisLoc) = @_; return $$this{"NUM_DATA_POINTS$yAxisLoc"}} +sub getNumDataPoints {my ($this) = @_; return _getNumDataPoints($this, $LEFT)} +sub getNumDataPoints1 {my ($this) = @_; return _getNumDataPoints($this, $LEFT)} +sub getNumDataPoints2 {my ($this) = @_; return _getNumDataPoints($this, $RIGHT)} -sub setYmax { my( $this, $yMax ) = @_; $$this{Y_MAX} = $yMax; } -sub getYmax { my ($this) = @_; return $$this{Y_MAX}; } -sub setYmaxOfData { my ($this, $ymax) = @_; $$this{Y_DATA_MAX} = $ymax; } -sub getYmaxOfData { my ($this) = @_; return $$this{Y_DATA_MAX}; } +sub setXmin {my ($this, $xMin) = @_; $$this{X_MIN} = $xMin} +sub getXmin {my ($this) = @_; return $$this{X_MIN}} -sub setSubTypes { my ($this, @subTypes) = @_; $$this{SUB_TYPES} = \@subTypes; } -sub getSubTypes { my ($this) = @_; return @{$$this{SUB_TYPES}}; } +sub getXminOfData {my ($this) = @_; return $$this{X_DATA_MIN}} +sub setXminOfData {my ($this, $xmin) = @_; $$this{X_DATA_MIN} = $xmin} -sub setXaxis { my ($this, @xAxis) = @_; $$this{X_AXIS} = \@xAxis; } -sub getXaxis { my ($this) = @_; return @{$$this{X_AXIS}}; } +sub setXmax {my ($this, $xMax) = @_; $$this{X_MAX} = $xMax} +sub getXmax {my ($this) = @_; return $$this{X_MAX}} -sub setXaxisAngle { my ($this, $angle) = @_; $$this{X_AXIS_ANGLE} = $angle; } -sub getXaxisAngle { my ($this) = @_; return $$this{X_AXIS_ANGLE}; } +sub setXmaxOfData {my ($this, $xmax) = @_; $$this{X_DATA_MAX} = $xmax} +sub getXmaxOfData {my ($this) = @_; return $$this{X_DATA_MAX}} -sub setYaxis { my ($this, $yAxis) = @_; $$this{Y_AXIS} = $yAxis; } -sub getYaxis { my ($this) = @_; return $$this{Y_AXIS}; } +sub _setYmin {my ($this, $yAxisLoc, $yMin) = @_; $$this{"Y_MIN$yAxisLoc"} = $yMin} +sub setYmin {my ($this, $yMin) = @_; _setYmin($this, $LEFT, $yMin)} +sub setYmin1 {my ($this, $yMin) = @_; _setYmin($this, $LEFT, $yMin)} +sub setYmin2 {my ($this, $yMin) = @_; _setYmin($this, $RIGHT, $yMin)} -sub setNumYGrids { my ($this, $numYGrids) = @_; $$this{NUM_Y_GRIDS} = $numYGrids; } -sub getNumYGrids { my ($this) = @_; return $$this{NUM_Y_GRIDS}; } +sub _getYmin {my ($this, $yAxisLoc) = @_; return $$this{"Y_MIN$yAxisLoc"}} +sub getYmin {my ($this) = @_; return _getYmin($this, $LEFT)} +sub getYmin1 {my ($this) = @_; return _getYmin($this, $LEFT)} +sub getYmin2 {my ($this) = @_; return _getYmin($this, $RIGHT)} -sub setNumYTics { my ($this, $numYTics) = @_; $$this{NUM_Y_TICS} = $numYTics; } -sub getNumYTics { my ($this) = @_; return $$this{NUM_Y_TICS}; } +sub _setYminOfData {my ($this, $yAxisLoc, $ymin) = @_; $$this{"Y_DATA_MIN$yAxisLoc"} = $ymin} +sub setYminOfData {my ($this, $ymin) = @_; _setYminOfData($this, $LEFT, $ymin)} +sub setYminOfData1 {my ($this, $ymin) = @_; _setYminOfData($this, $LEFT, $ymin)} +sub setYminOfData2 {my ($this, $ymin) = @_; _setYminOfData($this, $RIGHT, $ymin)} -sub setNumXGrids { my ($this, $numXGrids) = @_; $$this{NUM_X_GRIDS} = $numXGrids; } -sub getNumXGrids { my ($this) = @_; return $$this{NUM_X_GRIDS}; } +sub _getYminOfData {my ($this, $yAxisLoc) = @_; return $$this{"Y_DATA_MIN$yAxisLoc"}} +sub getYminOfData {my ($this) = @_; return _getYminOfData($this, $LEFT)} +sub getYminOfData1 {my ($this) = @_; return _getYminOfData($this, $LEFT)} +sub getYminOfData2 {my ($this) = @_; return _getYminOfData($this, $RIGHT)} -sub setXgrid { my ($this, $xGrid) = @_; $$this{X_GRID} = $xGrid; } -sub getXgrid { my ($this) = @_; return $$this{X_GRID}; } +sub _setYmax {my ($this, $yAxisLoc, $yMax) = @_; $$this{"Y_MAX$yAxisLoc"} = $yMax} +sub setYmax {my ($this, $yMax) = @_; _setYmax($this, $LEFT, $yMax)} +sub setYmax1 {my ($this, $yMax) = @_; _setYmax($this, $LEFT, $yMax)} +sub setYmax2 {my ($this, $yMax) = @_; _setYmax($this, $RIGHT, $yMax)} -sub setYgrid { my ($this, $yGrid) = @_; $$this{Y_GRID} = $yGrid; } -sub getYgrid { my ($this) = @_; return $$this{Y_GRID}; } +sub _getYmax {my ($this, $yAxisLoc) = @_; return $$this{"Y_MAX$yAxisLoc"}} +sub getYmax {my ($this) = @_; return _getYmax($this, $LEFT)} +sub getYmax1 {my ($this) = @_; return _getYmax($this, $LEFT)} +sub getYmax2 {my ($this) = @_; return _getYmax($this, $RIGHT)} -sub setScale { my ($this, $scale) = @_; $$this{SCALE} = $scale; } -sub getScale { my ($this) = @_; return $$this{SCALE}; } +sub _setYmaxOfData {my ($this, $yAxisLoc, $ymax) = @_; $$this{"Y_DATA_MAX$yAxisLoc"} = $ymax} +sub setYmaxOfData {my ($this, $ymax) = @_; _setYmaxOfData($this, $LEFT, $ymax)} +sub setYmaxOfData1 {my ($this, $ymax) = @_; _setYmaxOfData($this, $LEFT, $ymax)} +sub setYmaxOfData2 {my ($this, $ymax) = @_; _setYmaxOfData($this, $RIGHT, $ymax)} -sub setDataLabels { my ($this, @dataLabels) = @_; $$this{DATA_LABELS} = \@dataLabels; } -sub getDataLabels { my ($this) = @_; return @{$$this{DATA_LABELS}}; } +sub _getYmaxOfData {my ($this, $yAxisLoc) = @_; return $$this{"Y_DATA_MAX$yAxisLoc"}} +sub getYmaxOfData {my ($this) = @_; return _getYmaxOfData($this, $LEFT)} +sub getYmaxOfData1 {my ($this) = @_; return _getYmaxOfData($this, $LEFT)} +sub getYmaxOfData2 {my ($this) = @_; return _getYmaxOfData($this, $RIGHT)} -sub setLegend { my ($this, @legend) = @_; $$this{LEGEND} = \@legend; } -sub getLegend { my ($this) = @_; return @{$$this{LEGEND}}; } +sub setSubTypes {my ($this, @subTypes) = @_; $$this{SUB_TYPES} = \@subTypes} +sub getSubTypes {my ($this) = @_; return @{$$this{SUB_TYPES}}} -sub setImageWidth { my ($this, $imageWidth) = @_; $$this{IMAGE_WIDTH} = _getInt( $imageWidth ); } -sub getImageWidth { my ($this) = @_; return $$this{IMAGE_WIDTH}; } +sub setXaxis {my ($this, @xAxis) = @_; $$this{X_AXIS} = \@xAxis} +sub getXaxis {my ($this) = @_; return @{$$this{X_AXIS}}} -sub setImageHeight { my ($this, $imageHeight) = @_; $$this{IMAGE_HEIGHT} = _getInt( $imageHeight ); } -sub getImageHeight { my ($this) = @_; return $$this{IMAGE_HEIGHT}; } +sub setXaxisAngle {my ($this, $angle) = @_; $$this{X_AXIS_ANGLE} = $angle} +sub getXaxisAngle {my ($this) = @_; return $$this{X_AXIS_ANGLE}} -sub setAreaColors -{ +sub _setYaxis {my ($this, $yAxisLoc, $yAxis) = @_; $$this{"Y_AXIS$yAxisLoc"} = $yAxis} +sub setYaxis {my ($this, $yAxis) = @_; _setYaxis($this, $LEFT, $yAxis)} +sub setYaxis1 {my ($this, $yAxis) = @_; _setYaxis($this, $LEFT, $yAxis)} +sub setYaxis2 {my ($this, $yAxis) = @_; _setYaxis($this, $RIGHT, $yAxis)} + +sub _getYaxis {my ($this, $yAxisLoc) = @_; return $$this{"Y_AXIS$yAxisLoc"}} +sub getYaxis {my ($this) = @_; return _getYaxis($this, $LEFT)} +sub getYaxis1 {my ($this) = @_; return _getYaxis($this, $LEFT)} +sub getYaxis2 {my ($this) = @_; return _getYaxis($this, $RIGHT)} + +sub setDefNumYGrids {my ($this, $numYGrids) = @_; $$this{DEFAULT_NUM_Y_GRIDS} = $numYGrids} +sub getDefNumYGrids {my ($this) = @_; return $$this{DEFAULT_NUM_Y_GRIDS}} + +sub _setNumYGrids {my ($this, $yAxisLoc, $numYGrids) = @_; $$this{"NUM_Y_GRIDS$yAxisLoc"} = $numYGrids} +sub setNumYGrids {my ($this, $numYGrids) = @_; _setNumYGrids($this, $LEFT, $numYGrids)} +sub setNumYGrids1 {my ($this, $numYGrids) = @_; _setNumYGrids($this, $LEFT, $numYGrids)} +sub setNumYGrids2 {my ($this, $numYGrids) = @_; _setNumYGrids($this, $RIGHT, $numYGrids)} + +sub _getNumYGrids {my ($this, $yAxisLoc) = @_; return $$this{"NUM_Y_GRIDS$yAxisLoc"}} +sub getNumYGrids {my ($this) = @_; return _getNumYGrids($this, $LEFT)} +sub getNumYGrids1 {my ($this) = @_; return _getNumYGrids($this, $LEFT)} +sub getNumYGrids2 {my ($this) = @_; return _getNumYGrids($this, $RIGHT)} + +sub _setNumYTics {my ($this, $yAxisLoc, $numYTics) = @_; $$this{"NUM_Y_TICS$yAxisLoc"} = $numYTics} +sub setNumYTics {my ($this, $numYTics) = @_; _setNumYTics($this, $LEFT, $numYTics)} +sub setNumYTics1 {my ($this, $numYTics) = @_; _setNumYTics($this, $LEFT, $numYTics)} +sub setNumYTics2 {my ($this, $numYTics) = @_; _setNumYTics($this, $RIGHT, $numYTics)} + +sub _getNumYTics {my ($this, $yAxisLoc) = @_; return $$this{"NUM_Y_TICS$yAxisLoc"}} +sub getNumYTics {my ($this) = @_; return _getNumYTics($this, $LEFT)} +sub getNumYTics1 {my ($this) = @_; return _getNumYTics($this, $LEFT)} +sub getNumYTics2 {my ($this) = @_; return _getNumYTics($this, $RIGHT)} + +sub setNumXGrids {my ($this, $numXGrids) = @_; $$this{NUM_X_GRIDS} = $numXGrids} +sub getNumXGrids {my ($this) = @_; return $$this{NUM_X_GRIDS}} + +sub setXgrid {my ($this, $xGrid) = @_; $$this{X_GRID} = $xGrid} +sub getXgrid {my ($this) = @_; return $$this{X_GRID}} + +sub setYgrid {my ($this, $yGrid) = @_; $$this{Y_GRID} = $yGrid} +sub getYgrid {my ($this) = @_; return $$this{Y_GRID}} + +sub setScale {my ($this, $scale) = @_; $$this{SCALE} = $scale} +sub getScale {my ($this) = @_; return $$this{SCALE}} + +sub setDataLabels {my ($this, @dataLabels) = @_; $$this{DATA_LABELS} = \@dataLabels} +sub getDataLabels {my ($this) = @_; return @{$$this{DATA_LABELS}}} + +sub setLegend {my ($this, @legend) = @_; $$this{LEGEND} = \@legend} +sub getLegend {my ($this) = @_; return @{$$this{LEGEND}}} + +sub setImageWidth {my ($this, $imageWidth) = @_; $$this{IMAGE_WIDTH} = _getInt($imageWidth)} +sub getImageWidth {my ($this) = @_; return $$this{IMAGE_WIDTH}} + +sub setImageHeight {my ($this, $imageHeight) = @_; $$this{IMAGE_HEIGHT} = _getInt($imageHeight)} +sub getImageHeight {my ($this) = @_; return $$this{IMAGE_HEIGHT}} + +sub setAreaColors { my ($this, @AreaColors) = @_; - $$this{AREA_COLORS} = \@AreaColors; + $$this{AREA_COLORS} = \@AreaColors; $$this{NEXT_AREA_COLOR} = 0; } -sub getAreaColors { my ($this) = @_; return @{$$this{AREA_COLORS}}; } +sub getAreaColors {my ($this) = @_; return @{$$this{AREA_COLORS}}} + sub getAreaColorByIndex { my ($this, $index) = @_; - my @colors = $this->getAreaColors(); + my @colors = $this->getAreaColors(); my $nextColor = $colors[$index % @colors]; return $nextColor; } -sub setLineColors -{ +sub setLineColors { my ($this, @LineColors) = @_; - $$this{LINE_COLORS} = \@LineColors; + $$this{LINE_COLORS} = \@LineColors; $$this{NEXT_LINE_COLOR} = 0; } -sub getLineColors { my ($this) = @_; return @{$$this{LINE_COLORS}}; } +sub getLineColors {my ($this) = @_; return @{$$this{LINE_COLORS}}} + sub getLineColorByIndex { my ($this, $index) = @_; - my @colors = $this->getLineColors(); + my @colors = $this->getLineColors(); my $nextColor = $colors[$index % @colors]; return $nextColor; } -sub setColors { my ($this, @Colors) = @_; $$this{COLORS} = \@Colors; } -sub getColors { my ($this) = @_; return @{$$this{COLORS}}; } +sub setColors {my ($this, @Colors) = @_; $$this{COLORS} = \@Colors} +sub getColors {my ($this) = @_; return @{$$this{COLORS}}} -sub setGridColor { my ($this, @gridColor) = @_; $$this{GRID_COLOR} = \@gridColor; } -sub getGridColor { my ($this) = @_; return @{$$this{GRID_COLOR}}; } +sub setGridColor {my ($this, @gridColor) = @_; $$this{GRID_COLOR} = \@gridColor} +sub getGridColor {my ($this) = @_; return @{$$this{GRID_COLOR}}} -sub setFileDir { my ($this, $dir) = @_; $$this{FILE_DIR} = $dir; } -sub getFileDir { my ($this) = @_; return $$this{FILE_DIR}; } +sub setBorderColor {my ($this, $borderColor) = @_; $$this{BORDER_COLOR} = $borderColor} +sub getBorderColor {my ($this) = @_; return $$this{BORDER_COLOR}} -sub setFileName { my ($this, $name) = @_; $$this{FILE_NAME} = $name; } -sub getFileName { my ($this) = @_; return $$this{FILE_NAME}; } +sub setFileDir {my ($this, $dir) = @_; $$this{FILE_DIR} = $dir} +sub getFileDir {my ($this) = @_; return $$this{FILE_DIR}} -sub setMargin { my ($this, $margin) = @_; $$this{MARGIN} = $margin; } -sub getMargin { my ($this) = @_; return $$this{MARGIN}; } +sub setFileName {my ($this, $name) = @_; $$this{FILE_NAME} = $name} +sub getFileName {my ($this) = @_; return $$this{FILE_NAME}} -sub setImage { my ($this, $image) = @_; $$this{IMAGE} = $image; } -sub getImage { my ($this) = @_; return $$this{IMAGE}; } +sub setMargin {my ($this, $margin) = @_; $$this{MARGIN} = $margin} +sub getMargin {my ($this) = @_; return $$this{MARGIN}} -sub setFont -{ +sub setImage { + my ($this, $image) = @_; + $$this{IMAGE} = $image; + undef %colorCache; +} +sub getImage {my ($this) = @_; return $$this{IMAGE}} + +sub setFont { my ($this, $type, $font) = @_; - $$this{"FONT_$type"} = $font; - $$this{"FONT_WIDTH_$type"} = $font->width; + $$this{"FONT_$type"} = $font; + $$this{"FONT_WIDTH_$type"} = $font->width; $$this{"FONT_HEIGHT_$type"} = $font->height; } -sub getFont { my ($this, $type) = @_; return $$this{"FONT_$type"}; } -sub getFontWidth { my ($this, $type) = @_; return $$this{"FONT_WIDTH_$type"}; } -sub getFontHeight { my ($this, $type) = @_; return $$this{"FONT_HEIGHT_$type"}; } +sub getFont {my ($this, $type) = @_; return $$this{"FONT_$type"}} +sub getFontWidth {my ($this, $type) = @_; return $$this{"FONT_WIDTH_$type"}} +sub getFontHeight {my ($this, $type) = @_; return $$this{"FONT_HEIGHT_$type"}} -sub setBGcolor { my ($this, @bgcolor) = @_; $$this{BGCOLOR} = \@bgcolor; } -sub getBGcolor { my ($this) = @_; return @{$$this{BGCOLOR}}; } +sub setBGcolor {my ($this, @bgcolor) = @_; $$this{BGCOLOR} = \@bgcolor} +sub getBGcolor {my ($this) = @_; return @{$$this{BGCOLOR}}} -sub setNumYDigits { my ($this, $numDigits) = @_; $$this{NUM_Y_DIGITS} = $numDigits; } -sub getNumYDigits { my ($this) = @_; return $$this{NUM_Y_DIGITS}; } -sub setNumXDigits { my ($this, $numDigits) = @_; $$this{NUM_X_DIGITS} = $numDigits; } -sub getNumXDigits { my ($this) = @_; return $$this{NUM_X_DIGITS}; } +sub _setNumYDigits {my ($this, $yAxisLoc, $numDigits) = @_; $$this{"NUM_Y_DIGITS$yAxisLoc"} = $numDigits} +sub setNumYDigits {my ($this, $numDigits) = @_; _setNumYDigits($this, $LEFT, $numDigits)} +sub setNumYDigits1 {my ($this, $numDigits) = @_; _setNumYDigits($this, $LEFT, $numDigits)} +sub setNumYDigits2 {my ($this, $numDigits) = @_; _setNumYDigits($this, $RIGHT, $numDigits)} -sub setPointSize { my ($this, $pixels) = @_; $$this{POINT_SIZE} = $pixels; } -sub getPointSize { my ($this) = @_; return $$this{POINT_SIZE}; } -sub setLineWidth { my ($this, $pixels) = @_; $$this{LINE_WIDTH} = $pixels; } -sub getLineWidth { my ($this) = @_; return $$this{LINE_WIDTH}; } +sub _getNumYDigits {my ($this, $yAxisLoc) = @_; return $$this{"NUM_Y_DIGITS$yAxisLoc"}} +sub getNumYDigits {my ($this) = @_; return _getNumYDigits($this, $LEFT)} +sub getNumYDigits1 {my ($this) = @_; return _getNumYDigits($this, $LEFT)} +sub getNumYDigits2 {my ($this) = @_; return _getNumYDigits($this, $RIGHT)} -sub setBarLeadingSpace { my ($this, $pixels) = @_; $$this{BAR_LEADING_SPACE} = $pixels; } -sub getBarLeadingSpace { my ($this) = @_; return $$this{BAR_LEADING_SPACE}; } -sub setBarTrailingSpace { my ($this, $pixels) = @_; $$this{BAR_TRAILING_SPACE} = $pixels; } -sub getBarTrailingSpace { my ($this) = @_; return $$this{BAR_TRAILING_SPACE}; } -sub setBarSpace { my ($this, $pixels) = @_; $$this{BAR_SPACE} = $pixels; } -sub getBarSpace { my ($this) = @_; return $$this{BAR_SPACE}; } +sub setNumXDigits {my ($this, $numDigits) = @_; $$this{NUM_X_DIGITS} = $numDigits} +sub getNumXDigits {my ($this) = @_; return $$this{NUM_X_DIGITS}} -sub computeFinalColors -{ - my ($this) = @_; - my $numDataSets = $this->getNumDataSets(); - my @subTypes = $this->getSubTypes(); - my $im = $this->getImage(); +sub setPointSize {my ($this, $pixels) = @_; $$this{POINT_SIZE} = $pixels} +sub getPointSize {my ($this) = @_; return $$this{POINT_SIZE}} +sub setLineWidth {my ($this, $pixels) = @_; $$this{LINE_WIDTH} = $pixels} +sub getLineWidth {my ($this) = @_; return $$this{LINE_WIDTH}} +sub setBarLeadingSpaceUnits {my ($this, $units) = @_; $$this{BAR_LEADING_SPACE_UNITS} = $units} +sub getBarLeadingSpaceUnits {my ($this) = @_; return $$this{BAR_LEADING_SPACE_UNITS}} +sub setBarTrailingSpaceUnits {my ($this, $units) = @_; $$this{BAR_TRAILING_SPACE_UNITS} = $units} +sub getBarTrailingSpaceUnits {my ($this) = @_; return $$this{BAR_TRAILING_SPACE_UNITS}} +sub setBarSpaceUnits {my ($this, $units) = @_; $$this{BAR_SPACE_UNITS} = $units} +sub getBarSpaceUnits {my ($this) = @_; return $$this{BAR_SPACE_UNITS}} +sub setBarWidthUnits {my ($this, $units) = @_; $$this{BAR_WIDTH_UNITS} = $units} +sub getBarWidthUnits {my ($this) = @_; return $$this{BAR_WIDTH_UNITS}} +sub setShowBarBorder {my ($this, $flag) = @_; $$this{BAR_BORDER_FLAG} = $flag} +sub getShowBarBorder {my ($this) = @_; return $$this{BAR_BORDER_FLAG}} + +# Make sure colors are defined for each data set on each Y axis +sub computeFinalColors { + my ($this) = @_; + + my $numDataSets = $this->getNumDataSets1(); + $numDataSets += $this->getNumDataSets2(); + my @subTypes = $this->getSubTypes(); + my $im = $this->getImage(); + # Calculate the colors that will be needed. # If 'type' = line or area then call getColors(). If no colors # defined, then default to getLineColors() for lines and @@ -460,117 +693,183 @@ # defined, then call getDataType() to determine if the data sets are # specified as lines or areas and get the next available color from # getLineColors() and/or getAreaColors(). - my @colors = $this->getColors(); - my @lineColors = $this->getLineColors(); - my @areaColors = $this->getAreaColors(); - my @chartColors = (); # Actual colors used for each line/area + my @colors = $this->getColors(); + my @lineColors = $this->getLineColors(); + my @areaColors = $this->getAreaColors(); + my @chartColors = (); # Actual colors used for each line/area if (@colors) { - # User defined colors. Reuse colors if there are more data sets - # than colors. - my $numColors = @colors; - for (1..POSIX::ceil($numDataSets / $numColors)) { - push (@chartColors, @colors); - } + # User defined colors. Reuse colors if there are more data sets + # than colors. + my $numColors = @colors; + for (1 .. POSIX::ceil($numDataSets / $numColors)) { + push(@chartColors, @colors); + } } else { - # No user defined colors so use the defaults. This can be a bit - # tricky since depending on what 'type' the data is will determine - # where we get the next color. - my $index = 0; - for my $subType (@subTypes) { - my $color; - if ($subType =~ /(area|bar)/ ) { - $color = $this->getAreaColorByIndex($index); - } else { - $color = $this->getLineColorByIndex($index); - } - push (@chartColors, $color); - $index++; - } - } + # No user defined colors so use the defaults. This can be a bit + # tricky since depending on what 'type' the data is will determine + # where we get the next color. + my $index = 0; + for my $subType (@subTypes) { + my $color; + if ($subType =~ /(area|bar)/) { + $color = $this->getAreaColorByIndex($index); + } else { + $color = $this->getLineColorByIndex($index); + } + push(@chartColors, $color); + $index++; + } + } ## end else [ if (@colors) ] + # Walk through each color and allocate it in the GD. my @allocatedColors; for my $color (@chartColors) { - push (@allocatedColors, $im->colorAllocate(_convert_color($color))); + push(@allocatedColors, $this->allocateColor($color)); } return @allocatedColors; -} +} ## end sub computeFinalColors -# Calculate the 'types' for each of the data sets. If getType() is 'area', -# then artificially fill in subTypes for each data set to match that type. -# If type is 'combo', then subTypes should have been specified by the user. -# If not, then assume that all but the last data set are 'area' and the -# last is 'line'. +# Calculate the 'types' for each of the data sets on each Y axis. If +# getType() is 'area', then artificially fill in subTypes for each data set +# to match that type. If type is 'combo', then subTypes should have been +# specified by the user. If not, then assume that all but the last data +# set are 'area' and the last is 'line'. sub computeSubTypes { - my ($this) = @_; - my $numDataSets = $this->getNumDataSets(); - my $type = $this->getType(); - my @subTypes = (); + my ($this) = @_; + my $numDataSets = $this->getNumDataSets1(); + $numDataSets += $this->getNumDataSets2(); + my $type = $this->getType(); + my @subTypes; # Deal with types that don't allow subtypes. In this case force all # subtypes to be the same as the type. - if (($type eq "area") || ($type eq "bar") ) { - for (1..$numDataSets) { - push (@subTypes, $type); - } - $this->setSubTypes(@subTypes); + if (($type eq "area") || ($type eq "bar")) { + for (1 .. $numDataSets) { + push(@subTypes, $type); + } + $this->setSubTypes(@subTypes); } else { - # If a user specified subtype, then reuse user's info over and - # over again if there are more data sets than subtypes specified. - # If no subtype, then assume all but the last data set are 'area' - # and the last 'line'. - my @userSubTypes = $this->getSubTypes(); - if (@userSubTypes) { - my $numUserSubTypes = @userSubTypes; - for (1..POSIX::ceil($numDataSets / $numUserSubTypes)) { - push (@subTypes, @userSubTypes); - } - } else { - # If no 'subtype' specified and type is 'combo' or 'scatter', - # assume all 'area' except the last which is 'line', otherwise - # assume a subtype of 'line' for type 'line'. - if ($type eq "line") { - for my $y (1..$numDataSets) { - push (@subTypes, "line"); - } - } elsif ($type eq "scatter") { - for my $y (1..$numDataSets) { - push (@subTypes, "point"); - } - } else { - for my $y (1..$numDataSets-1) { - push (@subTypes, "area"); - } - push (@subTypes, "line"); - } - } - } - # Set the subTypes since they will have changed and other calculations - # need this information. + # If a user specified subtype, then reuse user's info over and + # over again if there are more data sets than subtypes specified. + # If no subtype, then assume all but the last data set are 'area' + # and the last 'line'. + my @userSubTypes = $this->getSubTypes(); + if (@userSubTypes) { + my $numUserSubTypes = @userSubTypes; + for (1 .. POSIX::ceil($numDataSets / $numUserSubTypes)) { + push(@subTypes, @userSubTypes); + } + } else { + # If no 'subtype' specified and type is 'combo' or 'scatter', + # assume all 'area' except the last which is 'line', otherwise + # assume a subtype of 'line' for type 'line'. + if ($type eq "line") { + for my $y (1 .. $numDataSets) { + push(@subTypes, "line"); + } + } elsif ($type eq "scatter") { + for my $y (1 .. $numDataSets) { + push(@subTypes, "point"); + } + } else { + for my $y (1 .. $numDataSets - 1) { + push(@subTypes, "area"); + } + push(@subTypes, "line"); + } + } ## end else [ if (@userSubTypes) ] + } ## end else [ if (($type eq "area") ...)] + # Set the subTypes since they will have changed and other calculations + # need this information. $this->setSubTypes(@subTypes); return @subTypes; +} ## end sub computeSubTypes + +# This routine is used to 'cache' colors so colors are reused instead of +# replicated. +sub allocateColor { + my ($this, $color) = @_; + my $im = $this->getImage(); + if (! defined($colorCache{$color})) { + $colorCache{$color} = $im->colorAllocate(_convert_color($color)); + } + return $colorCache{$color}; } +# This places and error inside of an image. +sub makeError { + my ($this, $msg) = @_; + my $imageWidth = $this->getImageWidth(); + my $imageHeight = $this->getImageHeight(); + my $im = new GD::Image($imageWidth, $imageHeight); + $this->setImage($im); + + my $initialBGcolor = $this->allocateColor($whiteColorValue); + my $red = $this->allocateColor($redColorValue); + my $font = $this->getFont("title"); + my $lineSpacing = 2; + + # Start with a totally white background + $im->filledRectangle(0, 0, $imageWidth - 1, $imageHeight - 1, $initialBGcolor); + $im->rectangle(0, 0, $imageWidth - 1, $imageHeight - 1, $red); + my $maxChars = int($imageWidth / $this->getFontWidth("title")) - 1; + $Text::Wrap::columns = $maxChars; + my $numLines = my @lines = split(/\n/, Text::Wrap::wrap("", "", $msg)); + + my $maxLines = $imageHeight / ($this->getFontHeight("title") + $lineSpacing); + my $y = int(($imageHeight / 2) - ($numLines / 2 * ($this->getFontHeight("title") + $lineSpacing))); + foreach my $line (@lines) { + $im->string($font, 10, $y, $line, $red); + $y += $this->getFontHeight("title") + $lineSpacing; + } + #$xLL += $this->getFontHeight("ylabel") + 10; + #$xUR -= $this->getFontWidth("yaxis") * $maxLength; + my $dir = $this->getFileDir(); + my $filename = $this->getFileName(); + umask(002); + open(IMAGE, ">$dir/$filename") or return "Can't create file '$dir/$filename: $!"; + binmode IMAGE; + if ($GD::VERSION > 1.19) { + print IMAGE $im->png; + } else { + print IMAGE $im->gif; + } + close IMAGE; + return undef; +} + # The main guts of this file. This routine takes all the information # specified in the Chart object and constructs a chart based on all of the # information contained in the object. sub makeChart { - my ($this) = @_; - my $imageWidth = $this->getImageWidth(); + my ($this) = @_; + my $imageWidth = $this->getImageWidth(); my $imageHeight = $this->getImageHeight(); # Create empty image to get filled in later. my $im = new GD::Image($imageWidth, $imageHeight); $this->setImage($im); + # Create the background color. If not defined, default to white, else + # use the user specified value. + # Define some commonly used colors - my $defaultBGcolorText = "ffffff"; - my $defaultBGcolor = $im->colorAllocate(_convert_color($defaultBGcolorText)); - my $black = $im->colorAllocate(0,0,0); # black + my ($outsideBGColor, $insideBGColor, $boxBGColor) = $this->getBGcolor(); + my $initialBGcolor = $this->allocateColor($whiteColorValue); + my $transparentColor = $this->allocateColor("transparent"); + my $black = $this->allocateColor($blackColorValue); + my $borderColorText = $this->getBorderColor(); + my $borderColor; + if ($borderColorText eq "transparent") { + $borderColor = undef; + } else { + $borderColor = $this->allocateColor($borderColorText); + } - # Create the background color. If not defined, default to white, else - # use the user specified value. - my @bgcolor = $this->getBGcolor(); + # Define the transparent color so users can pick a transparent background. + $im->transparent($transparentColor); + # Start with a totally white background - $im->filledRectangle(0, 0, $imageWidth - 1, $imageHeight - 1, $defaultBGcolor); + $im->filledRectangle(0, 0, $imageWidth - 1, $imageHeight - 1, $initialBGcolor); # Calculate the 'types' for each of the data sets. my @subTypes = $this->computeSubTypes(); @@ -584,7 +883,7 @@ $scatterChart = 1 if ($type eq "scatter"); # Get the number of pixels width/height of drawn data points. - my $pointSize = $this->getPointSize; + my $pointSize = $this->getPointSize; my $pointSizeHalf = $pointSize / 2; # Get the number of pixels width lines are to be drawn with @@ -593,11 +892,19 @@ # Get the Y axis scale to use my $scale = $this->getScale(); + my @yAxisLocs = ($LEFT); # Get the data and info about the data. - my @data = $this->getData(); - my $numDataSets = $this->getNumDataSets(); - my $numDataPoints = $this->getNumDataPoints(); - return "Error: Number of data points needs to be > 1" if ($numDataPoints <= 1); + my @data1 = $this->getData1(); + my @data2 = $this->getData2(); + my $numDataSets1 = $this->getNumDataSets1(); + my $numDataSets2 = $this->getNumDataSets2(); + my $isData2Data = $numDataSets2; + push (@yAxisLocs, $RIGHT) if ($isData2Data); + my @numDataPoints; + $numDataPoints[$LEFT] = $this->getNumDataPoints1(); + $numDataPoints[$RIGHT] = $this->getNumDataPoints2(); + return "Error: Number of data points needs to be > 1" if ($numDataPoints[$LEFT] <= 1); + return "Error: Number of data points for data2 needs to be > 1" if ($isData2Data && $numDataPoints[$RIGHT] <= 1); # Calculate the colors that will be needed for the various lines and # areas figuring out which color is needed when and also dealing with @@ -611,22 +918,22 @@ my %gridColors; # Get unique list of colors. foreach my $color ($this->getGridColor()) { - $firstColor = $color if (! defined $firstColor); - $gridColors{$color} = 1; + $firstColor = $color if (! defined $firstColor); + $gridColors{$color} = 1; } # Allocate color for each unique grid color for my $color (keys %gridColors) { - next if ($color eq "transparent"); - $gridColors{$color} = $im->colorAllocate(_convert_color($color)); + next if ($color eq "transparent"); + $gridColors{$color} = $this->allocateColor($color); } my @gridColors; # Now define array of colors for grid foreach my $color ($this->getGridColor()) { - if ($color eq "transparent") { - push(@gridColors, gdTransparent) - } else { - push(@gridColors, $gridColors{$color}) - } + if ($color eq "transparent") { + push(@gridColors, gdTransparent); + } else { + push(@gridColors, $gridColors{$color}); + } } $im->setStyle(@gridColors); # Get the first color for solid grid lines @@ -637,102 +944,149 @@ # graphic image. Depending on X/Y labels, titles, legends, etc. this # will change in the code below. my $margin = $this->getMargin(); - my $xLL = $margin; - my $yLL = $imageHeight - 1 - $margin; - my $xUR = $imageWidth - 1 - $margin; - my $yUR = $margin; + my $xLL = $margin; + my $yLL = $imageHeight - 1 - $margin; + my $xUR = $imageWidth - 1 - $margin; + my $yUR = $margin; - # Calculate how much space will be needed for the Y label (if specified). - my $yLabel = $this->getYlabel(); - if (defined $yLabel) { - # Add space for the label as well as some space between the label - # and the Y Axis labels or the left side of the chart. - $xLL += $this->getFontHeight("ylabel") + 10; + # Calculate how much space will be needed for the Y label1 (if specified). + my $yLabel1 = $this->getYlabel1(); + if (defined $yLabel1) { + # Allocate space for label1 as well as some space between the label + # and the Y Axis labels or the left side of the chart. + $xLL += $this->getFontHeight("ylabel") + 10; } + # Calculate how much space will be needed for the Y label2 (if specified). + my $yLabel2 = $this->getYlabel2(); + if (defined $yLabel2) { + # Allocate space for label2 as well as some space between the label + # and the Y Axis labels or the side of the chart. + $xUR -= $this->getFontHeight("ylabel"); + } - # Get various bits of info concerning the chart. - my $yAxisMin = $this->getYmin(); - my $yAxisMax = $this->getYmax(); - my $yAxisDecimalDigits = 0; - # If the user has not specified either of ymin or ymax, then compute - # some reasonable values based on the min/max of the actual data - # itself. Also compute the number of significant digits in the numbers - # to aid in knowing how to format the numbers when printing as well as - # the new number of Y grids. - my $numYGrids = $this->getNumYGrids(); - if (! defined($yAxisMin) && ! defined($yAxisMax)) { - if ($scale eq "semilog") { - $this->computeYMinMaxLog(); - } else { - my ($newYmin, $newYmax, $newNumYGrids, $newNumYDecimalDigits) = $this->computeLinearMinMax($this->getYminOfData(), $this->getYmaxOfData(), $numYGrids); - $this->setYminOfData($newYmin); - $this->setYmaxOfData($newYmax); - $this->setNumYGrids($numYGrids = $newNumYGrids); - $this->setNumYDigits($newNumYDecimalDigits); + my @yAxisMin; + my @yAxisMax; + my @numYGrids; + # Foreach data set, see if we need to compute ymin and ymax. + foreach my $yAxisLoc (@yAxisLocs) { + # Get the users specified ymin, ymax, and numygrids + $yAxisMin[$yAxisLoc] = $this->_getYmin($yAxisLoc); + $yAxisMax[$yAxisLoc] = $this->_getYmax($yAxisLoc); + $numYGrids[$yAxisLoc] = $this->_getNumYGrids($yAxisLoc); + # If ymin not set, either as a CHART default or a per CHART value, + # compute a good default. + if (! defined($yAxisMin[$yAxisLoc])) { + my $yMinData = $this->_getYminOfData($yAxisLoc); + if ($scale eq "semilog") { + my $yMinLog10 = log10($yMinData); + $yAxisMin[$yAxisLoc] = pow(10.0, floor($yMinLog10)); + } else { + $yAxisMin[$yAxisLoc] = computeFloor($yMinData); + } } - } else { - # At least one of ymin or ymax specified by the user. - # If scale=semilog, we still auto compute min/max while if - # scale=linear we use the users set values. + # If ymax not set, either as a CHART default or a per CHART value, + # compute a good default. + if (! defined($yAxisMax[$yAxisLoc])) { + my $yMaxData = $this->_getYmaxOfData($yAxisLoc); + if ($scale eq "semilog") { + my $yMaxLog10 = log10($yMaxData); + $yAxisMax[$yAxisLoc] = pow(10.0, ceil($yMaxLog10)); + } else { + $yAxisMax[$yAxisLoc] = computeCeil($yMaxData); + } + } + # If indications are that there is no chart height and the user hasn't + # set ymin or ymax, then adjust things to make the chart height + # non-zero. + if ($yAxisMin[$yAxisLoc] == $yAxisMax[$yAxisLoc]) { + # First see if we can tweak ymax + if (! defined($this->_getYmax($yAxisLoc))) { + $yAxisMax[$yAxisLoc] = computeCeil($yAxisMax[$yAxisLoc] + 0.1); + } elsif (! defined($this->_getYmin($yAxisLoc))) { + $yAxisMin[$yAxisLoc] = computeFloor($yAxisMin[$yAxisLoc] - 0.1); + } + } + $this->_setYminOfData($yAxisLoc, $yAxisMin[$yAxisLoc]); + $this->_setYmaxOfData($yAxisLoc, $yAxisMax[$yAxisLoc]); + # Check for valid user specified min/max + if (defined($this->_getYmin($yAxisLoc)) && defined($this->_getYmax($yAxisLoc)) && $yAxisMin[$yAxisLoc] == $yAxisMax[$yAxisLoc]) { + return "No Chart height with ymin$yAxisLoc($yAxisMin[$yAxisLoc]) == ymax($yAxisMax[$yAxisLoc])"; + } + } + # If numygrids not defined, then no user specified value so attempt to + # compute a reasonable value from data1 data. + if (! defined($numYGrids[$LEFT]) || $numYGrids[$LEFT] eq "") { + if ($scale eq "semilog") { + $numYGrids[$LEFT] = log10($yAxisMax[$LEFT]) - log10($yAxisMin[$LEFT]) - 1; + } else { + if ($this->getDefNumYGrids() eq "") { + # So no user defined numxgrids and no system default, so + # calculate one using at most 9 grid lines. + $numYGrids[$LEFT] = $this->computeLinearNumGrids($yAxisMin[$LEFT], $yAxisMax[$LEFT], 9); + } else { + $numYGrids[$LEFT] = $this->getDefNumYGrids(); + } + } + $this->setNumYGrids1($numYGrids[$LEFT]); + } + # If there is data2, then we force the numYGrids for data set=RIGHT to be + # the same as numYGrids on data1. + if ($isData2Data) { + $this->setNumYGrids2($numYGrids[$RIGHT] = $numYGrids[$LEFT]); + } + + foreach my $yAxisLoc (@yAxisLocs) { if ($scale eq "semilog") { - $this->setYminOfData($yAxisMin) if (defined $yAxisMin); - $this->setYmaxOfData($yAxisMax) if (defined $yAxisMax); - $this->computeYMinMaxLog(); - undef $yAxisMin; # Force re-read below - undef $yAxisMax; + $this->_setNumYDigits($yAxisLoc, 0); + my $tics = $this->_getNumYTics($yAxisLoc); + $this->_setNumYTics($yAxisLoc, 8) if (! defined($tics) || $tics > 8); } else { - # Compute the number of digits to use when displaying the Y axis - # labels. - my $ymin = $this->getYmin(); - my $ymax = $this->getYmax(); - $ymin = $this->getYminOfData() if (! defined($ymin)); - $ymax = $this->getYmaxOfData() if (! defined($ymax)); - my $maxDigits = $this->computeNumDigits($ymin, $ymax, $numYGrids); - $this->setNumYDigits($maxDigits); + # Calculate how many digits we need to display on the yaxis + my $maxYDigits = $this->computeNumDigits($yAxisMin[$yAxisLoc], $yAxisMax[$yAxisLoc], $numYGrids[$yAxisLoc]); + $this->_setNumYDigits($yAxisLoc, $maxYDigits); } } - $yAxisMin = $this->getYminOfData() if (! defined($yAxisMin)); - $yAxisMax = $this->getYmaxOfData() if (! defined($yAxisMax)); - my $scaledYAxisMin = $this->_scale($yAxisMin); - my $scaledYAxisMax = $this->_scale($yAxisMax); - $yAxisDecimalDigits = $this->getNumYDigits(); - my $chartHeight = $scaledYAxisMax - $scaledYAxisMin; - # Check to see if either the user specified ymin/ymax values, or the - # data itself was such that they are both the same such that there is - # no height. - return "Chart height = 0 (ymin($yAxisMin) == ymax($yAxisMax))" if ($chartHeight == 0); - # Check to see if either the user specified ymin/ymax values, or the - # data itself was such that ymin > ymax. - return "Y max ($yAxisMax) < Y Min ($yAxisMin)" if ($chartHeight < 0); + my @scaledYAxisMin; + my @scaledYAxisMax; + my @chartHeight; + foreach my $yAxisLoc (@yAxisLocs) { + $scaledYAxisMin[$yAxisLoc] = $this->_scale($yAxisMin[$yAxisLoc]); + $scaledYAxisMax[$yAxisLoc] = $this->_scale($yAxisMax[$yAxisLoc]); + $chartHeight[$yAxisLoc] = $scaledYAxisMax[$yAxisLoc] - $scaledYAxisMin[$yAxisLoc]; + # Check to see if either the user specified ymin/ymax values, or the + # data itself was such that ymin > ymax. + return "Y max$yAxisLoc ($yAxisMax[$yAxisLoc]) < Y Min$yAxisLoc ($yAxisMin[$yAxisLoc])" if ($chartHeight[$yAxisLoc] < 0); + } + # To support scatter graphs (where the X data is probably not sorted) # we get the X axis labels and sort them (numerically) producing an # array of indexes which will then be used when plotting the actual # data. For example, if @xAxis is (1 3 5 2 4), then the indexes of the # values to plot would be 0 3 1 4 2 - my @xAxis = $this->getXaxis(); - my $xGrid = $this->getXgrid(); - my $xAxisNumeric = 1; # Flag whether the X axis values are numeric or not. + my @xAxis = $this->getXaxis(); + my $xGrid = $this->getXgrid(); + my $xAxisNumeric = 1; # Flag whether the X axis values are numeric or not. my %xAxisIndex; # Check to see if the X axis values are numeric or not. If not, then # we fake X axis data to be the indexes in the array. if (@xAxis) { - foreach my $index (0..$#xAxis) { - if ($xAxis[$index] =~ m/([\-]?[0-9.]+[eE]?[+-]?\d*)/) { - $xAxisIndex{$index} = $1; - } else { - $xAxisIndex{$index} = $index; - $xAxisNumeric = 0; - } - } + foreac... [truncated message content] |