=head1 SYNOPSIS
- use Imager::Graph::SubClass;
- my $chart = Imager::Graph::SubClass->new;
+ use Imager::Graph::Sub_class;
+ my $chart = Imager::Graph::Sub_class->new;
my $img = $chart->draw(data=> \@data, ...)
or die $chart->error;
bless {}, $_[0];
}
-=item setGraphSize($size)
+=item set_graph_size($size)
Sets the size of the graph (in pixels) within the image. The size of the image defaults to 1.5 * $graph_size.
=cut
-sub setGraphSize {
- $_[0]->{'graph_size'} = $_[1];
+sub set_graph_size {
+ $_[0]->{'custom_style'}->{'size'} = $_[1];
}
-sub _getGraphSize {
- return $_[0]->{'graph_size'};
-}
-
-=item setImageWidth($width)
+=item set_image_width($width)
Sets the width of the image in pixels.
=cut
-sub setImageWidth {
- $_[0]->{'image_width'} = $_[1];
+sub set_image_width {
+ $_[0]->{'custom_style'}->{'width'} = $_[1];
}
-sub _getImageWidth {
- return $_[0]->{'image_width'};
-}
-
-=item setImageHeight($height)
+=item set_image_height($height)
Sets the height of the image in pixels.
=cut
-sub setImageHeight {
- $_[0]->{'image_height'} = $_[1];
-}
-
-sub _getImageHeight {
- return $_[0]->{'image_height'};
+sub set_image_height {
+ $_[0]->{'custom_style'}->{'height'} = $_[1];
}
-=item addDataSeries([8, 6, 7, 5, 3, 0, 9], 'Series Name');
+=item add_data_series([8, 6, 7, 5, 3, 0, 9], 'Series Name');
Adds a data series to the graph. For L<Imager::Graph::Pie>, only one data series can be added.
=cut
-sub addDataSeries {
+sub add_data_series {
my $self = shift;
my $data_ref = shift;
my $series_name = shift;
return;
}
-sub _getDataSeries {
+sub _get_data_series {
my ($self, $opts) = @_;
# return the data supplied to draw() if any.
return $self->{'graph_data'};
}
-=item setLabels(['label1', 'label2' ... ])
+=item set_labels(['label1', 'label2' ... ])
Labels the specific data points. For line/bar graphs, this is the x-axis. For pie graphs, it is the label for the wedges.
=cut
-sub setLabels {
+sub set_labels {
$_[0]->{'labels'} = $_[1];
}
-sub _getLabels {
+sub _get_labels {
my ($self, $opts) = @_;
$opts->{labels}
return $_[0]->{'labels'}
}
-=item setTitle($title)
+=item set_title($title)
Sets the title of the graph. Requires setting a font.
=cut
-sub setTitle {
- $_[0]->{'image_title'} = $_[1];
-}
-
-sub _getTitle {
- return $_[0]->{'image_title'};
+sub set_title {
+ $_[0]->{'custom_style'}->{'title'}->{'text'} = $_[1];
}
-=item setFont(Imager::Font->new())
+=item set_font($font)
Sets the font to use for text. Takes an L<Imager::Font> object.
=cut
-sub setFont {
- $_[0]->{'font'} = $_[1];
-}
-
-sub _getFont {
- return $_[0]->{'font'};
+sub set_font {
+ $_[0]->{'custom_style'}->{'font'} = $_[1];
}
-=item setStyle($style_name)
+=item set_style($style_name)
Sets the style to be used for the graph. Imager::Graph comes with several pre-defined styles: fount_lin (default), fount_rad, mono, primary_red, and primary.
=cut
-sub setStyle {
+sub set_style {
$_[0]->{'style'} = $_[1];
}
-sub _getStyle {
+sub _get_style {
my ($self, $opts) = @_;
$opts->{style}
Creates a new image, draws the chart onto that image and returns it.
-Typically you will need to supply a C<data> parameter in the format
+Optionally, instead of using the api methods to configure your chart,
+you can supply a C<data> parameter in the format
required by that particular graph, and if your graph will use any
text, a C<font> parameter
The C<style> parameter will selects a basic color set, and possibly
sets other related parameters. See L</"STYLES">.
- my $font = Imager::Font->new(file => 'ImUgly.ttf');
+ my $font = Imager::Font->new(file => 'Im_ugly.ttf');
my $img = $chart->draw(
data => \@data,
font => $font,
=back
+=head1 Style API
+
+To set or override styles, you can use the following methods:
+
+=over 4
+
+=item set_image_background
+
+=cut
+
+sub set_image_background {
+ $_[0]->{'custom_style'}->{'back'} = $_[1];
+}
+
+=item set_channels
+
+=cut
+
+sub set_channels {
+ $_[0]->{'custom_style'}->{'channels'} = $_[1];
+}
+
+=item set_line_color
+
+=cut
+
+sub set_line_color {
+ $_[0]->{'custom_style'}->{'line'} = $_[1];
+}
+
+=item set_title_font_size
+
+=cut
+
+sub set_title_font_size {
+ $_[0]->{'custom_style'}->{'title'}->{'size'} = $_[1];
+}
+
+=item set_title_font_color
+
+=cut
+
+sub set_title_font_color {
+ $_[0]->{'custom_style'}->{'title'}->{'color'} = $_[1];
+}
+
+=item set_title_horizontal_align
+
+=cut
+
+sub set_title_horizontal_align {
+ $_[0]->{'custom_style'}->{'title'}->{'halign'} = $_[1];
+}
+
+=item set_title_vertical_align
+
+=cut
+
+sub set_title_vertical_align {
+ $_[0]->{'custom_style'}->{'title'}->{'valign'} = $_[1];
+}
+
+=item set_text_font_color
+
+=cut
+
+sub set_text_font_color {
+ $_[0]->{'custom_style'}->{'text'}->{'color'} = $_[1];
+}
+
+=item set_text_font_size
+
+=cut
+
+sub set_text_font_size {
+ $_[0]->{'custom_style'}->{'text'}->{'size'} = $_[1];
+}
+
+=item set_graph_background_color
+
+=cut
+
+sub set_graph_background_color {
+ $_[0]->{'custom_style'}->{'bg'} = $_[1];
+}
+
+=item set_graph_foreground_color
+
+=cut
+
+sub set_graph_foreground_color {
+ $_[0]->{'custom_style'}->{'fg'} = $_[1];
+}
+
+=item set_legend_font_color
+
+=cut
+
+sub set_legend_font_color {
+ $_[0]->{'custom_style'}->{'legend'}->{'color'} = $_[1];
+}
+
+=item set_legend_font
+
+=cut
+
+sub set_legend_font {
+ $_[0]->{'custom_style'}->{'legend'}->{'font'} = $_[1];
+}
+
+=item set_legend_font_size
+
+=cut
+
+sub set_legend_font_size {
+ $_[0]->{'custom_style'}->{'legend'}->{'size'} = $_[1];
+}
+
+=item set_legend_patch_size
+
+=cut
+
+sub set_legend_patch_size {
+ $_[0]->{'custom_style'}->{'legend'}->{'patchsize'} = $_[1];
+}
+
+=item set_legend_patch_gap
+
+=cut
+
+sub set_legend_patch_gap {
+ $_[0]->{'custom_style'}->{'legend'}->{'patchgap'} = $_[1];
+}
+
+=item set_legend_horizontal_align
+
+=cut
+
+sub set_legend_horizontal_align {
+ $_[0]->{'custom_style'}->{'legend'}->{'halign'} = $_[1];
+}
+
+=item set_legend_vertical_align
+
+=cut
+
+sub set_legend_vertical_align {
+ $_[0]->{'custom_style'}->{'legend'}->{'valign'} = $_[1];
+}
+
+=item set_legend_padding
+
+=cut
+
+sub set_legend_padding {
+ $_[0]->{'custom_style'}->{'legend'}->{'padding'} = $_[1];
+}
+
+=item set_legend_outside_padding
+
+=cut
+
+sub set_legend_outside_padding {
+ $_[0]->{'custom_style'}->{'legend'}->{'outsidepadding'} = $_[1];
+}
+
+=item set_legend_fill
+
+=cut
+
+sub set_legend_fill {
+ $_[0]->{'custom_style'}->{'legend'}->{'fill'} = $_[1];
+}
+
+=item set_legend_border
+
+=cut
+
+sub set_legend_border {
+ $_[0]->{'custom_style'}->{'legend'}->{'border'} = $_[1];
+}
+
+=item set_legend_orientation
+
+=cut
+
+sub set_legend_orientation {
+ $_[0]->{'custom_style'}->{'legend'}->{'orientation'} = $_[1];
+}
+
+=item set_callout_font_color
+
+=cut
+
+sub set_callout_font_color {
+ $_[0]->{'custom_style'}->{'callout'}->{'color'} = $_[1];
+}
+
+=item set_callout_font
+
+=cut
+
+sub set_callout_font {
+ $_[0]->{'custom_style'}->{'callout'}->{'font'} = $_[1];
+}
+
+=item set_callout_font_size
+
+=cut
+
+sub set_callout_font_size {
+ $_[0]->{'custom_style'}->{'callout'}->{'size'} = $_[1];
+}
+
+=item set_callout_line_color
+
+=cut
+
+sub set_callout_line_color {
+ $_[0]->{'custom_style'}->{'callout'}->{'line'} = $_[1];
+}
+
+=item set_callout_leader_inside_length
+
+=cut
+
+sub set_callout_leader_inside_length {
+ $_[0]->{'custom_style'}->{'callout'}->{'inside'} = $_[1];
+}
+
+=item set_callout_leader_outside_length
+
+=cut
+
+sub set_callout_leader_outside_length {
+ $_[0]->{'custom_style'}->{'callout'}->{'outside'} = $_[1];
+}
+
+=item set_callout_leader_length
+
+=cut
+
+sub set_callout_leader_length {
+ $_[0]->{'custom_style'}->{'callout'}->{'leadlen'} = $_[1];
+}
+
+=item set_callout_gap
+
+=cut
+
+sub set_callout_gap {
+ $_[0]->{'custom_style'}->{'callout'}->{'gap'} = $_[1];
+}
+
+=item set_label_font_color
+
+=cut
+
+sub set_label_font_color {
+ $_[0]->{'custom_style'}->{'label'}->{'color'} = $_[1];
+}
+
+=item set_label_font
+
+=cut
+
+sub set_label_font {
+ $_[0]->{'custom_style'}->{'label'}->{'font'} = $_[1];
+}
+
+=item set_label_font_size
+
+=cut
+
+sub set_label_font_size {
+ $_[0]->{'custom_style'}->{'label'}->{'size'} = $_[1];
+}
+
+=item set_drop_shadow_fill_color
+
+=cut
+
+sub set_drop_shadow_fill_color {
+ $_[0]->{'custom_style'}->{'dropshadow'}->{'fill'} = $_[1];
+}
+
+=item set_drop_shadow_offset
+
+=cut
+
+sub set_drop_shadow_offset {
+ $_[0]->{'custom_style'}->{'dropshadow'}->{'off'} = $_[1];
+}
+
+=item set_drop_shadowXOffset
+
+=cut
+
+sub set_drop_shadowXOffset {
+ $_[0]->{'custom_style'}->{'dropshadow'}->{'offx'} = $_[1];
+}
+
+=item set_drop_shadowYOffset
+
+=cut
+
+sub set_drop_shadowYOffset {
+ $_[0]->{'custom_style'}->{'dropshadow'}->{'offy'} = $_[1];
+}
+
+=item set_drop_shadow_filter
+
+=cut
+
+sub set_drop_shadow_filter {
+ $_[0]->{'custom_style'}->{'dropshadow'}->{'filter'} = $_[1];
+}
+
+=item set_outline_color
+
+=cut
+
+sub set_outline_color {
+ $_[0]->{'custom_style'}->{'outline'}->{'line'} = $_[1];
+}
+
+=item set_data_area_fills
+
+=cut
+
+sub set_data_area_fills {
+ $_[0]->{'custom_style'}->{'fills'} = $_[1];
+}
+
+=item set_data_line_colors
+
+=cut
+
+sub set_data_line_colors {
+ $_[0]->{'custom_style'}->{'colors'} = $_[1];
+}
+
+=back
+
=head1 FEATURES
Each graph type has a number of features. These are used to add
various items that are displayed in the graph area. Some common
-features are:
+methods are:
+
+=over
+
+=item show_legend()
+
+adds a box containing boxes filled with the data filess, with
+the labels provided to the draw method. The legend will only be
+displayed if both the legend feature is enabled and labels are
+supplied.
+
+=cut
+
+sub show_legend {
+ $_[0]->{'custom_style'}->{'features'}->{'legend'} = 1;
+}
+
+=item show_outline()
+
+draws a border around the data areas.
+
+=cut
+
+sub show_outline {
+ $_[0]->{'custom_style'}->{'features'}->{'outline'} = 1;
+}
+
+=item show_labels()
+
+labels each data fill, usually by including text inside the data fill.
+If the text does not fit in the fill, they could be displayed in some
+other form, eg. as callouts in a pie graph. There usually isn't much
+point in including both labels and a legend.
+
+=cut
+
+sub show_labels {
+ $_[0]->{'custom_style'}->{'features'}->{'labels'} = 1;
+}
+
+=item show_drop_shadow()
+
+a simple drop shadow is shown behind some of the graph elements.
+
+=cut
+
+sub show_drop_shadow {
+ $_[0]->{'custom_style'}->{'features'}->{'dropshadow'} = 1;
+}
+
+=item reset_features()
+
+Unsets all of the features
+
+=cut
+
+sub reset_features {
+ $_[0]->{'custom_style'}->{'features'} = {};
+ $_[0]->{'custom_style'}->{'features'}->{'reset'} = 1;
+}
+
+=back
+
+Additionally, features can be set by passing them into the draw() method:
=over
qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
],
fg=>'000000',
+ negative_bg=>'EEEEEE',
bg=>'E0E0E0',
legend=>
{
qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
],
fg=>'000000',
+ negative_bg=>'EEEEEE',
bg=>'C08080',
legend=>
{
channels=>1,
bg=>'FFFFFF',
fg=>'000000',
+ negative_bg=>'EEEEEE',
features=>{ outline=>1 },
pie =>{
blur=>undef,
( positions=>[0, 1],
colors=>[ NC('6060FF'), NC('60FF60') ]) },
fg=>'000000',
+ negative_bg=>'EEEEEE',
bg=>'FFFFFF',
features=>{ dropshadow=>1 },
},
( positions=>[0, 1],
colors=>[ NC('6060FF'), NC('60FF60') ]) },
fg=>'000000',
+ negative_bg=>'EEEEEE',
bg=>'FFFFFF',
}
);
+$styles{'ocean'} = {
+ fills => [
+ {
+ fountain =>'linear',
+ xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
+ segments => Imager::Fountain->simple(
+ positions=>[0, 1],
+ colors=>[ NC('FFFFFF'), NC('E6E2AF') ]),
+ },
+ {
+ fountain =>'linear',
+ xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
+ segments => Imager::Fountain->simple(
+ positions=>[0, 1],
+ colors=>[ NC('FFFFFF'), NC('A7A37E') ]),
+ },
+ {
+ fountain =>'linear',
+ xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
+ segments => Imager::Fountain->simple(
+ positions=>[0, 1],
+ colors=>[ NC('FFFFFF'), NC('80B4A2') ]),
+ },
+ {
+ fountain =>'linear',
+ xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
+ segments => Imager::Fountain->simple(
+ positions=>[0, 1],
+ colors=>[ NC('FFFFFF'), NC('046380') ]),
+ },
+ {
+ fountain =>'linear',
+ xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
+ segments => Imager::Fountain->simple(
+ positions=>[0, 1],
+ colors=>[ NC('FFFFFF'), NC('877EA7') ]),
+ },
+ {
+ fountain =>'linear',
+ xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
+ segments => Imager::Fountain->simple(
+ positions=>[0, 1],
+ colors=>[ NC('FFFFFF'), NC('67A35E') ]),
+ },
+ {
+ fountain =>'linear',
+ xa_ratio => 0, ya_ratio=>0, xb_ratio=>1.0, yb_ratio=>1.0,
+ segments => Imager::Fountain->simple(
+ positions=>[0, 1],
+ colors=>[ NC('FFFFFF'), NC('B4726F') ]),
+ },
+ ],
+ colors => [
+ qw(E6E2AF A7A37E 80B4A2 046380 877EA7 67A35E B4726F)
+ ],
+ fg=>'000000',
+ negative_bg=>'EEEEEE',
+ bg=>'FFFFFF',
+ features=>{ dropshadow=>1 },
+
+};
+
=item $self->_style_setup(\%opts)
Uses the values from %opts to build a customized hash describing the
my $style_defs = $self->_style_defs;
my $style;
- # fill in values from api calls
- $opts->{'size'} = $opts->{'size'} || $self->_getGraphSize();
- $opts->{'width'} = $opts->{'width'} || $self->_getImageWidth();
- $opts->{'height'} = $opts->{'height'} || $self->_getImageHeight();
- $opts->{'font'} = $opts->{'font'} || $self->_getFont();
- $opts->{'title'} = $opts->{'title'};
- if (!$opts->{'title'} && $self->_getTitle()) {
- $opts->{'title'} = { text => $self->_getTitle() };
- }
-
- my $pre_def_style = $self->_getStyle($opts);
+ my $pre_def_style = $self->_get_style($opts);
+ my $api_style = $self->{'custom_style'} || {};
$style = $styles{$pre_def_style} if $pre_def_style;
$style ||= $styles{$def_style};
- my @search_list = ( $style_defs, $style, $opts);
+ my @search_list = ( $style_defs, $style, $api_style, $opts);
my %work;
my @composite = $self->_composite();
$img;
}
+sub _get_image {
+ my $self = shift;
+
+ if (!$self->{'_image'}) {
+ $self->{'_image'} = $self->_make_img();
+ }
+ return $self->{'_image'};
+}
+
=item _text_style($name)
Returns parameters suitable for calls to Imager::Font's bounding_box()
for my $label (@$labels) {
my @patchbox = ( $patchpos - $patchsize/2, $ypos - $patchsize/2,
$patchpos + $patchsize * 3 / 2, $ypos + $patchsize*3/2 );
- my @fill = $self->_data_fill($dataindex, \@patchbox)
- or return;
+
+ my @fill;
+ if ($self->_draw_flat_legend()) {
+ @fill = (color => $self->_data_color($dataindex), filled => 1);
+ }
+ else {
+ @fill = $self->_data_fill($dataindex, \@patchbox)
+ or return;
+ }
$img->box(xmin=>$patchpos, ymin=>$ypos, xmax=>$patchpos + $patchsize,
ymax=>$ypos + $patchsize, @fill);
if ($self->{_style}{legend}{patchborder}) {
}
}
+sub _draw_flat_legend {
+ return 0;
+}
+
=item _composite()
Returns a list of style fields that are stored as composites, and
lib/Imager/Graph/Pie.pm
lib/Imager/Graph/StackedColumn.pm
lib/Imager/Graph/Util.pm
+lib/Imager/Graph/Vertical.pm
Makefile.PL
MANIFEST
MANIFEST.bak
t/lib/Imager/Font/Test.pm
t/t00load.t
t/t10pie.t
+t/t11line.t
+t/t12column.t
+t/t13stacked_col.t
t/t20api.t
+t/t21style_api.t
t/t91pod.t
t/t93podcover.t
testimg/t10_hlegend.png
^Makefile\.old$
^pm_to_blib$
\.diff$
+\.rej$
use strict;
use vars qw(@ISA);
-use Imager::Graph;
-@ISA = qw(Imager::Graph);
+use Imager::Graph::Vertical;
+@ISA = qw(Imager::Graph::Vertical);
-=item setYTics($count)
-
-Set the number of Y tics to use. Their value and position will be determined by the data range.
-
-=cut
-
-sub setYTics {
- $_[0]->{'y_tics'} = $_[1];
-}
-
-sub _getYTics {
- return $_[0]->{'y_tics'};
-}
-
-sub draw {
- my ($self, %opts) = @_;
-
- $self->_processOptions(\%opts);
-
- if (!$self->_validInput()) {
- return;
- }
-
- $self->_style_setup(\%opts);
-
- my $style = $self->{_style};
-
- my $img = $self->_make_img()
- or return;
-
- my @chart_box = ( 0, 0, $img->getwidth-1, $img->getheight-1 );
-
- my @labels = map { $_->{'series_name'} } @{$self->_getDataSeries()};
- if ($style->{features}{legend} && (scalar @labels)) {
- $self->_draw_legend($img, \@labels, \@chart_box)
- or return;
- }
-
- my @series = @{$self->_getDataSeries()};
- my $max_value = 0;
- my $min_value = 0;
-
- my $column_count = 0;
- foreach my $series (@series) {
- my @data = @{$series->{'data'}};
-
- foreach my $value (@data) {
- $column_count++;
- if ($value > $max_value) { $max_value = $value; }
- if ($value < $min_value) { $min_value = $value; }
- }
- }
-
- my $value_range = $max_value - $min_value;
-
- my $width = $self->_get_number('width');
- my $height = $self->_get_number('height');
- my $size = $self->_get_number('size');
-
- my $bottom = ($height - $size) / 2;
- my $left = ($width - $size) / 2;
-
- my @graph_box = ( $left, $bottom, $left + $size - 1, $bottom + $size - 1 );
-
- $img->box(
- color => '000000',
- xmin => $left,
- xmax => $left+$size,
- ymin => $bottom,
- ymax => $bottom+$size,
- );
-
- $img->box(
- color => 'FFFFFF',
- xmin => $left + 1,
- xmax => $left+$size - 1,
- ymin => $bottom + 1,
- ymax => $bottom+$size -1 ,
- filled => 1,
- );
-
- my $zero_position = $bottom + $size - (-1*$min_value / $value_range) * ($size -1);
-
- if ($min_value < 0) {
- $img->box(
- color => 'EEEEEE',
- xmin => $left + 1,
- xmax => $left+$size - 1,
- ymin => $zero_position,
- ymax => $bottom+$size -1,
- filled => 1,
- );
- }
-
- if ($self->_getYTics()) {
- $self->_drawYTics($img, $min_value, $max_value, $self->_getYTics(), \@graph_box, \@chart_box);
- }
- if ($self->_getLabels()) {
- $self->_drawXTics($img, \@graph_box, \@chart_box);
- }
-
- my $bar_width = $size / $column_count;
-
- my $outline_color;
- if ($style->{'features'}{'outline'}) {
- $outline_color = $self->_get_color('outline.line');
- }
-
- my $series_counter = 0;
- foreach my $series (@series) {
- my @data = @{$series->{'data'}};
- my $data_size = scalar @data;
- my @fill = $self->_data_fill($series_counter, \@graph_box);
- my $color = $self->_data_color($series_counter);
- for (my $i = 0; $i < $data_size; $i++) {
- my $x1 = $left + $i * $size / ($data_size) + ($bar_width * $series_counter);
- my $x2 = $x1 + $bar_width;
-
- my $y1 = $bottom + ($value_range - $data[$i] + $min_value)/$value_range * $size;
-
- if ($data[$i] > 0) {
- $img->box(xmin => $x1, xmax => $x2, ymin => $y1, ymax => $zero_position-1, @fill);
- if ($style->{'features'}{'outline'}) {
- $img->box(xmin => $x1, xmax => $x2, ymin => $y1, ymax => $zero_position, color => $outline_color);
- }
- }
- else {
- $img->box(xmin => $x1, xmax => $x2, ymin => $zero_position, ymax => $y1-1, @fill);
- if ($style->{'features'}{'outline'}) {
- $img->box(xmin => $x1, xmax => $x2, ymin => $zero_position, ymax => $y1, color => $outline_color);
- }
- }
- }
-
- $series_counter++;
- }
-
- return $img;
-}
-
-sub _drawYTics {
- my $self = shift;
- my $img = shift;
- my $min = shift;
- my $max = shift;
- my $tic_count = shift;
- my $graph_box = shift;
- my $image_box = shift;
-
- my $interval = ($max - $min) / ($tic_count - 1);
-
- my %text_info = $self->_text_style('legend')
- or return;
-
- my $tic_distance = ($graph_box->[3] - $graph_box->[1]) / ($tic_count - 1);
- for my $count (0 .. $tic_count - 1) {
- my $x1 = $graph_box->[0] - 5;
- my $x2 = $graph_box->[0] + 5;
- my $y1 = $graph_box->[3] - ($count * $tic_distance);
-
- $img->line(x1 => $x1, x2 => $x2, y1 => $y1, y2 => $y1, aa => 1, color => '000000');
-
- my $value = sprintf("%.2f", ($count*$interval)+$min);
-
- my @box = $self->_text_bbox($value, 'legend')
- or return;
-
- my $width = $box[2];
- my $height = $box[3];
-
- $img->string(%text_info,
- x => ($x1 - $width - 3),
- y => ($y1 + ($height / 2)),
- text => $value
- );
- }
-
-}
-
-sub _drawXTics {
- my $self = shift;
- my $img = shift;
- my $graph_box = shift;
- my $image_box = shift;
-
- my $labels = $self->_getLabels();
-
- my $tic_count = (scalar @$labels) - 1;
-
- my $tic_distance = ($graph_box->[2] - $graph_box->[0]) / ($tic_count);
- my %text_info = $self->_text_style('legend')
- or return;
-
- for my $count (0 .. $tic_count) {
- my $label = $labels->[$count];
- my $x1 = $graph_box->[0] + ($tic_distance * $count);
- my $y1 = $graph_box->[3] + 5;
- my $y2 = $graph_box->[3] - 5;
-
- $img->line(x1 => $x1, x2 => $x1, y1 => $y1, y2 => $y2, aa => 1, color => '000000');
-
- my @box = $self->_text_bbox($label, 'legend')
- or return;
-
- my $width = $box[2];
- my $height = $box[3];
-
- $img->string(%text_info,
- x => ($x1 - ($width / 2)),
- y => ($y1 + ($height + 5)),
- text => $label
- );
-
- }
+sub _get_default_series_type {
+ return 'column';
}
-sub _validInput {
- my $self = shift;
-
- if (!defined $self->_getDataSeries() || !scalar @{$self->_getDataSeries()}) {
- return $self->_error("No data supplied");
- }
-
- if (!scalar @{$self->_getDataSeries()->[0]->{'data'}}) {
- return $self->_error("No values in data series");
- }
-
- my @data = @{$self->_getDataSeries()->[0]->{'data'}};
-
- return 1;
-}
-
-
-
1;
use strict;
use vars qw(@ISA);
-use Imager::Graph;
-@ISA = qw(Imager::Graph);
-
-=item setYTics($count)
-
-Set the number of Y tics to use. Their value and position will be determined by the data range.
-
-=cut
-
-sub setYTics {
- $_[0]->{'y_tics'} = $_[1];
-}
-
-sub _getYTics {
- return $_[0]->{'y_tics'};
-}
-
-sub draw {
- my ($self, %opts) = @_;
-
- $self->_processOptions(\%opts);
-
- if (!$self->_validInput()) {
- return;
- }
-
- $self->_style_setup(\%opts);
-
- my $style = $self->{_style};
-
- my $img = $self->_make_img()
- or return;
-
- my @chart_box = ( 0, 0, $img->getwidth-1, $img->getheight-1 );
-
- my @labels = map { $_->{'series_name'} } @{$self->_getDataSeries()};
- if ($style->{features}{legend} && (scalar @labels)) {
- $self->_draw_legend($img, \@labels, \@chart_box)
- or return;
- }
-
- my @series = @{$self->_getDataSeries()};
- my $max_value = 0;
- my $min_value = 0;
-
- foreach my $series (@series) {
- my @data = @{$series->{'data'}};
-
- foreach my $value (@data) {
- if ($value > $max_value) { $max_value = $value; }
- if ($value < $min_value) { $min_value = $value; }
- }
- }
-
- my $value_range = $max_value - $min_value;
-
- my $width = $self->_get_number('width');
- my $height = $self->_get_number('height');
- my $size = $self->_get_number('size');
-
- my $bottom = ($height - $size) / 2;
- my $left = ($width - $size) / 2;
-
- my @graph_box = ( $left, $bottom, $left + $size - 1, $bottom + $size - 1 );
-
- $img->box(
- color => '000000',
- xmin => $left,
- xmax => $left+$size,
- ymin => $bottom,
- ymax => $bottom+$size,
- );
-
- $img->box(
- color => 'FFFFFF',
- xmin => $left + 1,
- xmax => $left+$size - 1,
- ymin => $bottom + 1,
- ymax => $bottom+$size -1 ,
- filled => 1,
- );
-
- if ($min_value < 0) {
- $img->box(
- color => 'EEEEEE',
- xmin => $left + 1,
- xmax => $left+$size - 1,
- ymin => $bottom + $size - (-1*$min_value / $value_range) * ($size -1),
- ymax => $bottom+$size -1,
- filled => 1,
- );
- }
-
- if ($self->_getYTics()) {
- $self->_drawYTics($img, $min_value, $max_value, $self->_getYTics(), \@graph_box, \@chart_box);
- }
- if ($self->_getLabels()) {
- $self->_drawXTics($img, \@graph_box, \@chart_box);
- }
-
- my $series_counter = 0;
- foreach my $series (@series) {
- my @data = @{$series->{'data'}};
- my $data_size = scalar @data;
- my @fill = $self->_data_fill($series_counter, \@graph_box);
- my $color = $self->_data_color($series_counter);
- for (my $i = 0; $i < $data_size - 1; $i++) {
- my $x1 = $left + $i * $size / ($data_size - 1);
- my $x2 = $left + ($i + 1) * $size / ($data_size - 1);
-
- my $y1 = $bottom + ($value_range - $data[$i] + $min_value)/$value_range * $size;
- my $y2 = $bottom + ($value_range - $data[$i + 1] + $min_value)/$value_range * $size;
-
- $img->line(x1 => $x1, y1 => $y1, x2 => $x2, y2 => $y2, aa => 1, color => $color) || die $img->errstr;
- $img->circle(x => $x1, y => $y1, r => 3, aa => 1, filled => 1, @fill);
- }
-
- my $x2 = $left + ($data_size - 1) * $size / ($data_size - 1);
- my $y2 = $bottom + ($value_range - $data[$data_size - 1] + $min_value)/$value_range * $size;
-
- $img->circle(x => $x2, y => $y2, r => 3, aa => 1, filled => 1, @fill);
- $series_counter++;
- }
-
- return $img;
-}
-
-sub _drawYTics {
- my $self = shift;
- my $img = shift;
- my $min = shift;
- my $max = shift;
- my $tic_count = shift;
- my $graph_box = shift;
- my $image_box = shift;
-
- my $interval = ($max - $min) / ($tic_count - 1);
-
- my %text_info = $self->_text_style('legend')
- or return;
-
- my $tic_distance = ($graph_box->[3] - $graph_box->[1]) / ($tic_count - 1);
- for my $count (0 .. $tic_count - 1) {
- my $x1 = $graph_box->[0] - 5;
- my $x2 = $graph_box->[0] + 5;
- my $y1 = $graph_box->[3] - ($count * $tic_distance);
-
- $img->line(x1 => $x1, x2 => $x2, y1 => $y1, y2 => $y1, aa => 1, color => '000000');
-
- my $value = sprintf("%.2f", ($count*$interval)+$min);
-
- my @box = $self->_text_bbox($value, 'legend')
- or return;
-
- my $width = $box[2];
- my $height = $box[3];
-
- $img->string(%text_info,
- x => ($x1 - $width - 3),
- y => ($y1 + ($height / 2)),
- text => $value
- );
- }
+use Imager::Graph::Vertical;
+@ISA = qw(Imager::Graph::Vertical);
+sub _get_default_series_type {
+ return 'line';
}
-sub _drawXTics {
- my $self = shift;
- my $img = shift;
- my $graph_box = shift;
- my $image_box = shift;
-
- my $labels = $self->_getLabels();
-
- my $tic_count = (scalar @$labels) - 1;
-
- my $tic_distance = ($graph_box->[2] - $graph_box->[0]) / ($tic_count);
- my %text_info = $self->_text_style('legend')
- or return;
-
- for my $count (0 .. $tic_count) {
- my $label = $labels->[$count];
- my $x1 = $graph_box->[0] + ($tic_distance * $count);
- my $y1 = $graph_box->[3] + 5;
- my $y2 = $graph_box->[3] - 5;
-
- $img->line(x1 => $x1, x2 => $x1, y1 => $y1, y2 => $y2, aa => 1, color => '000000');
-
- my @box = $self->_text_bbox($label, 'legend')
- or return;
-
- my $width = $box[2];
- my $height = $box[3];
-
- $img->string(%text_info,
- x => ($x1 - ($width / 2)),
- y => ($y1 + ($height + 5)),
- text => $label
- );
-
- }
-}
-
-sub _validInput {
- my $self = shift;
-
- if (!defined $self->_getDataSeries() || !scalar @{$self->_getDataSeries()}) {
- return $self->_error("No data supplied");
- }
-
- if (!scalar @{$self->_getDataSeries()->[0]->{'data'}}) {
- return $self->_error("No values in data series");
- }
-
- my @data = @{$self->_getDataSeries()->[0]->{'data'}};
-
- return 1;
-}
-
-
-
1;
=over
+=item show_callouts_onAll_segments()
+
+all labels are presented as callouts
+
+=cut
+
+sub show_callouts_onAll_segments {
+ $_[0]->{'custom_style'}->{'features'}->{'allcallouts'} = 1;
+}
+
+=item show_only_label_percentages()
+
+only show the percentage, not the labels.
+
+=cut
+
+sub show_only_label_percentages {
+ $_[0]->{'custom_style'}->{'features'}->{'labelspconly'} = 1;
+}
+
+=item show_label_percentages()
+
+adds the percentage of the pie to each label.
+
+=cut
+
+sub show_label_percentages {
+ $_[0]->{'custom_style'}->{'features'}->{'labelspc'} = 1;
+}
+
+=back
+
+Additionally, arguments can be added to draw() :
+
+=over
+
=item legend
adds a legend to your graph. Requires the labels parameter
# from the Netcraft September 2001 web survey
# http://www.netcraft.com/survey/
my @data = qw(17874757 8146372 1321544 811406 );
- my @labels = qw(Apache Microsoft iPlanet Zeus );
+ my @labels = qw(Apache Microsoft i_planet Zeus );
my $pie = Imager::Graph::Pie->new;
sub draw {
my ($self, %opts) = @_;
- my $data_series = $self->_getDataSeries(\%opts);
+ my $data_series = $self->_get_data_series(\%opts);
- $self->_validInput($data_series)
+ $self->_valid_input($data_series)
or return;
my @data = @{$data_series->[0]->{'data'}};
- my @labels = @{$self->_getLabels(\%opts) || []};
+ my @labels = @{$self->_get_labels(\%opts) || []};
$self->_style_setup(\%opts);
# color=>Imager::Color->new(0,0,0));
$img->string(%label_text, x=>$tcx-$label->{lbox}[2]/2,
'y'=>$tcy+$label->{lbox}[3]/2+$label->{lbox}[1],
- text=>$label->{text});
+ text=>$label->{text}, aa => 1);
}
else {
$label->{callout} = 1;
my $ty = $oy + $label->{cbox}[3]/2+$label->{cbox}[1];
if ($lx < $cx) {
$img->string(%callout_text, x=>$lx-$callout_gap-$label->{cbox}[2],
- 'y'=>$ty, text=>$label->{text});
+ 'y'=>$ty, text=>$label->{text}, aa=>1);
}
else {
$img->string(%callout_text, x=>$lx+$callout_gap, 'y'=>$ty,
- text=>$label->{text});
+ text=>$label->{text}, aa=>1);
}
}
}
$img;
}
-sub _validInput {
+sub _valid_input {
my ($self, $data_series) = @_;
if (!defined $data_series || !scalar @$data_series) {
my ($self, $data, $labels, $total) = @_;
my @others;
- my $index;
+ my $index = 0;
for my $item (@$data) {
if ($item / $total < $self->{_style}{pie}{maxsegment}) {
push(@others, $index);
use strict;
use vars qw(@ISA);
-use Imager::Graph;
-@ISA = qw(Imager::Graph);
+use Imager::Graph::Vertical;
+@ISA = qw(Imager::Graph::Vertical);
-=item setYTics($count)
-
-Set the number of Y tics to use. Their value and position will be determined by the data range.
-
-=cut
-
-sub setYTics {
- $_[0]->{'y_tics'} = $_[1];
-}
-
-sub _getYTics {
- return $_[0]->{'y_tics'};
-}
-
-sub draw {
- my ($self, %opts) = @_;
-
- $self->_processOptions(\%opts);
-
- if (!$self->_validInput()) {
- return;
- }
-
- $self->_style_setup(\%opts);
-
- my $style = $self->{_style};
-
- my $img = $self->_make_img()
- or return;
-
- my @chart_box = ( 0, 0, $img->getwidth-1, $img->getheight-1 );
-
- my @labels = map { $_->{'series_name'} } @{$self->_getDataSeries()};
- if ($style->{features}{legend} && (scalar @labels)) {
- $self->_draw_legend($img, \@labels, \@chart_box)
- or return;
- }
-
- my @series = @{$self->_getDataSeries()};
- my $max_value = 0;
- my $min_value = 0;
-
- my $column_count = 0;
- my @max_entries;
- my @min_entries;
- for (my $i = scalar @series - 1; $i >= 0; $i--) {
- my $series = $series[$i];
- my $data = $series->{'data'};
-
- for (my $i = 0; $i < scalar @$data; $i++) {
- my $value = 0;
- if ($data->[$i] > 0) {
- $value = $data->[$i] + $max_entries[$i];
- $data->[$i] = $value;
- $max_entries[$i] = $value;
- }
- elsif ($data->[$i] < 0) {
- $value = $data->[$i] + $min_entries[$i];
- $data->[$i] = $value;
- $min_entries[$i] = $value;
- }
- if ($value > $max_value) { $max_value = $value; }
- if ($value < $min_value) { $min_value = $value; }
- }
- if (scalar @$data > $column_count) {
- $column_count = scalar @$data;
- }
- }
-
- my $value_range = $max_value - $min_value;
-
- my $width = $self->_get_number('width');
- my $height = $self->_get_number('height');
- my $size = $self->_get_number('size');
-
- my $bottom = ($height - $size) / 2;
- my $left = ($width - $size) / 2;
-
- my @graph_box = ( $left, $bottom, $left + $size - 1, $bottom + $size - 1 );
-
- $img->box(
- color => '000000',
- xmin => $left,
- xmax => $left+$size,
- ymin => $bottom,
- ymax => $bottom+$size,
- );
-
- $img->box(
- color => 'FFFFFF',
- xmin => $left + 1,
- xmax => $left+$size - 1,
- ymin => $bottom + 1,
- ymax => $bottom+$size -1 ,
- filled => 1,
- );
-
- my $zero_position = $bottom + $size - (-1*$min_value / $value_range) * ($size -1);
-
- if ($min_value < 0) {
- $img->box(
- color => 'EEEEEE',
- xmin => $left + 1,
- xmax => $left+$size - 1,
- ymin => $zero_position,
- ymax => $bottom+$size -1,
- filled => 1,
- );
- }
-
- if ($self->_getYTics()) {
- $self->_drawYTics($img, $min_value, $max_value, $self->_getYTics(), \@graph_box, \@chart_box);
- }
- if ($self->_getLabels()) {
- $self->_drawXTics($img, \@graph_box, \@chart_box);
- }
-
- my $bar_width = $size / $column_count;
-
- my $outline_color;
- if ($style->{'features'}{'outline'}) {
- $outline_color = $self->_get_color('outline.line');
- }
-
- my $series_counter = 0;
- foreach my $series (@series) {
- my @data = @{$series->{'data'}};
- my $data_size = scalar @data;
- my @fill = $self->_data_fill($series_counter, \@graph_box);
- my $color = $self->_data_color($series_counter);
- for (my $i = 0; $i < $data_size; $i++) {
- my $x1 = $left + $i * $size / ($data_size);
- my $x2 = $x1 + $bar_width;
-
- my $y1 = $bottom + ($value_range - $data[$i] + $min_value)/$value_range * $size;
-
- if ($data[$i] > 0) {
- $img->box(xmin => $x1, xmax => $x2, ymin => $y1, ymax => $zero_position-1, @fill);
- if ($style->{'features'}{'outline'}) {
- $img->box(xmin => $x1, xmax => $x2, ymin => $y1, ymax => $zero_position, color => $outline_color);
- }
- }
- else {
- $img->box(xmin => $x1, xmax => $x2, ymin => $zero_position, ymax => $y1-1, @fill);
- if ($style->{'features'}{'outline'}) {
- $img->box(xmin => $x1, xmax => $x2, ymin => $zero_position, ymax => $y1, color => $outline_color);
- }
- }
- }
-
- $series_counter++;
- }
-
- return $img;
-}
-
-sub _drawYTics {
- my $self = shift;
- my $img = shift;
- my $min = shift;
- my $max = shift;
- my $tic_count = shift;
- my $graph_box = shift;
- my $image_box = shift;
-
- my $interval = ($max - $min) / ($tic_count - 1);
-
- my %text_info = $self->_text_style('legend')
- or return;
-
- my $tic_distance = ($graph_box->[3] - $graph_box->[1]) / ($tic_count - 1);
- for my $count (0 .. $tic_count - 1) {
- my $x1 = $graph_box->[0] - 5;
- my $x2 = $graph_box->[0] + 5;
- my $y1 = $graph_box->[3] - ($count * $tic_distance);
-
- $img->line(x1 => $x1, x2 => $x2, y1 => $y1, y2 => $y1, aa => 1, color => '000000');
-
- my $value = sprintf("%.2f", ($count*$interval)+$min);
-
- my @box = $self->_text_bbox($value, 'legend')
- or return;
-
- my $width = $box[2];
- my $height = $box[3];
-
- $img->string(%text_info,
- x => ($x1 - $width - 3),
- y => ($y1 + ($height / 2)),
- text => $value
- );
- }
-
-}
-
-sub _drawXTics {
- my $self = shift;
- my $img = shift;
- my $graph_box = shift;
- my $image_box = shift;
-
- my $labels = $self->_getLabels();
-
- my $tic_count = (scalar @$labels) - 1;
-
- my $tic_distance = ($graph_box->[2] - $graph_box->[0]) / ($tic_count);
- my %text_info = $self->_text_style('legend')
- or return;
-
- for my $count (0 .. $tic_count) {
- my $label = $labels->[$count];
- my $x1 = $graph_box->[0] + ($tic_distance * $count);
- my $y1 = $graph_box->[3] + 5;
- my $y2 = $graph_box->[3] - 5;
-
- $img->line(x1 => $x1, x2 => $x1, y1 => $y1, y2 => $y2, aa => 1, color => '000000');
-
- my @box = $self->_text_bbox($label, 'legend')
- or return;
-
- my $width = $box[2];
- my $height = $box[3];
-
- $img->string(%text_info,
- x => ($x1 - ($width / 2)),
- y => ($y1 + ($height + 5)),
- text => $label
- );
-
- }
+sub _get_default_series_type {
+ return 'stacked_column';
}
-sub _validInput {
- my $self = shift;
-
- if (!defined $self->_getDataSeries() || !scalar @{$self->_getDataSeries()}) {
- return $self->_error("No data supplied");
- }
-
- if (!scalar @{$self->_getDataSeries()->[0]->{'data'}}) {
- return $self->_error("No values in data series");
- }
-
- my @data = @{$self->_getDataSeries()->[0]->{'data'}};
-
- return 1;
-}
-
-
-
1;
--- /dev/null
+package Imager::Graph::Vertical;
+
+=head1 NAME
+
+ Imager::Graph::Vertical- A super class for line/bar/column charts
+
+=cut
+
+use strict;
+use vars qw(@ISA);
+use Imager::Graph;
+@ISA = qw(Imager::Graph);
+
+=over 4
+
+=item add_data_series(\@data, $series_name)
+
+Add a data series to the graph, of the default type.
+
+=cut
+
+sub add_data_series {
+ my $self = shift;
+ my $data_ref = shift;
+ my $series_name = shift;
+
+ my $series_type = $self->_get_default_series_type();
+ $self->_add_data_series($series_type, $data_ref, $series_name);
+
+ return;
+}
+
+=item add_column_data_series(\@data, $series_name)
+
+Add a column data series to the graph.
+
+=cut
+
+sub add_column_data_series {
+ my $self = shift;
+ my $data_ref = shift;
+ my $series_name = shift;
+
+ $self->_add_data_series('column', $data_ref, $series_name);
+
+ return;
+}
+
+=item add_stacked_column_data_series(\@data, $series_name)
+
+Add a stacked column data series to the graph.
+
+=cut
+
+sub add_stacked_column_data_series {
+ my $self = shift;
+ my $data_ref = shift;
+ my $series_name = shift;
+
+ $self->_add_data_series('stacked_column', $data_ref, $series_name);
+
+ return;
+}
+
+=item add_line_data_series(\@data, $series_name)
+
+Add a line data series to the graph.
+
+=cut
+
+sub add_line_data_series {
+ my $self = shift;
+ my $data_ref = shift;
+ my $series_name = shift;
+
+ $self->_add_data_series('line', $data_ref, $series_name);
+
+ return;
+}
+
+=item set_range_padding($percentage)
+
+Sets the padding to be used, as a percentage. For example, if your data ranges from 0 to 10, and you have a 20 percent padding, the y axis will go to 12.
+
+Defaults to 10.
+
+=cut
+
+sub set_range_padding {
+ $_[0]->{'custom_style'}->{'range_padding'} = $_[1];
+}
+
+=item draw()
+
+Draw the graph
+
+=cut
+
+sub draw {
+ my ($self, %opts) = @_;
+
+ if (!$self->_valid_input()) {
+ return;
+ }
+
+ $self->_style_setup(\%opts);
+
+ my $style = $self->{_style};
+
+ my $img = $self->_get_image()
+ or return;
+
+ my @image_box = ( 0, 0, $img->getwidth-1, $img->getheight-1 );
+ $self->_set_image_box(\@image_box);
+
+ # Scale the graph box down to the widest graph that can cleanly hold the # of columns.
+ $self->_get_data_range();
+ my $column_count = $self->_get_column_count();
+
+ my $width = $self->_get_number('width');
+ my $height = $self->_get_number('height');
+ my $size = $self->_get_number('size');
+
+ my $bottom = ($height - $size) / 2;
+ my $left = ($width - $size) / 2;
+
+ my $col_width = int($size / $column_count) -1;
+ my $graph_width = $col_width * $column_count + 1;
+
+ my @graph_box = ( $left, $bottom, $left + $graph_width - 1, $bottom + $size - 1 );
+ $self->_set_graph_box(\@graph_box);
+
+ $self->_draw_legend();
+
+ $img->box(
+ color => $self->_get_color('outline.line'),
+ xmin => $left,
+ xmax => $left+$graph_width,
+ ymin => $bottom,
+ ymax => $bottom+$size,
+ );
+
+ $img->box(
+ color => $self->_get_color('bg'),
+ xmin => $left + 1,
+ xmax => $left+$graph_width - 1,
+ ymin => $bottom + 1,
+ ymax => $bottom+$size -1 ,
+ filled => 1,
+ );
+
+ my $min_value = $self->_get_min_value();
+ my $max_value = $self->_get_max_value();
+ my $value_range = $max_value - $min_value;
+
+ my $zero_position;
+ if ($value_range) {
+ $zero_position = $bottom + $size - (-1*$min_value / $value_range) * ($size -1);
+ }
+
+ if ($min_value < 0) {
+ $img->box(
+ color => $self->_get_color('negative_bg'),
+ xmin => $left + 1,
+ xmax => $left+$graph_width- 1,
+ ymin => $zero_position,
+ ymax => $bottom+$size -1,
+ filled => 1,
+ );
+ $img->line(
+ x1 => $left+1,
+ y1 => $zero_position,
+ x2 => $left + $graph_width,
+ y2 => $zero_position,
+ color => $self->_get_color('outline.line'),
+ );
+ }
+
+ if ($self->_get_data_series()->{'stacked_column'}) {
+ $self->_draw_stacked_columns();
+ }
+ if ($self->_get_data_series()->{'column'}) {
+ $self->_draw_columns();
+ }
+ if ($self->_get_data_series()->{'line'}) {
+ $self->_draw_lines();
+ }
+ return $self->_get_image();
+}
+
+sub _get_data_range {
+ my $self = shift;
+
+ my $max_value = 0;
+ my $min_value = 0;
+ my $column_count = 0;
+
+ my ($sc_min, $sc_max, $sc_cols) = $self->_get_stacked_column_range();
+ my ($c_min, $c_max, $c_cols) = $self->_get_column_range();
+ my ($l_min, $l_max, $l_cols) = $self->_get_line_range();
+
+ # These are side by side...
+ $sc_cols += $c_cols;
+
+ $min_value = $self->_min(0, $sc_min, $c_min, $l_min);
+ $max_value = $self->_max(0, $sc_max, $c_max, $l_max);
+
+ my $range_padding = $self->_get_number('range_padding');
+ if (!defined $range_padding) {
+ $range_padding = 10;
+ }
+ if ($range_padding && $min_value < 0) {
+ my $difference = $min_value * $range_padding / 100;
+ if ($min_value < -1 && $difference > -1) {
+ $difference = -1;
+ }
+ $min_value += $difference;
+ }
+ if ($range_padding && $max_value > 0) {
+ my $difference = $max_value * $range_padding / 100;
+ if ($max_value > 1 && $difference < 1) {
+ $difference = 1;
+ }
+ $max_value += $difference;
+ }
+
+ $column_count = $self->_max(0, $sc_cols, $l_cols);
+
+ $self->_set_max_value($max_value);
+ $self->_set_min_value($min_value);
+ $self->_set_column_count($column_count);
+}
+
+sub _min {
+ my $self = shift;
+ my $min = shift;
+
+ foreach my $value (@_) {
+ if ($value < $min) { $min = $value; }
+ }
+ return $min;
+}
+
+sub _max {
+ my $self = shift;
+ my $min = shift;
+
+ foreach my $value (@_) {
+ if ($value > $min) { $min = $value; }
+ }
+ return $min;
+}
+
+sub _get_line_range {
+ my $self = shift;
+ my $series = $self->_get_data_series()->{'line'};
+ return (0, 0, 0) unless $series;
+
+ my $max_value = 0;
+ my $min_value = 0;
+ my $column_count = 0;
+
+ my @series = @{$series};
+ foreach my $series (@series) {
+ my @data = @{$series->{'data'}};
+
+ if (scalar @data > $column_count) {
+ $column_count = scalar @data;
+ }
+
+ foreach my $value (@data) {
+ if ($value > $max_value) { $max_value = $value; }
+ if ($value < $min_value) { $min_value = $value; }
+ }
+ }
+
+ return ($min_value, $max_value, $column_count);
+}
+
+sub _get_column_range {
+ my $self = shift;
+
+ my $series = $self->_get_data_series()->{'column'};
+ return (0, 0, 0) unless $series;
+
+ my $max_value = 0;
+ my $min_value = 0;
+ my $column_count = 0;
+
+ my @series = @{$series};
+ foreach my $series (@series) {
+ my @data = @{$series->{'data'}};
+
+ foreach my $value (@data) {
+ $column_count++;
+ if ($value > $max_value) { $max_value = $value; }
+ if ($value < $min_value) { $min_value = $value; }
+ }
+ }
+
+ return ($min_value, $max_value, $column_count);
+}
+
+sub _get_stacked_column_range {
+ my $self = shift;
+
+ my $max_value = 0;
+ my $min_value = 0;
+ my $column_count = 0;
+
+ return (0, 0, 0) unless $self->_get_data_series()->{'stacked_column'};
+ my @series = @{$self->_get_data_series()->{'stacked_column'}};
+
+ my @max_entries;
+ my @min_entries;
+ for (my $i = scalar @series - 1; $i >= 0; $i--) {
+ my $series = $series[$i];
+ my $data = $series->{'data'};
+
+ for (my $i = 0; $i < scalar @$data; $i++) {
+ my $value = 0;
+ if ($data->[$i] > 0) {
+ $value = $data->[$i] + ($max_entries[$i] || 0);
+ $data->[$i] = $value;
+ $max_entries[$i] = $value;
+ }
+ elsif ($data->[$i] < 0) {
+ $value = $data->[$i] + ($min_entries[$i] || 0);
+ $data->[$i] = $value;
+ $min_entries[$i] = $value;
+ }
+ if ($value > $max_value) { $max_value = $value; }
+ if ($value < $min_value) { $min_value = $value; }
+ }
+ if (scalar @$data > $column_count) {
+ $column_count = scalar @$data;
+ }
+ }
+
+ return ($min_value, $max_value, $column_count);
+}
+
+sub _draw_legend {
+ my $self = shift;
+ my $style = $self->{'_style'};
+
+ my @labels;
+ my $img = $self->_get_image();
+ if (my $series = $self->_get_data_series()->{'stacked_column'}) {
+ push @labels, map { $_->{'series_name'} } @$series;
+ }
+ if (my $series = $self->_get_data_series()->{'column'}) {
+ push @labels, map { $_->{'series_name'} } @$series;
+ }
+ if (my $series = $self->_get_data_series()->{'line'}) {
+ push @labels, map { $_->{'series_name'} } @$series;
+ }
+
+ if ($style->{features}{legend} && (scalar @labels)) {
+ $self->SUPER::_draw_legend($self->_get_image(), \@labels, $self->_get_image_box())
+ or return;
+ }
+ return;
+}
+
+sub _draw_flat_legend {
+ return 1;
+}
+
+sub _draw_lines {
+ my $self = shift;
+ my $style = $self->{'_style'};
+
+ my $img = $self->_get_image();
+
+ my $max_value = $self->_get_max_value();
+ my $min_value = $self->_get_min_value();
+ my $column_count = $self->_get_column_count();
+
+ my $value_range = $max_value - $min_value;
+
+ my $width = $self->_get_number('width');
+ my $height = $self->_get_number('height');
+ my $size = $self->_get_number('size');
+
+ my $bottom = ($height - $size) / 2;
+ my $left = ($width - $size) / 2;
+
+ my $zero_position = $bottom + $size - (-1*$min_value / $value_range) * ($size -1);
+
+ if ($self->_get_y_tics()) {
+ $self->_draw_y_tics();
+ }
+ if ($self->_get_labels()) {
+ $self->_draw_x_tics();
+ }
+
+ my $line_series = $self->_get_data_series()->{'line'};
+ my $series_counter = $self->_get_series_counter() || 0;
+
+ my $has_columns = (defined $self->_get_data_series()->{'column'} || $self->_get_data_series->{'stacked_column'}) ? 1 : 0;
+
+ my $col_width = int($size / $column_count) -1;
+ my $graph_width = $col_width * $column_count + 1;
+
+ foreach my $series (@$line_series) {
+ my @data = @{$series->{'data'}};
+ my $data_size = scalar @data;
+
+ my $interval;
+ if ($has_columns) {
+ $interval = $graph_width / ($data_size);
+ }
+ else {
+ $interval = $graph_width / ($data_size - 1);
+ }
+ my @fill = $self->_data_fill($series_counter, $self->_get_graph_box());
+ my $color = $self->_data_color($series_counter);
+ for (my $i = 0; $i < $data_size - 1; $i++) {
+ my $x1 = $left + $i * $interval;
+ my $x2 = $left + ($i + 1) * $interval;
+
+ $x1 += $has_columns * $interval / 2;
+ $x2 += $has_columns * $interval / 2;
+
+ my $y1 = $bottom + ($value_range - $data[$i] + $min_value)/$value_range * $size;
+ my $y2 = $bottom + ($value_range - $data[$i + 1] + $min_value)/$value_range * $size;
+
+ $img->line(x1 => $x1, y1 => $y1, x2 => $x2, y2 => $y2, aa => 1, color => $color) || die $img->errstr;
+ $img->circle(x => $x1, y => $y1, r => 3, aa => 1, filled => 1, @fill);
+ }
+
+ my $x2 = $left + ($data_size - 1) * $interval;
+ $x2 += $has_columns * $interval / 2;
+
+ my $y2 = $bottom + ($value_range - $data[$data_size - 1] + $min_value)/$value_range * $size;
+
+ $img->circle(x => $x2, y => $y2, r => 3, aa => 1, filled => 1, @fill);
+ $series_counter++;
+ }
+
+ $self->_set_series_counter($series_counter);
+ return;
+}
+
+sub _draw_columns {
+ my $self = shift;
+ my $style = $self->{'_style'};
+
+ my $img = $self->_get_image();
+
+ my $max_value = $self->_get_max_value();
+ my $min_value = $self->_get_min_value();
+ my $column_count = $self->_get_column_count();
+
+ my $value_range = $max_value - $min_value;
+
+ my $width = $self->_get_number('width');
+ my $height = $self->_get_number('height');
+ my $size = $self->_get_number('size');
+
+ my $bottom = ($height - $size) / 2;
+ my $left = ($width - $size) / 2 + 1;
+
+ my $zero_position = int($bottom + $size - (-1*$min_value / $value_range) * ($size -1));
+
+ if ($self->_get_y_tics()) {
+ $self->_draw_y_tics();
+ }
+ if ($self->_get_labels()) {
+ $self->_draw_x_tics();
+ }
+
+ my $bar_width = int(($size)/ $column_count - 2);
+
+ my $outline_color;
+ if ($style->{'features'}{'outline'}) {
+ $outline_color = $self->_get_color('outline.line');
+ }
+
+ my $series_counter = $self->_get_series_counter() || 0;
+ my $col_series = $self->_get_data_series()->{'column'};
+
+ # This tracks the series we're in relative to the starting series - this way colors stay accurate, but the columns don't start out too far to the right.
+ my $column_series = 0;
+
+ # If there are stacked columns, non-stacked columns need to start one to the right of where they would otherwise
+ my $has_stacked_columns = (defined $self->_get_data_series()->{'stacked_column'} ? 1 : 0);
+
+ for (my $series_pos = 0; $series_pos < scalar @$col_series; $series_pos++) {
+ my $series = $col_series->[$series_pos];
+ my @data = @{$series->{'data'}};
+ my $data_size = scalar @data;
+ my $color = $self->_data_color($series_counter);
+ for (my $i = 0; $i < $data_size; $i++) {
+ my $x1 = int($left + $bar_width * (scalar @$col_series * $i + $series_pos)) + scalar @$col_series * $i + $series_pos;
+ if ($has_stacked_columns) {
+ $x1 += ($i + 1) * $bar_width + $i + 1;
+ }
+ my $x2 = $x1 + $bar_width;
+
+ my $y1 = int($bottom + ($value_range - $data[$i] + $min_value)/$value_range * $size);
+
+ my $color = $self->_data_color($series_counter);
+
+ # my @fill = $self->_data_fill($series_counter, [$x1, $y1, $x2, $zero_position]);
+ if ($data[$i] > 0) {
+ $img->box(xmin => $x1, xmax => $x2, ymin => $y1, ymax => $zero_position-1, color => $color, filled => 1);
+ if ($style->{'features'}{'outline'}) {
+ $img->box(xmin => $x1, xmax => $x2, ymin => $y1, ymax => $zero_position, color => $outline_color);
+ }
+ }
+ else {
+ $img->box(xmin => $x1, xmax => $x2, ymin => $zero_position+1, ymax => $y1, color => $color, filled => 1);
+ if ($style->{'features'}{'outline'}) {
+ $img->box(xmin => $x1, xmax => $x2, ymin => $zero_position+1, ymax => $y1+1, color => $outline_color);
+ }
+ }
+ }
+
+ $series_counter++;
+ $column_series++;
+ }
+ $self->_set_series_counter($series_counter);
+ return;
+}
+
+sub _draw_stacked_columns {
+ my $self = shift;
+ my $style = $self->{'_style'};
+
+ my $img = $self->_get_image();
+
+ my $max_value = $self->_get_max_value();
+ my $min_value = $self->_get_min_value();
+ my $column_count = $self->_get_column_count();
+ my $value_range = $max_value - $min_value;
+
+ my $graph_box = $self->_get_graph_box();
+ my $left = $graph_box->[0] + 1;
+ my $bottom = $graph_box->[1];
+ my $size = $self->_get_number('size');
+
+ if ($self->_get_y_tics()) {
+ $self->_draw_y_tics();
+ }
+ if ($self->_get_labels()) {
+ $self->_draw_x_tics();
+ }
+
+ my $bar_width = int($size / $column_count -2);
+ my $column_series = 0;
+ if (my $column_series_data = $self->_get_data_series()->{'column'}) {
+ $column_series = (scalar @$column_series_data);
+ }
+ $column_series++;
+
+ my $outline_color;
+ if ($style->{'features'}{'outline'}) {
+ $outline_color = $self->_get_color('outline.line');
+ }
+
+ my $zero_position = $bottom + $size - (-1*$min_value / $value_range) * ($size -1);
+ my $col_series = $self->_get_data_series()->{'stacked_column'};
+ my $series_counter = $self->_get_series_counter() || 0;
+ foreach my $series (@$col_series) {
+ my @data = @{$series->{'data'}};
+ my $data_size = scalar @data;
+ my $color = $self->_data_color($series_counter);
+ for (my $i = 0; $i < $data_size; $i++) {
+ my $x1 = int($left + $bar_width * ($column_series * $i)) + $column_series * $i;
+# my $x1 = $left + $i * $size / ($data_size);
+ my $x2 = $x1 + $bar_width;
+
+ my $y1 = $bottom + ($value_range - $data[$i] + $min_value)/$value_range * $size;
+
+ if ($data[$i] > 0) {
+ $img->box(xmin => $x1, xmax => $x2, ymin => $y1, ymax => $zero_position-1, color => $color, filled => 1);
+ if ($style->{'features'}{'outline'}) {
+ $img->box(xmin => $x1, xmax => $x2, ymin => $y1, ymax => $zero_position, color => $outline_color);
+ }
+ }
+ else {
+ $img->box(xmin => $x1, xmax => $x2, ymin => $zero_position+1, ymax => $y1, color => $color, filled => 1);
+ if ($style->{'features'}{'outline'}) {
+ $img->box(xmin => $x1, xmax => $x2, ymin => $zero_position+1, ymax => $y1+1, color => $outline_color);
+ }
+ }
+ }
+
+ $series_counter++;
+ }
+ $self->_set_series_counter($series_counter);
+ return;
+}
+
+sub _add_data_series {
+ my $self = shift;
+ my $series_type = shift;
+ my $data_ref = shift;
+ my $series_name = shift;
+
+ my $graph_data = $self->{'graph_data'} || {};
+
+ my $series = $graph_data->{$series_type} || [];
+
+ push @$series, { data => $data_ref, series_name => $series_name };
+
+ $graph_data->{$series_type} = $series;
+
+ $self->{'graph_data'} = $graph_data;
+ return;
+}
+
+=over
+
+=item set_y_tics($count)
+
+Set the number of Y tics to use. Their value and position will be determined by the data range.
+
+=cut
+
+sub set_y_tics {
+ $_[0]->{'y_tics'} = $_[1];
+}
+
+sub _get_y_tics {
+ return $_[0]->{'y_tics'};
+}
+
+sub _draw_y_tics {
+ my $self = shift;
+ my $min = $self->_get_min_value();
+ my $max = $self->_get_max_value();
+ my $tic_count = $self->_get_y_tics();
+
+ my $img = $self->_get_image();
+ my $graph_box = $self->_get_graph_box();
+ my $image_box = $self->_get_image_box();
+
+ my $interval = ($max - $min) / ($tic_count - 1);
+
+ my %text_info = $self->_text_style('legend')
+ or return;
+
+ my $tic_distance = ($graph_box->[3] - $graph_box->[1]) / ($tic_count - 1);
+ for my $count (0 .. $tic_count - 1) {
+ my $x1 = $graph_box->[0] - 5;
+ my $x2 = $graph_box->[0] + 5;
+ my $y1 = $graph_box->[3] - ($count * $tic_distance);
+
+ my $value = sprintf("%.2f", ($count*$interval)+$min);
+ if ($value < 0) {
+ $y1++;
+ }
+ my @box = $self->_text_bbox($value, 'legend')
+ or return;
+
+ $img->line(x1 => $x1, x2 => $x2, y1 => $y1, y2 => $y1, aa => 1, color => '000000');
+
+ my $width = $box[2];
+ my $height = $box[3];
+
+ $img->string(%text_info,
+ x => ($x1 - $width - 3),
+ y => ($y1 + ($height / 2)),
+ text => $value
+ );
+ }
+
+}
+
+sub _draw_x_tics {
+ my $self = shift;
+
+ my $img = $self->_get_image();
+ my $graph_box = $self->_get_graph_box();
+ my $image_box = $self->_get_image_box();
+
+ my $labels = $self->_get_labels();
+
+ my $tic_count = (scalar @$labels) - 1;
+
+ my $has_columns = (defined $self->_get_data_series()->{'column'} || defined $self->_get_data_series()->{'stacked_column'});
+
+ # If we have columns, we want the x-ticks to show up in the middle of the column, not on the left edge
+ my $denominator = $tic_count;
+ if ($has_columns) {
+ $denominator ++;
+ }
+ my $tic_distance = ($graph_box->[2] - $graph_box->[0]) / ($denominator);
+ my %text_info = $self->_text_style('legend')
+ or return;
+
+ for my $count (0 .. $tic_count) {
+ my $label = $labels->[$count];
+ my $x1 = $graph_box->[0] + ($tic_distance * $count);
+
+ if ($has_columns) {
+ $x1 += $tic_distance / 2;
+ }
+ my $y1 = $graph_box->[3] + 5;
+ my $y2 = $graph_box->[3] - 5;
+
+ $img->line(x1 => $x1, x2 => $x1, y1 => $y1, y2 => $y2, aa => 1, color => '000000');
+
+ my @box = $self->_text_bbox($label, 'legend')
+ or return;
+
+ my $width = $box[2];
+ my $height = $box[3];
+
+ $img->string(%text_info,
+ x => ($x1 - ($width / 2)),
+ y => ($y1 + ($height + 5)),
+ text => $label
+ );
+
+ }
+}
+
+sub _valid_input {
+ my $self = shift;
+
+ if (!defined $self->_get_data_series() || !keys %{$self->_get_data_series()}) {
+ return $self->_error("No data supplied");
+ }
+
+ my $data = $self->_get_data_series();
+ if (defined $data->{'line'} && !scalar @{$data->{'line'}->[0]->{'data'}}) {
+ return $self->_error("No values in data series");
+ }
+ if (defined $data->{'column'} && !scalar @{$data->{'column'}->[0]->{'data'}}) {
+ return $self->_error("No values in data series");
+ }
+ if (defined $data->{'stacked_column'} && !scalar @{$data->{'stacked_column'}->[0]->{'data'}}) {
+ return $self->_error("No values in data series");
+ }
+
+ return 1;
+}
+
+sub _set_column_count { $_[0]->{'column_count'} = $_[1]; }
+sub _set_min_value { $_[0]->{'min_value'} = $_[1]; }
+sub _set_max_value { $_[0]->{'max_value'} = $_[1]; }
+sub _set_image_box { $_[0]->{'image_box'} = $_[1]; }
+sub _set_graph_box { $_[0]->{'graph_box'} = $_[1]; }
+sub _set_series_counter { $_[0]->{'series_counter'} = $_[1]; }
+sub _get_column_count { return $_[0]->{'column_count'} }
+sub _get_min_value { return $_[0]->{'min_value'} }
+sub _get_max_value { return $_[0]->{'max_value'} }
+sub _get_image_box { return $_[0]->{'image_box'} }
+sub _get_graph_box { return $_[0]->{'graph_box'} }
+sub _get_series_counter { return $_[0]->{'series_counter'} }
+
+
+
+1;
--- /dev/null
+#!perl -w
+use strict;
+use Imager::Graph::Line;
+use lib 't/lib';
+use Imager::Font::Test;
+use Test::More;
+
+-d 'testout'
+ or mkdir "testout", 0700
+ or die "Could not create output directory: $!";
+
+++$|;
+
+use Imager qw(:handy);
+
+plan tests => 2;
+
+#my $fontfile = 'ImUgly.ttf';
+#my $font = Imager::Font->new(file=>$fontfile, type => 'ft2', aa=>1)
+# or plan skip_all => "Cannot create font object: ",Imager->errstr,"\n";
+my $font = Imager::Font::Test->new();
+
+my @data = ( 100, 180, 80, 20, 2, 1, 0.5 );
+my @labels = qw(alpha beta gamma delta epsilon phi gi);
+
+my $line = Imager::Graph::Line->new();
+ok($line, "creating line chart object");
+
+$line->add_data_series(\@data);
+$line->set_labels(\@labels);
+
+my $img1 = $line->draw();
+ok($img1, "drawing line chart");
+
+$img1->write(file=>'testout/t11_basic.ppm') or die "Can't save img1: ".$img1->errstr."\n";
+
+
--- /dev/null
+#!perl -w
+use strict;
+use Imager::Graph::Column;
+use lib 't/lib';
+use Imager::Font::Test;
+use Test::More;
+
+-d 'testout'
+ or mkdir "testout", 0700
+ or die "Could not create output directory: $!";
+
+++$|;
+
+use Imager qw(:handy);
+
+plan tests => 2;
+
+#my $fontfile = 'ImUgly.ttf';
+#my $font = Imager::Font->new(file=>$fontfile, type => 'ft2', aa=>1)
+# or plan skip_all => "Cannot create font object: ",Imager->errstr,"\n";
+my $font = Imager::Font::Test->new();
+
+my @data = ( 100, 180, 80, 20, 2, 1, 0.5 );
+my @labels = qw(alpha beta gamma delta epsilon phi gi);
+
+my $column = Imager::Graph::Column->new();
+ok($column, "creating column chart object");
+
+$column->add_data_series(\@data);
+$column->set_labels(\@labels);
+
+my $img1 = $column->draw();
+ok($img1, "drawing column chart");
+
+$img1->write(file=>'testout/t12_basic.ppm') or die "Can't save img1: ".$img1->errstr."\n";
+
+
--- /dev/null
+#!perl -w
+use strict;
+use Imager::Graph::StackedColumn;
+use lib 't/lib';
+use Imager::Font::Test;
+use Test::More;
+
+-d 'testout'
+ or mkdir "testout", 0700
+ or die "Could not create output directory: $!";
+
+++$|;
+
+use Imager qw(:handy);
+
+plan tests => 2;
+
+#my $fontfile = 'ImUgly.ttf';
+#my $font = Imager::Font->new(file=>$fontfile, type => 'ft2', aa=>1)
+# or plan skip_all => "Cannot create font object: ",Imager->errstr,"\n";
+my $font = Imager::Font::Test->new();
+
+my @data = ( 100, 180, 80, 20, 2, 1, 0.5 );
+my @labels = qw(alpha beta gamma delta epsilon phi gi);
+
+my $stacked_col = Imager::Graph::StackedColumn->new();
+ok($stacked_col, "creating stacked_col chart object");
+
+$stacked_col->add_data_series(\@data);
+$stacked_col->set_labels(\@labels);
+
+my $img1 = $stacked_col->draw();
+ok($img1, "drawing stacked_col chart");
+
+$img1->write(file=>'testout/t13_basic.ppm') or die "Can't save img1: ".$img1->errstr."\n";
+
+
plan tests => 3;
-#my $fontfile = 'ImUgly.ttf';
+#my $fontfile = 'Im_ugly.ttf';
#my $font = Imager::Font->new(file=>$fontfile, type => 'ft2', aa=>1)
# or plan skip_all => "Cannot create font object: ",Imager->errstr,"\n";
my $font = Imager::Font::Test->new();
my $api_pie = Imager::Graph::Pie->new();
-$api_pie->addDataSeries(\@data, 'Demo series');
-$api_pie->setFont($font);
-$api_pie->setLabels(\@labels);
-$api_pie->setGraphSize(50);
-$api_pie->setImageWidth(200);
-$api_pie->setImageHeight(200);
-$api_pie->setTitle('Test 20');
-$api_pie->setStyle('fount_rad');
+$api_pie->add_data_series(\@data, 'Demo series');
+$api_pie->set_font($font);
+$api_pie->set_labels(\@labels);
+$api_pie->set_graph_size(50);
+$api_pie->set_image_width(200);
+$api_pie->set_image_height(200);
+$api_pie->set_title('Test 20');
+$api_pie->set_style('fount_rad');
my $api_img = $api_pie->draw();
ok($api_img);
my ($api_content, $data_content);
-$data_img->write(data => \$data_content, type=>'tiff', tiff_compression => 'none') or die "Err: ".$data_img->errstr;
-$api_img->write(data => \$api_content, type=>'tiff', tiff_compression => 'none') or die "Err: ".$api_img->errstr;
+$data_img->write(data => \$data_content, type=>'raw') or die "Err: ".$data_img->errstr;
+$api_img->write(data => \$api_content, type=>'raw') or die "Err: ".$api_img->errstr;
ok($data_content eq $api_content);
+# These are just here to make it easy to see why the above test failed, if it did
+$data_img->write(file=>'testout/t20_data.ppm')or die "Error: ".$data_img->errstr."\n";
+$api_img->write(file=>'testout/t20_api.ppm')or die "Error: ".$api_img->errstr."\n";
--- /dev/null
+#!perl -w
+use strict;
+use Imager::Graph::Pie;
+use lib 't/lib';
+use Imager::Font::Test;
+use Test::More;
+
+++$|;
+
+use Imager qw(:handy);
+
+plan tests => 3;
+
+my $font = Imager::Font::Test->new();
+
+my @data;
+
+for (0 .. 10) {
+ push @data, $_;
+}
+
+my $api_graph = Imager::Graph::Pie->new();
+$api_graph->add_data_series(\@data, 'Positive Slope');
+$api_graph->set_style('ocean');
+$api_graph->set_labels([0 .. 10]);
+
+$api_graph->set_image_width(800);
+$api_graph->set_image_height(600);
+$api_graph->set_graph_size(500);
+$api_graph->set_font($font);
+$api_graph->set_image_background('FF00FF');
+$api_graph->set_channels(3);
+$api_graph->set_line_color('00FF00');
+$api_graph->set_title('Tester Title');
+$api_graph->set_title_font_size(14);
+$api_graph->set_title_font_color('444444');
+$api_graph->set_title_horizontal_align('left');
+$api_graph->set_title_vertical_align('bottom');
+$api_graph->set_text_font_size(18);
+$api_graph->set_text_font_color('FFFFFF');
+$api_graph->set_graph_background_color('00FF00');
+$api_graph->set_graph_foreground_color('FF00FF');
+$api_graph->set_legend_font_color('0000FF');
+$api_graph->set_legend_font($font);
+$api_graph->set_legend_font_size(17);
+$api_graph->set_legend_patch_size(30);
+$api_graph->set_legend_patch_gap(20);
+$api_graph->set_legend_horizontal_align('left');
+$api_graph->set_legend_vertical_align('top');
+$api_graph->set_legend_padding(5);
+$api_graph->set_legend_outside_padding(12);
+$api_graph->set_legend_fill('000000');
+$api_graph->set_legend_border('222222');
+$api_graph->set_legend_orientation('horizontal');
+$api_graph->set_callout_font_color('FF0000');
+$api_graph->set_callout_font($font);
+$api_graph->set_callout_font_size(45);
+$api_graph->set_callout_line_color('FF2211');
+$api_graph->set_callout_leader_inside_length(10);
+$api_graph->set_callout_leader_outside_length(20);
+$api_graph->set_callout_leader_length(30);
+$api_graph->set_callout_gap(5);
+$api_graph->set_label_font_color('55FFFF');
+$api_graph->set_label_font($font);
+$api_graph->set_label_font_size(16);
+$api_graph->set_drop_shadow_fill_color('113333');
+$api_graph->set_drop_shadow_offset(25);
+$api_graph->set_drop_shadowXOffset(30);
+$api_graph->set_drop_shadowYOffset(5);
+$api_graph->set_drop_shadow_filter({ type=>'mosaic', size => 20 });
+$api_graph->set_outline_color('FF00FF');
+$api_graph->set_data_area_fills([qw(FF0000 00FF00 0000FF)]);
+$api_graph->set_data_line_colors([qw(FF0000 00FF00 0000FF)]);
+
+my $api_img = $api_graph->draw(
+ features => [qw(legend outline labels)],
+) || die $api_graph->error;
+
+ok($api_img);
+
+my $style_graph = Imager::Graph::Pie->new();
+
+$style_graph->add_data_series(\@data, 'Positive Slope');
+$style_graph->set_style('ocean');
+$style_graph->set_labels([0 .. 10]);
+
+my $style_img = $style_graph->draw(
+ features => [qw(legend outline labels)],
+ font => $font, # base font * set_font()
+ back => 'FF00FF', # Background color/fill - set_image_background()
+ size => 500, # Size of the graph * set_size()
+ width => 800, # width of the image * set_width()
+ height => 600, # height of the image * set_height()
+ channels => 3, # # of channels in the image - set_channels()
+ line => '00FF00', # color of lines - set_line_color()
+ title => {
+ text => 'Tester Title', # title for the chart * set_title()
+ size => '14', # size of the title font - set_title_font_size()
+ color => '444444', # color of the title - set_title_font_color()
+ halign => 'left', # horizontal alignment of the title - set_title_horizontal_align()
+ valign => 'bottom', # vertical alignment of the title - set_title_vertical_align()
+ },
+ text => {
+ color => 'FFFFFF', # default color of text - set_text_font_color()
+ size => '18', # default size of text - set_text_font_size()
+ },
+ bg => '00FF00', # background color of the graph - set_graph_background_color()
+ fg => 'FF00FF', # foreground color of the graph - set_graph_foreground_color()
+ legend => {
+ color => '0000FF', # text color for the legend - set_legend_font_color()
+ font => $font, # font to be used for the legend - set_legend_font()
+ size => 17, # font size to be used for labels
+ # in the legend - set_legend_font_size()
+ patchsize => 30, # the size in pixels? percent? - set_legend_patch_size()
+ # of the color patches in
+ # the legend.
+ patchgap => 20, # gap between the color patches. - set_legend_patch_gap()
+ # in pixels? percent?
+ halign => 'left', # horizontal alignment of the - set_legend_horizontal_align()
+ # legend within the graph
+ valign => 'top', # vertical alignment of the - set_legend_vertical_align()
+ # legend within the graph
+ padding => '5', # the space between the patches - set_legend_padding()
+ # of color and the outside of
+ # the legend box
+ outsidepadding => '12', # the space between the - set_legend_outside_padding()
+ # border of the legend,
+ # and the outside edge of the
+ # legend
+ fill => '000000', # A fill for the background - set_legend_fill()
+ # of the legend.
+ border => '222222', # The color of the border of - set_legend_border()
+ # the legend.
+ orientation => 'horizontal', # the orientation of the - set_legend_orientation()
+ # legend
+ },
+ callout => {
+ color => 'FF0000', # the color of the callout text - set_callout_font_color()
+ font => $font, # the font to use for callouts - set_callout_font()
+ size => 45, # the font size for callout text - set_callout_font_size()
+ line => 'FF2211', # the color of the line from the - set_callout_line_color()
+ # callout to the graph
+ inside => '10', # the length in pixels? of the - set_callout_leader_inside_length()
+ # leader...
+ outside => '20', # the other side of the leader? - set_callout_leader_outside_length()
+ leadlen => '30', # the length of the horizontal - set_callout_leader_length()
+ # part of the leader
+ gap => '5', # the space between the callout - set_callout_gap()
+ # leader and the callout text
+ },
+ label => {
+ color => '55FFFF', # the color of the label text - set_label_font_color()
+ font => $font, # font used for labels - set_label_font()
+ size => 16, # the font size used for labels - set_label_font_size()
+ },
+ dropshadow => {
+ fill => '113333', # the color used for drop shadows - set_drop_shadow_fill_color()
+ off => 25, # the offset of the dropshadow... - set_drop_shadow_offset()
+ # in percent? pixels?
+ offx => 30, # horizontal offset of the - set_drop_shadowXOffset()
+ # dropshadow
+ offy => 5, # vertical offset of the dropshadow - set_drop_shadowYOffset()
+ filter => { type=>'mosaic', size => 20 },
+
+ # the filter description passed to - set_drop_shadow_filter()
+ # Imager's filter method to blur
+ # the drop shadow. Default: an 11
+ # element convolution filter.
+ },
+ outline => {
+ line => 'FF00FF', # the color of the outline - set_outline_color()
+ # around data areas
+ },
+ fills => [
+ qw(FF0000 00FF00 0000FF)
+ # An array ref describing how to fill data areas - set_data_area_fills()
+ # in the graph. used by pie, column, stacked
+ # column graphs
+ ],
+ colors => [
+ qw(FF0000 00FF00 0000FF)
+ # An array ref of colors, used by line graphs. - set_data_line_colors()
+ ],
+
+) || die $style_graph->error;
+
+ok($api_img);
+
+my ($api_content, $style_content);
+
+$style_img->write(data => \$style_content, type=>'raw') or die "Err: ".$style_img->errstr;
+$api_img->write(data => \$api_content, type=>'raw') or die "Err: ".$api_img->errstr;
+
+open (my $file, ">", '/var/www/tmp/pie_api.tiff');
+print $file $api_content;
+close $file;
+
+ok($style_content eq $api_content);
+
+
+