in progress changes:
authorTony Cook <tony@develop-help.com>
Sun, 11 Jul 2010 12:03:47 +0000 (12:03 +0000)
committerTony Cook <tony@develop-help.com>
Sun, 11 Jul 2010 12:03:47 +0000 (12:03 +0000)
- vertical graph gridline control and styling

- vertical graph fill styling

- vertical graph outline control and styling

- no{feature} in arrayref/scalar features

- hoist line styling up

- labels in parameters for vertical graphs

- vertical gridline control and styling

- area chart integration

14 files changed:
Changes
Graph.pm
MANIFEST.SKIP
lib/Imager/Graph/Horizontal.pm
lib/Imager/Graph/Vertical.pm
t/t40area.t
t/x50vstyle.t [new file with mode: 0644]
testimg/t40area1.png
xtestimg/x50line_dashout.png [new file with mode: 0644]
xtestimg/x50line_def.png [new file with mode: 0644]
xtestimg/x50line_fill.png [new file with mode: 0644]
xtestimg/x50line_grid.png [new file with mode: 0644]
xtestimg/x50line_griddef.png [new file with mode: 0644]
xtestimg/x50line_noout.png [new file with mode: 0644]

diff --git a/Changes b/Changes
index 32a8c96..29209e3 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,5 +1,90 @@
 Revision history for Perl extension Imager::Graph.
 
+Imager-Graph 0.08 - unreleased
+=================
+
+Now depends on Imager 0.75.
+
+More cool changes from Patrick Michaud:
+
+ - Area charts
+
+Other changes:
+
+ - horizonal_gridlines is now a feature and can be enabled like other
+   features (or disabled)
+
+ - hoisted the dashed-line drawing into Imager::Graph::_line() and
+   added a _get_line() method to retrieve line style information.
+
+ - features enabled earlier in the style list can now be disabled with
+   a feature "no{stylename}" when features are supplied as an arrayref
+   or scalar. eg. "nograph_outline".  If you supply features as a hashref
+   you can disable a feature with:
+     { stylename => 0 }
+
+ - for vertical charts, allow the outline of the graph area
+   (graph_outline) to be disabled (with nograph_outline in features.)
+
+ - for vertical charts, allow the fill of the graph area to be
+   controlled separately from the base bg (graph.fill)
+
+ - tests
+
+Bug fixes:
+
+ - generate y-axis tic-label widths baed on the labels actually used.
+
+ - make draw() idempotent for vertical charts
+
+ - multiple calls to draw() no longer return the same image
+
+ - labels supplied to draw() are now used for vertical and horizontal
+   charts
+
+TODO:
+
+ - changes from Vertical into Horizontal too:
+
+   - gridlines control
+
+   - chart area outline control
+
+   - line style for gridlines
+
+   - line style for chart area outline
+
+ - control of drawing markers for:
+
+   - area
+
+   - line
+
+ - tic size/positioning
+
+ - gap between tic and labels
+
+ - tests:
+
+   - gridlines control for h
+
+   - gridlines styling for h
+
+   - graph area outline control for h
+
+   - graph area outline style for h
+
+   - test idempotent for h
+
+ - document:
+
+   - features: horizontal_gridlines, graph_outline, graph_fill
+
+   - graph outline and gridline styles
+
+   - document no{feature}
+
+
 Imager-Graph 0.07 - 21 May 2009
 =================
 
index 2f60fb3..fc96184 100644 (file)
--- a/Graph.pm
+++ b/Graph.pm
@@ -1593,7 +1593,12 @@ sub _style_setup {
       }
       else {
         # just set that single feature
-        $work{features}{$src->{features}} = 1;
+       if ($src->{features} =~ /^no(.+)$/) {
+         delete $features{$1};
+       }
+       else {
+         $features{$src->{features}} = 1;
+       }
       }
     }
   }
@@ -1863,7 +1868,7 @@ sub _data_color {
   return '000000';
 }
 
-=item _get_fill($index, $box)
+=item _get_fill($name, $box)
 
 Retrieves fill parameters for a named fill.
 
@@ -1887,6 +1892,67 @@ sub _get_fill {
   return $self->_translate_fill($what, $box, @depth);
 }
 
+=item _get_line($name)
+
+Return color (and possibly other) parameters for drawing a line with
+the _line() method.
+
+=cut
+
+sub _get_line {
+  my ($self, $name, @depth) = @_;
+
+  push (@depth, $name);
+  my $what;
+  if ($name =~ /^(\w+)\.(\w+)$/) {
+    $what = $self->{_style}{$1}{$2};
+  }
+  else {
+    $what = $self->{_style}{$name};
+  }
+
+  defined($what)
+    or return $self->_error("no line style $name found");
+
+  if (ref $what) {
+    if (eval { $what->isa("Imager::Color") }) {
+      return $what;
+    }
+    if (ref $what eq "HASH") {
+      # allow each kep to be looked up
+      my %work = %$what;
+
+      if ($work{color} =~ /^lookup\((.*)\)$/) {
+       $work{color} = $self->_get_color($1, @depth);
+      }
+      for my $key (keys %work) {
+       $key eq "color" and next;
+
+       if ($work{$key} =~ /^lookup\((.*)\)$/) {
+         $work{$key} = $self->_get_thing($1);
+       }
+      }
+
+      return %work;
+    }
+    return ( color => Imager::Color->new(@$what) );
+  }
+  else {
+    if ($what =~ /^lookup\((\w+(?:\.\w+)?)\)$/) {
+      @depth < MAX_DEPTH
+       or return $self->_error("too many levels of recursion in lookup (@depth)");
+      return $self->_get_line($1, @depth);
+    }
+    else {
+      # presumably a text color
+      my $color = Imager::Color->new($what)
+       or return $self->_error("Could not translate $what as a color: ".Imager->errstr);
+
+      return ( color => $color );
+    }
+  }
+}
+
 =item _make_img()
 
 Builds the image object for the graph and fills it with the background
@@ -1903,19 +1969,19 @@ sub _make_img {
 
   $channels ||= 3;
 
-  my $img = Imager->new(xsize=>$width, ysize=>$height, channels=>$channels);
+  my $img = Imager->new(xsize=>$width, ysize=>$height, channels=>$channels)
+    or return $self->_error("Error creating image: " . Imager->errstr);
 
   $img->box($self->_get_fill('back', [ 0, 0, $width-1, $height-1]));
 
-  $img;
+  $self->{_image} = $img;
+
+  return $img;
 }
 
 sub _get_image {
   my $self = shift;
 
-  if (!$self->{'_image'}) {
-    $self->{'_image'} = $self->_make_img();
-  }
   return $self->{'_image'};
 }
 
@@ -2340,7 +2406,7 @@ should be merged instead of just being replaced.
 =cut
 
 sub _composite {
-  qw(title legend text label dropshadow outline callout);
+  qw(title legend text label dropshadow outline callout graph);
 }
 
 sub _filter_region {
@@ -2362,6 +2428,101 @@ sub _filter_region {
   $masked->filter(%$filter);
 }
 
+=item _line(x1 => $x1, y1 => $y1, ..., style => $style)
+
+Wrapper for line drawing, implements styles Imager doesn't.
+
+Currently styles are limited to horizontal and vertical lines.
+
+=cut
+
+sub _line {
+  my ($self, %opts) = @_;
+
+  my $img = delete $opts{img}
+    or die "No img supplied to _line()";
+  my $style = delete $opts{style} || "solid";
+
+  if ($style eq "solid" || ($opts{x1} != $opts{x2} && $opts{y1} != $opts{y2})) {
+    return $img->line(%opts);
+  }
+  elsif ($style eq 'dashed' || $style eq 'dotted') {
+    my ($x1, $y1, $x2, $y2) = delete @opts{qw/x1 y1 x2 y2/};
+    # the line is vertical or horizontal, so swapping doesn't hurt
+    $x1 > $x2 and ($x1, $x2) = ($x2, $x1);
+    $y1 > $y2 and ($y1, $y2) = ($y2, $y1);
+    my ($stepx, $stepy) = ( 0, 0 );
+    my $step_size = $style eq "dashed" ? 8 : 2;
+    my ($counter, $count_end);
+    if ($x1 == $x2) {
+      $stepy = $step_size;
+      ($counter, $count_end) = ($y1, $y2);
+    }
+    else {
+      $stepx = $step_size;
+      ($counter, $count_end) = ($x1, $x2);
+    }
+    my ($x, $y) = ($x1, $y1);
+    while ($counter < $count_end) {
+      if ($style eq "dotted") {
+       $img->setpixel(x => $x, y => $y, color => $opts{color});
+      }
+      else {
+       my $xe = $stepx ? $x + $stepx / 2 - 1 : $x;
+       $xe > $x2 and $xe = $x2;
+       my $ye = $stepy ? $y + $stepy / 2 - 1 : $y;
+       $ye > $y2 and $ye = $y2;
+       $img->line(x1 => $x, y1 => $y, x2 => $xe, y2 => $ye, %opts);
+      }
+      $counter += $step_size;
+      $x += $stepx;
+      $y += $stepy;
+    }
+
+    return 1;
+  }
+  else {
+    $self->_error("Unknown line style $style");
+    return;
+  }
+}
+
+=item _box(xmin ..., style => $style)
+
+A wrapper for drawing styled box outlines.
+
+=cut
+
+sub _box {
+  my ($self, %opts) = @_;
+
+  my $style = delete $opts{style} || "solid";
+  my $img = delete $opts{img}
+    or die "No img supplied to _box";
+
+  if ($style eq "solid") {
+    return $img->box(%opts);
+  }
+  else {
+    my $box = delete $opts{box};
+    # replicate Imager's defaults
+    my %work_opts = ( xmin => 0, ymin => 0, xmax => $img->getwidth() - 1, ymax => $img->getheight() -1, %opts, style => $style, img => $img );
+    my ($xmin, $ymin, $xmax, $ymax) = delete @work_opts{qw/xmin ymin xmax ymax/};
+    if ($box) {
+      ($xmin, $ymin, $xmax, $ymax) = @$box;
+    }
+    $xmin > $xmax and ($xmin, $xmax) = ($xmax, $xmin);
+    $ymin > $ymax and ($ymin, $ymax) = ($ymax, $ymin);
+
+    if ($xmax - $xmin > 1) {
+      $self->_line(x1 => $xmin+1, y1 => $ymin, x2 => $xmax-1, y2 => $ymin, %work_opts);
+      $self->_line(x1 => $xmin+1, y1 => $ymax, x2 => $xmax-1, y2 => $ymax, %work_opts);
+    }
+    $self->_line(x1 => $xmin, y1 => $ymin, x2 => $xmin, y2 => $ymax, %work_opts);
+    return $self->_line(x1 => $xmin, y1 => $ymin, x2 => $xmin, y2 => $ymax, %work_opts);
+  }
+}
+
 1;
 __END__
 
index 712bbda..090fc76 100644 (file)
@@ -10,3 +10,7 @@
 \.rej$
 \.bak$
 ^Imager-Graph
+
+# t/x tests aren't distributed
+^t/x
+^xtestimg/
index aae51cf..99ef541 100644 (file)
@@ -101,6 +101,9 @@ sub draw {
 
   my $style = $self->{_style};
 
+  $self->_make_img
+    or return;
+
   my $img = $self->_get_image()
     or return;
 
@@ -640,7 +643,7 @@ Shows vertical gridlines at the y-tics.
 =cut
 
 sub show_vertical_gridlines {
-    $_[0]->{'custom_style'}->{'vertical_gridlines'} = 1;
+    $_[0]->{'custom_style'}{features}{'vertical_gridlines'} = 1;
 }
 
 =item use_automatic_axis()
@@ -830,7 +833,8 @@ sub _draw_x_tics {
   my %text_info = $self->_text_style('legend')
     or return;
 
-  my $show_gridlines = $self->_get_number('vertical_gridlines');
+  my $show_gridlines = $self->{_style}{features}{'vertical_gridlines'};
+  my @grid_line = $self->_get_line("vgrid");
   for my $count (0 .. $tic_count-1) {
     my $x1 = $graph_box->[0] + ($tic_distance * $count);
 
@@ -853,14 +857,11 @@ sub _draw_x_tics {
                  text => $value
                 );
 
-    if ($show_gridlines) {
-      # XXX - line styles!
-      for (my $i = $graph_box->[1]; $i < $graph_box->[3]; $i += 6) {
-        my $y1 = $i;
-        my $y2 = $i + 2;
-        if ($y2 > $graph_box->[3]) { $y2 = $graph_box->[3]; }
-        $img->line(x1 => $x1, x2 => $x1, y1 => $y1, y2 => $y2, aa => 1, color => '000000');
-      }
+    if ($show_gridlines && $x1 != $graph_box->[0] && $x1 != $graph_box->[2]) {
+      $self->_line(x1 => $x1, x2 => $x1,
+                  y1 => $y1, y2 => $y2,
+                  img => $img,
+                  @grid_line);
     }
   }
 }
@@ -899,5 +900,10 @@ sub _get_image_box      { return $_[0]->{'image_box'} }
 sub _get_graph_box      { return $_[0]->{'graph_box'} }
 sub _get_series_counter { return $_[0]->{'series_counter'} }
 
+sub _composite {
+  my ($self) = @_;
+  return ( $self->SUPER::_composite(), "graph", "vgrid" );
+}
+
 1;
 
index d43253b..a12669b 100644 (file)
@@ -166,6 +166,9 @@ sub draw {
 
   my $style = $self->{_style};
 
+  $self->_make_img
+    or return;
+
   my $img = $self->_get_image()
     or return;
 
@@ -181,7 +184,7 @@ sub draw {
 
   # Scale the graph box down to the widest graph that can cleanly hold the # of columns.
   return unless $self->_get_data_range();
-  $self->_remove_tics_from_chart_box(\@chart_box);
+  $self->_remove_tics_from_chart_box(\@chart_box, \%opts);
   my $column_count = $self->_get_column_count();
 
   my $width = $self->_get_number('width');
@@ -213,13 +216,17 @@ sub draw {
 
   my @fill_box = ( $left, $top, $left+$graph_width, $top+$graph_height );
   if ($self->{_style}{features}{graph_outline}) {
-    $img->box(
-             color   => $self->_get_color('graph.outline'),
-             xmin    => $left,
-             xmax    => $left+$graph_width,
-             ymin    => $top,
-             ymax    => $top+$graph_height,
-            );
+    my @line = $self->_get_line("graph.outline")
+      or return;
+
+    $self->_box(
+               @line,
+               xmin    => $left,
+               xmax    => $left+$graph_width,
+               ymin    => $top,
+               ymax    => $top+$graph_height,
+               img     => $img,
+              );
     ++$fill_box[0];
     ++$fill_box[1];
     --$fill_box[2];
@@ -258,6 +265,8 @@ sub draw {
     );
   }
 
+  $self->_reset_series_counter();
+
   if ($self->_get_data_series()->{'stacked_column'}) {
     return unless $self->_draw_stacked_columns();
   }
@@ -274,8 +283,8 @@ sub draw {
   if ($self->_get_y_tics()) {
     $self->_draw_y_tics();
   }
-  if ($self->_get_labels()) {
-    $self->_draw_x_tics();
+  if ($self->_get_labels(\%opts)) {
+    $self->_draw_x_tics(\%opts);
   }
 
   return $self->_get_image();
@@ -976,12 +985,34 @@ sub _add_data_series {
 
 =item show_horizontal_gridlines()
 
-Shows horizontal gridlines at the y-tics.
+Enables the C<horizontal_gridlines> feature, which shows horizontal
+gridlines at the y-tics.
+
+The style of the gridlines can be controlled with the
+set_horizontal_gridline_style() method (or by setting the hgrid
+style).
 
 =cut
 
 sub show_horizontal_gridlines {
-    $_[0]->{'custom_style'}->{'horizontal_gridlines'} = 1;
+    $_[0]->{'custom_style'}{features}{'horizontal_gridlines'} = 1;
+}
+
+=item set_horizontal_gridline_style(style => $style, color => $color)
+
+Set the style and color of horizonal gridlines.
+
+See: L<Imager::Graph/"Line styles">
+
+=cut
+
+sub set_horizontal_gridline_style {
+  my ($self, %opts) = @_;
+
+  $self->{custom_style}{hgrid} ||= {};
+  @{$self->{custom_style}{hgrid}}{keys %opts} = values %opts;
+
+  return 1;
 }
 
 =item use_automatic_axis()
@@ -1015,15 +1046,14 @@ sub _get_y_tics {
 }
 
 sub _remove_tics_from_chart_box {
-  my $self = shift;
-  my $chart_box = shift;
+  my ($self, $chart_box, $opts) = @_;
 
   # XXX - bad default
   my $tic_width = $self->_get_y_tic_width() || 10;
   my @y_tic_box = ($chart_box->[0], $chart_box->[1], $chart_box->[0] + $tic_width, $chart_box->[3]);
 
   # XXX - bad default
-  my $tic_height = $self->_get_x_tic_height() || 10;
+  my $tic_height = $self->_get_x_tic_height($opts) || 10;
   my @x_tic_box = ($chart_box->[0], $chart_box->[3] - $tic_height, $chart_box->[2], $chart_box->[3]);
 
   $self->_remove_box($chart_box, \@y_tic_box);
@@ -1034,7 +1064,7 @@ sub _remove_tics_from_chart_box {
   $self->_remove_box($chart_box, \@y_tic_tops);
 
   # Make sure that the first and last label fit
-  if (my $labels = $self->_get_labels()) {
+  if (my $labels = $self->_get_labels($opts)) {
     if (my @box = $self->_text_bbox($labels->[0], 'legend')) {
       my @remove_box = ($chart_box->[0],
                         $chart_box->[1],
@@ -1056,7 +1086,7 @@ sub _remove_tics_from_chart_box {
   }
 }
 
-sub _get_y_tic_width{
+sub _get_y_tic_width {
   my $self = shift;
   my $min = $self->_get_min_value();
   my $max = $self->_get_max_value();
@@ -1088,9 +1118,9 @@ sub _get_y_tic_width{
 }
 
 sub _get_x_tic_height {
-  my $self = shift;
+  my ($self, $opts) = @_;
 
-  my $labels = $self->_get_labels();
+  my $labels = $self->_get_labels($opts);
 
   if (!$labels) {
         return;
@@ -1135,7 +1165,8 @@ sub _draw_y_tics {
     or return;
 
   my $line_style = $self->_get_color('outline.line');
-  my $show_gridlines = $self->_get_number('horizontal_gridlines');
+  my $show_gridlines = $self->{_style}{features}{'horizontal_gridlines'};
+  my @grid_line = $self->_get_line("hgrid");
   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;
@@ -1161,27 +1192,24 @@ sub _draw_y_tics {
                  text => $value
                 );
 
-    if ($show_gridlines) {
-      # XXX - line styles!
-      for (my $i = $graph_box->[0]; $i < $graph_box->[2]; $i += 6) {
-        my $x1 = $i;
-        my $x2 = $i + 2;
-        if ($x2 > $graph_box->[2]) { $x2 = $graph_box->[2]; }
-        $img->line(x1 => $x1, x2 => $x2, y1 => $y1, y2 => $y1, aa => 1, color => $line_style);
-      }
+    if ($show_gridlines && $y1 != $graph_box->[1] && $y1 != $graph_box->[3]) {
+      $self->_line(x1 => $graph_box->[0], y1 => $y1,
+                  x2 => $graph_box->[2], y2 => $y1,
+                  img => $img,
+                  @grid_line);
     }
   }
 
 }
 
 sub _draw_x_tics {
-  my $self = shift;
+  my ($self, $opts) = @_;
 
   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 $labels = $self->_get_labels($opts);
 
   my $tic_count = (scalar @$labels) - 1;
 
@@ -1278,6 +1306,7 @@ 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 _reset_series_counter { $_[0]->{series_counter} = 0 }
 sub _get_series_counter { return $_[0]->{'series_counter'} }
 
 sub _style_defs {
@@ -1289,13 +1318,18 @@ sub _style_defs {
      opacity => 0.5,
     };
   push @{$work{features}}, qw/graph_outline graph_fill/;
+  $work{hgrid} =
+    {
+     color => "lookup(fg)",
+     style => "solid",
+    };
 
   return \%work;
 }
 
 sub _composite {
   my ($self) = @_;
-  return ( $self->SUPER::_composite(), "graph" );
+  return ( $self->SUPER::_composite(), "graph", "hgrid" );
 }
 
 1;
index dafef7b..7dfbf71 100644 (file)
@@ -29,46 +29,69 @@ my @data2 =
   );
 my @labels = qw(alpha beta gamma delta epsilon phi gi);
 
-plan tests => 4;
-
-my $area = Imager::Graph::Area->new;
-ok($area, "creating area chart object");
-
-# this may change output quality too
-
-print "# Imager version: $Imager::VERSION\n";
-print "# Font type: ",ref $font,"\n";
-
-$area->add_data_series(\@data1, "Test Area");
-$area->add_data_series(\@data2, "Test Area 2");
+plan tests => 7;
+
+{
+  my $area = Imager::Graph::Area->new;
+  ok($area, "creating area chart object");
+
+  # this may change output quality too
+  print "# Imager version: $Imager::VERSION\n";
+  print "# Font type: ",ref $font,"\n";
+
+  $area->add_data_series(\@data1, "Test Area");
+  $area->add_data_series(\@data2, "Test Area 2");
+
+  my $img1 = $area->draw
+    (
+     #data => \@data,
+     labels => \@labels,
+     font => $font, 
+     title => "Test",
+     features => { legend => 1 },
+     legend =>
+     { 
+      valign => "bottom",
+      halign => "center",
+      orientation => "horizontal",
+     },
+     area =>
+     {
+      opacity => 0.8,
+     },
+     #outline => { line => '404040' },
+    )
+      or print "# ", $area->error, "\n";
+
+  ok($img1, "made the image");
+
+  ok($img1->write(file => "testout/t40area1.ppm"),
+     "save to testout");
+
+  cmpimg($img1, "testimg/t40area1.png");
+}
 
-my $img1 = $area->draw
-  (
-   #data => \@data,
-   labels => \@labels,
-   font => $font, 
-   title => "Test",
-   features => { legend => 1 },
-   legend =>
-   { 
-    valign => "bottom",
-    halign => "center",
-    orientation => "horizontal",
-   },
-   area =>
-   {
-    opacity => 0.8,
-   },
-   #outline => { line => '404040' },
-  )
-  or print "# ", $area->error, "\n";
-
-ok($img1, "made the image");
-
-ok($img1->write(file => "testout/t40area1.ppm"),
-   "save to testout");
-
-cmpimg($img1, "testimg/t40area1.png");
+{
+  my $area = Imager::Graph::Area->new;
+  ok($area, "made area chart object");
+  $area->add_data_series(\@data1, "Test area");
+  $area->show_horizontal_gridlines();
+  $area->use_automatic_axis();
+  my $img2 = $area->draw
+    (
+     features => [ "horizontal_gridlines" ],
+     labels => \@labels,
+     font => $font,
+     hgrid => { style => "dashed", color => "#888" },
+     graph =>
+     {
+      outline => { color => "#F00", style => "dotted" },
+     },
+    );
+  ok($img2, "made second area chart image");
+  ok($img2->write(file => "testout/t40area2.ppm"),
+     "save to file");
+}
 
 END {
   unless ($ENV{IMAGER_GRAPH_KEEP_FILES}) {
diff --git a/t/x50vstyle.t b/t/x50vstyle.t
new file mode 100644 (file)
index 0000000..873fee6
--- /dev/null
@@ -0,0 +1,238 @@
+#!perl -w
+use strict;
+use Imager::Graph::Line;
+use lib 't/lib';
+use Imager::Font::Test;
+use Test::More;
+use Imager::Test qw(is_image_similar is_image);
+
+-d 'testout' 
+  or mkdir "testout", 0700 
+  or die "Could not create output directory: $!";
+
+++$|;
+
+use Imager qw(:handy);
+
+#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 @data1 =
+  (
+    100, 180, 80, 20, 2, 1, 0.5 ,
+  );
+my @labels = qw(alpha beta gamma delta epsilon phi gi);
+
+plan tests => 29;
+
+# this may change output quality too
+print "# Imager version: $Imager::VERSION\n";
+print "# Font type: ",ref $font,"\n";
+
+{
+  my $vert = Imager::Graph::Vertical->new;
+  ok($vert, "creating chart object");
+  $vert->set_y_tics(10);
+
+  $vert->add_line_data_series(\@data1, "Test Line");
+
+  my $img1;
+  { # default outline of chart area
+    $img1 = $vert->draw
+      (
+       labels => \@labels,
+       font => $font, 
+       title => "Test",
+      )
+       or print "# ", $vert->error, "\n";
+
+    ok($img1, "made the image");
+
+    ok($img1->write(file => "testout/x50line_def.ppm"),
+       "save to testout");
+
+    cmpimg($img1, "xtestimg/x50line_def.png");
+  }
+
+  { # no outline
+    my $img2 = $vert->draw
+      (
+       labels => \@labels,
+       font => $font, 
+       title => "Test",
+       features => [ qw/nograph_outline/ ],
+      )
+       or print "# ", $vert->error, "\n";
+
+    isnt($img1, $img2, "make sure they're different images");
+
+    ok($img2, "made the image");
+
+    ok($img2->write(file => "testout/x50line_noout.ppm"),
+       "save to testout");
+
+    cmpimg($img2, "xtestimg/x50line_noout.png");
+
+    my $img3 = $vert->draw
+      (
+       labels => \@labels,
+       font => $font, 
+       title => "Test",
+       features => "nograph_outline",
+      )
+       or print "# ", $vert->error, "\n";
+    ok($img3, "made with scalar features");
+    is_image($img3, $img2, "check that both feature mechanisms act the same");
+
+    my $img4 = $vert->draw
+      (
+       labels => \@labels,
+       font => $font, 
+       title => "Test",
+       features => { "graph_outline" => 0 },
+      )
+       or print "# ", $vert->error, "\n";
+    ok($img4, "made with hashref features");
+    is_image($img4, $img2, "check that all feature mechanisms act the same");
+  }
+
+  {
+    # check no state remembered from nograph_outline
+    my $img5 = $vert->draw
+      (
+       labels => \@labels,
+       font => $font, 
+       title => "Test",
+      )
+       or print "# ", $vert->error, "\n";
+    ok($img5, "make with border again to check no state held");
+    is_image($img1, $img5, "check no state held");
+  }
+
+  { # styled outline
+    my $img6 = $vert->draw
+      (
+       labels => \@labels,
+       font => $font,
+       title => "Test styled outline",
+       graph =>
+       {
+       outline =>
+       {
+        color => "#fff",
+        style => "dashed",
+       },
+       },
+      );
+    ok($img6, "make chart with dashed outline of graph area");
+    ok($img6->write(file => "testout/x50line_dashout.ppm"),
+       "save it");
+    cmpimg($img6, "xtestimg/x50line_dashout.png");
+  }
+
+  { # no outline, styled fill
+    my $img7 = $vert->draw
+      (
+       labels => \@labels,
+       font => $font,
+       title => "Test styled outline",
+       features => "nograph_outline",
+       graph =>
+       {
+       fill => { solid => "ffffffC0" },
+       },
+      )
+       or print "# ", $vert->error, "\n";
+    ok($img7, "made the image");
+    ok($img7->write(file => "testout/x50line_fill.ppm"),
+       "save it");
+    cmpimg($img7, "xtestimg/x50line_fill.png");
+  }
+
+  { # gridlines
+    my $img8 = $vert->draw
+      (
+       labels => \@labels,
+       font => $font,
+       title => "gridlines",
+       features => "horizontal_gridlines",
+       hgrid => { style => "dashed", color => "#A0A0A0" },
+      )
+       or print "# ", $vert->error, "\n";
+    ok($img8, "made the gridline image");
+    ok($img8->write(file => "testout/x50line_grid.ppm"),
+       "save it");
+    cmpimg($img8, "xtestimg/x50line_grid.png");
+
+    # default horizontal gridlines
+    my $imgb = $vert->draw
+      (
+       labels => \@labels,
+       font => $font,
+       title => "gridlines",
+       features => "horizontal_gridlines",
+      )
+       or print "# ", $vert->error, "\n";
+    ok($imgb, "made the gridline image");
+    ok($imgb->write(file => "testout/x50line_griddef.ppm"),
+       "save it");
+    cmpimg($imgb, "xtestimg/x50line_griddef.png");
+
+  }
+
+  { # gridlines (set by method)
+    my $vert2 = Imager::Graph::Vertical->new;
+    $vert2->show_horizontal_gridlines();
+    $vert2->set_horizontal_gridline_style(style => "dashed", color => "#A0A0A0");
+    $vert2->set_labels(\@labels);
+    $vert2->set_title("gridlines");
+    $vert2->add_line_data_series(\@data1, "Test Line");
+    $vert2->set_y_tics(10);
+    $vert2->set_font($font);
+
+    my $img9 = $vert2->draw
+      (
+       #labels => \@labels,
+       #font => $font,
+       #title => "gridlines",
+       #features => "horizontal_gridlines",
+       #hgrid => { style => "dashed", color => "#A0A0A0" },
+      )
+       or print "# ", $vert2->error, "\n";
+    ok($img9, "made the gridline image (set by methods)");
+    ok($img9->write(file => "testout/x50line_gridm.ppm"),
+       "save it");
+    cmpimg($img9, "xtestimg/x50line_grid.png");
+  }
+}
+
+END {
+  unless ($ENV{IMAGER_GRAPH_KEEP_FILES}) {
+    unlink "testout/x50line_def.ppm";
+    unlink "testout/x50line_noout.ppm";
+    unlink "testout/x50line_dashout.ppm";
+    unlink "testout/x50line_fill.ppm";
+    unlink "testout/x50line_grid.ppm";
+    unlink "testout/x50line_gridm.ppm";
+  }
+}
+
+sub cmpimg {
+  my ($img, $file, $limit) = @_;
+
+  $limit ||= 10000;
+
+ SKIP:
+  {
+    $Imager::formats{png}
+      or skip("No PNG support", 1);
+
+    my $cmpimg = Imager->new;
+    $cmpimg->read(file=>$file)
+      or return ok(0, "Cannot read $file: ".$cmpimg->errstr);
+    my $diff = Imager::i_img_diff($img->{IMG}, $cmpimg->{IMG});
+    is_image_similar($img, $cmpimg, $limit, "Comparison to $file ($diff)");
+  }
+}
index 0d299e4..7d91691 100644 (file)
Binary files a/testimg/t40area1.png and b/testimg/t40area1.png differ
diff --git a/xtestimg/x50line_dashout.png b/xtestimg/x50line_dashout.png
new file mode 100644 (file)
index 0000000..0d839db
Binary files /dev/null and b/xtestimg/x50line_dashout.png differ
diff --git a/xtestimg/x50line_def.png b/xtestimg/x50line_def.png
new file mode 100644 (file)
index 0000000..d1c06cb
Binary files /dev/null and b/xtestimg/x50line_def.png differ
diff --git a/xtestimg/x50line_fill.png b/xtestimg/x50line_fill.png
new file mode 100644 (file)
index 0000000..174989c
Binary files /dev/null and b/xtestimg/x50line_fill.png differ
diff --git a/xtestimg/x50line_grid.png b/xtestimg/x50line_grid.png
new file mode 100644 (file)
index 0000000..7fb99fb
Binary files /dev/null and b/xtestimg/x50line_grid.png differ
diff --git a/xtestimg/x50line_griddef.png b/xtestimg/x50line_griddef.png
new file mode 100644 (file)
index 0000000..5480b2d
Binary files /dev/null and b/xtestimg/x50line_griddef.png differ
diff --git a/xtestimg/x50line_noout.png b/xtestimg/x50line_noout.png
new file mode 100644 (file)
index 0000000..1360620
Binary files /dev/null and b/xtestimg/x50line_noout.png differ