changes from Patrick + manifest update
authorpmichaud <pmichaud@pobox.com>
Mon, 13 Apr 2009 22:46:29 +0000 (22:46 +0000)
committerTony Cook <tony@develop-help.com>
Mon, 13 Apr 2009 22:46:29 +0000 (22:46 +0000)
13 files changed:
Graph.pm
MANIFEST
MANIFEST.SKIP
lib/Imager/Graph/Column.pm
lib/Imager/Graph/Line.pm
lib/Imager/Graph/Pie.pm
lib/Imager/Graph/StackedColumn.pm
lib/Imager/Graph/Vertical.pm [new file with mode: 0644]
t/t11line.t [new file with mode: 0644]
t/t12column.t [new file with mode: 0644]
t/t13stacked_col.t [new file with mode: 0644]
t/t20api.t
t/t21style_api.t [new file with mode: 0644]

index eb2f546ef45bebfe69bea1c7a0cc15c8dd2f976a..c200dabe0dba8c1f2030aca51cd8f411917f4b22 100644 (file)
--- a/Graph.pm
+++ b/Graph.pm
@@ -7,8 +7,8 @@ Imager::Graph - Perl extension for producing Graphs using the Imager library.
 
 =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;
 
@@ -45,55 +45,43 @@ sub new {
   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;
@@ -106,7 +94,7 @@ sub addDataSeries {
   return;
 }
 
-sub _getDataSeries {
+sub _get_data_series {
   my ($self, $opts) = @_;
 
   # return the data supplied to draw() if any.
@@ -124,17 +112,17 @@ sub _getDataSeries {
   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}
@@ -143,45 +131,37 @@ sub _getLabels {
   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}
@@ -204,7 +184,8 @@ sub error {
 
 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
 
@@ -215,7 +196,7 @@ the value can be a hashref containing sub values.
 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,
@@ -286,11 +267,418 @@ the data and a linear blue to green fill for the background.
 
 =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
 
@@ -876,6 +1264,7 @@ my %styles =
      qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
     ],
     fg=>'000000',
+    negative_bg=>'EEEEEE',
     bg=>'E0E0E0',
     legend=>
     {
@@ -889,6 +1278,7 @@ my %styles =
      qw(FF0000 00FF00 0000FF C0C000 00C0C0 FF00FF)
     ],
     fg=>'000000',
+    negative_bg=>'EEEEEE',
     bg=>'C08080',
     legend=>
     {
@@ -911,6 +1301,7 @@ my %styles =
     channels=>1,
     bg=>'FFFFFF',
     fg=>'000000',
+    negative_bg=>'EEEEEE',
     features=>{ outline=>1 },
     pie =>{
            blur=>undef,
@@ -961,6 +1352,7 @@ my %styles =
             ( positions=>[0, 1],
               colors=>[ NC('6060FF'), NC('60FF60') ]) },
     fg=>'000000',
+    negative_bg=>'EEEEEE',
     bg=>'FFFFFF',
     features=>{ dropshadow=>1 },
    },
@@ -1009,10 +1401,73 @@ my %styles =
             ( 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
@@ -1025,22 +1480,13 @@ sub _style_setup {
   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();
@@ -1407,6 +1853,15 @@ sub _make_img {
   $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()
@@ -1725,8 +2180,15 @@ sub _draw_legend_vertical {
   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}) {
@@ -1783,6 +2245,10 @@ sub _small_extent {
   }
 }
 
+sub _draw_flat_legend {
+  return 0;
+}
+
 =item _composite()
 
 Returns a list of style fields that are stored as composites, and
index a631d73070296076b9ef092d57327c2ded636b75..89533c0cb04bee9323818d1e4f704f907c263030 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -6,6 +6,7 @@ lib/Imager/Graph/Line.pm
 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
@@ -14,7 +15,11 @@ README
 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
index 2c43f0eece50d32645971c1458b47ec33a9877d8..9b11164750f6fcdb841569e6e29a3869906a1f27 100644 (file)
@@ -7,3 +7,4 @@
 ^Makefile\.old$
 ^pm_to_blib$
 \.diff$
+\.rej$
index 099141a5a02f726d344fd1d7aee30ffef2443a1d..5aa9e31b5205155b3cfdcf225496245f94fc28bb 100644 (file)
@@ -12,242 +12,12 @@ package Imager::Graph::Column;
 
 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;
 
index f69079ce08a8f9eb58f62d5c622d26bf62f1b501..fc9afa1882795643bb32c53d303694ec3b05960e 100644 (file)
@@ -12,226 +12,12 @@ package Imager::Graph::Line;
 
 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;
 
index fb0ea8bca91fa18f3718a99508f0d05d0a0ae691..26caa768639d7a154b8b320304a5852cc6998c43 100644 (file)
@@ -56,6 +56,42 @@ your graph.  The features you can use with pie graphs are:
 
 =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
@@ -147,7 +183,7 @@ Assuming:
   # 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;
 
@@ -193,14 +229,14 @@ suitable for monochrome output:
 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);
 
@@ -387,7 +423,7 @@ sub draw {
         #          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;
@@ -415,11 +451,11 @@ sub draw {
       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);
       }
     }
   }
@@ -427,7 +463,7 @@ sub draw {
   $img;
 }
 
-sub _validInput {
+sub _valid_input {
   my ($self, $data_series) = @_;
 
   if (!defined $data_series || !scalar @$data_series) {
@@ -478,7 +514,7 @@ sub _consolidate_segments {
   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);
index 2199ea259c50e954798ee048bb7b9cb8037f052b..d943d87b94d2f66adfcead34c81bfbd80ca9fea4 100644 (file)
@@ -12,258 +12,12 @@ package Imager::Graph::StackedColumn;
 
 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;
 
diff --git a/lib/Imager/Graph/Vertical.pm b/lib/Imager/Graph/Vertical.pm
new file mode 100644 (file)
index 0000000..71f96b9
--- /dev/null
@@ -0,0 +1,758 @@
+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;
diff --git a/t/t11line.t b/t/t11line.t
new file mode 100644 (file)
index 0000000..09fb7e9
--- /dev/null
@@ -0,0 +1,37 @@
+#!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";
+
+
diff --git a/t/t12column.t b/t/t12column.t
new file mode 100644 (file)
index 0000000..abdb88c
--- /dev/null
@@ -0,0 +1,37 @@
+#!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";
+
+
diff --git a/t/t13stacked_col.t b/t/t13stacked_col.t
new file mode 100644 (file)
index 0000000..34c3781
--- /dev/null
@@ -0,0 +1,37 @@
+#!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";
+
+
index 97cfb2ab76a0e06f2f8d72f230b971d6a44f387d..60c367d23b10d72872d47a28c3610389af2a4598 100644 (file)
@@ -11,7 +11,7 @@ use Imager qw(:handy);
 
 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();
@@ -21,14 +21,14 @@ my @labels = qw(alpha beta gamma delta epsilon phi gi);
 
 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);
@@ -51,9 +51,12 @@ ok($data_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";
 
diff --git a/t/t21style_api.t b/t/t21style_api.t
new file mode 100644 (file)
index 0000000..6e305d3
--- /dev/null
@@ -0,0 +1,201 @@
+#!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);
+
+
+