]> git.imager.perl.org - imager-graph.git/commitdiff
- add horizontal legend boxes
authorTony Cook <tony@develop-help.com>
Mon, 21 Apr 2008 06:21:36 +0000 (06:21 +0000)
committerTony Cook <tony@develop-help.com>
Mon, 21 Apr 2008 06:21:36 +0000 (06:21 +0000)
make the legend outside padding an integer

update test images to match

Changes
Graph.pm
t/t10pie.t
testimg/t10_hlegend.png [new file with mode: 0644]
testimg/t10_pie2.png
testimg/t10_rad_fount.png

diff --git a/Changes b/Changes
index d87e7f0da93264b0f80db67b51541eee251262bb..a2240d47371461c29246da5558e37c425ce90764 100644 (file)
--- a/Changes
+++ b/Changes
@@ -8,6 +8,8 @@ Imager-Graph 0.06 - unreleased
  - round the dimensions used within the legend to integers to prevent
    later truncation from causing uneven output.
 
  - round the dimensions used within the legend to integers to prevent
    later truncation from causing uneven output.
 
+ - add horizontal legend boxes
+
 Imager-Graph 0.05 - 17 December 2007
 =================
 
 Imager-Graph 0.05 - 17 December 2007
 =================
 
index bea06d9201a08845d0375c621a9e0f07d21dfca5..127e30490e3919a86f5c77789ba8cdd795c67c97 100644 (file)
--- a/Graph.pm
+++ b/Graph.pm
@@ -380,8 +380,28 @@ the background fill for the legend.  Default: none
 the border color of the legend.  Default: none (no border is drawn
 around the legend.)
 
 the border color of the legend.  Default: none (no border is drawn
 around the legend.)
 
+=item orientation
+
+The orientation of the legend.  If this is C<vertical> the the patches
+and labels are stacked on top of each other.  If this is C<horizontal>
+the patchs and labels are word wrapped across the image.  Default:
+vertical.
+
 =back
 
 =back
 
+For example to create a horizontal legend with borderless patches,
+darker than the background, you might do:
+
+  my $im = $chart->draw
+    (...,
+    legend =>
+    {
+      patchborder => undef,
+      orientation => 'horizontal',
+      fill => { solid => Imager::Color->new(0, 0, 0, 32), }
+    },
+    ...);
+
 =item callout
 
 defines attributes for graph callouts, if any are present.  eg. if the
 =item callout
 
 defines attributes for graph callouts, if any are present.  eg. if the
@@ -528,8 +548,7 @@ Fills can be used for the graph background color, the background color
 for the legend block and for the fills used for each data element.
 
 You can specify a fill as a L<color value|Specifying colors> or as a
 for the legend block and for the fills used for each data element.
 
 You can specify a fill as a L<color value|Specifying colors> or as a
-general fill, see L<Imager::Fill> for details.  To use a general fill
-you need a version of Imager after 0.38.
+general fill, see L<Imager::Fill> for details.
 
 You don't need (or usually want) to call Imager::Fill::new yourself,
 since the various fill functions will call it for you, and
 
 You don't need (or usually want) to call Imager::Fill::new yourself,
 since the various fill functions will call it for you, and
@@ -553,6 +572,9 @@ define the fountain fills xa, ya, xb and yb parameters.
 As with colors, you can use lookup(name) or lookup(name1.name2) to
 have one element to inherit the fill of another.
 
 As with colors, you can use lookup(name) or lookup(name1.name2) to
 have one element to inherit the fill of another.
 
+Imager::Graph defaults the fill combine value to C<'normal'>.  This
+doesn't apply to simple color fills.
+
 =head2 Specifying numbers
 
 You can specify various numbers, usually representing the size of
 =head2 Specifying numbers
 
 You can specify various numbers, usually representing the size of
@@ -722,7 +744,6 @@ my %styles =
     [
      { fountain=>'linear',
        xa_ratio=>0.13, ya_ratio=>0.13, xb_ratio=>0.87, yb_ratio=>0.87,
     [
      { fountain=>'linear',
        xa_ratio=>0.13, ya_ratio=>0.13, xb_ratio=>0.87, yb_ratio=>0.87,
-       repeat=>'sawtooth',
        segments => Imager::Fountain->simple(positions=>[0, 1],
                                            colors=>[ NC('FFC0C0'), NC('FF0000') ]),
      },
        segments => Imager::Fountain->simple(positions=>[0, 1],
                                            colors=>[ NC('FFC0C0'), NC('FF0000') ]),
      },
@@ -926,7 +947,7 @@ Recursively looks up I<newname> in the style.
 
 =item scale(value1,value2)
 
 
 =item scale(value1,value2)
 
-Each value can be a number or a name.  Names are recursively looks up
+Each value can be a number or a name.  Names are recursively looked up
 in the style and the product is returned.
 
 =back
 in the style and the product is returned.
 
 =back
@@ -1056,8 +1077,9 @@ sub _translate_fill {
     }
     else {
       # a general fill
     }
     else {
       # a general fill
+      # default to normal combine mode
+      my %work = ( combine => 'normal', %$what );
       if ($what->{hatch}) {
       if ($what->{hatch}) {
-       my %work = %$what;
        if (!$work{fg}) {
          $work{fg} = $self->_get_color('fg')
            or return;
        if (!$work{fg}) {
          $work{fg} = $self->_get_color('fg')
            or return;
@@ -1069,7 +1091,6 @@ sub _translate_fill {
        return ( fill=>\%work );
       }
       elsif ($what->{fountain}) {
        return ( fill=>\%work );
       }
       elsif ($what->{fountain}) {
-       my %work = %$what;
        for my $key (qw(xa ya xb yb)) {
          if (exists $work{"${key}_ratio"}) {
            if ($key =~ /^x/) {
        for my $key (qw(xa ya xb yb)) {
          if (exists $work{"${key}_ratio"}) {
            if ($key =~ /^x/) {
@@ -1085,7 +1106,7 @@ sub _translate_fill {
        return ( fill=>\%work );
       }
       else {
        return ( fill=>\%work );
       }
       else {
-       return ( fill=> $what );
+       return ( fill=> \%work );
       }
     }
   }
       }
     }
   }
@@ -1287,6 +1308,123 @@ sub _remove_box {
 sub _draw_legend {
   my ($self, $img, $labels, $chart_box) = @_;
 
 sub _draw_legend {
   my ($self, $img, $labels, $chart_box) = @_;
 
+  my $orient = $self->_get_thing('legend.orientation');
+  defined $orient or $orient = 'vertical';
+
+  if ($orient eq 'vertical') {
+    return $self->_draw_legend_vertical($img, $labels, $chart_box);
+  }
+  elsif ($orient eq 'horizontal') {
+    return $self->_draw_legend_horizontal($img, $labels, $chart_box);
+  }
+  else {
+    return $self->_error("Unknown legend.orientation $orient");
+  }
+}
+
+sub _draw_legend_horizontal {
+  my ($self, $img, $labels, $chart_box) = @_;
+
+  defined(my $padding = $self->_get_integer('legend.padding'))
+    or return;
+  my $patchsize = $self->_get_integer('legend.patchsize')
+    or return;
+  defined(my $gap = $self->_get_integer('legend.patchgap'))
+    or return;
+
+  my $minrowsize = $patchsize + $gap;
+  my ($width, $height) = (0,0);
+  my $row_height = $minrowsize;
+  my $pos = 0;
+  my @sizes;
+  my @offsets;
+  for my $label (@$labels) {
+    my @text_box = $self->_text_bbox($label, 'legend');
+    push(@sizes, \@text_box);
+    my $entry_width = $patchsize + $gap + $text_box[2];
+    if ($pos == 0) {
+      # never re-wrap the first entry
+      push @offsets, [ 0, $height ];
+    }
+    else {
+      if ($pos + $gap + $entry_width > $chart_box->[2]) {
+       $pos = 0;
+       $height += $row_height;
+      }
+      push @offsets, [ $pos, $height ];
+    }
+    my $entry_right = $pos + $entry_width;
+    $pos += $gap + $entry_width;
+    $entry_right > $width and $width = $entry_right;
+    if ($text_box[3] > $row_height) {
+      $row_height = $text_box[3];
+    }
+  }
+  $height += $row_height;
+  my @box = ( 0, 0, $width + $padding * 2, $height + $padding * 2 );
+  my $outsidepadding = 0;
+  if ($self->{_style}{legend}{border}) {
+    defined($outsidepadding = $self->_get_integer('legend.outsidepadding'))
+      or return;
+    $box[2] += 2 * $outsidepadding;
+    $box[3] += 2 * $outsidepadding;
+  }
+  $self->_align_box(\@box, $chart_box, 'legend')
+    or return;
+  if ($self->{_style}{legend}{fill}) {
+    $img->box(xmin=>$box[0]+$outsidepadding, 
+              ymin=>$box[1]+$outsidepadding, 
+              xmax=>$box[2]-$outsidepadding, 
+              ymax=>$box[3]-$outsidepadding,
+            $self->_get_fill('legend.fill', \@box));
+  }
+  $box[0] += $outsidepadding;
+  $box[1] += $outsidepadding;
+  $box[2] -= $outsidepadding;
+  $box[3] -= $outsidepadding;
+  my %text_info = $self->_text_style('legend')
+    or return;
+  my $patchborder;
+  if ($self->{_style}{legend}{patchborder}) {
+    $patchborder = $self->_get_color('legend.patchborder')
+      or return;
+  }
+  
+  my $dataindex = 0;
+  for my $label (@$labels) {
+    my ($left, $top) = @{$offsets[$dataindex]};
+    $left += $box[0] + $padding;
+    $top += $box[1] + $padding;
+    my $textpos = $left + $patchsize + $gap;
+    my @patchbox = ( $left, $top,
+                     $left + $patchsize, $top + $patchsize );
+    my @fill = $self->_data_fill($dataindex, \@patchbox)
+      or return;
+    $img->box(xmin=>$left, ymin=>$top, xmax=>$left + $patchsize,
+              ymax=>$top + $patchsize, @fill);
+    if ($self->{_style}{legend}{patchborder}) {
+      $img->box(xmin=>$left, ymin=>$top, xmax=>$left + $patchsize,
+               ymax=>$top + $patchsize,
+               color=>$patchborder);
+    }
+    $img->string(%text_info, x=>$textpos, 'y'=>$top + $patchsize, 
+                 text=>$label);
+
+    ++$dataindex;
+  }
+  if ($self->{_style}{legend}{border}) {
+    my $border_color = $self->_get_color('legend.border')
+      or return;
+    $img->box(xmin=>$box[0], ymin=>$box[1], xmax=>$box[2], ymax=>$box[3],
+             color=>$border_color);
+  }
+  $self->_remove_box($chart_box, \@box);
+  1;
+}
+
+sub _draw_legend_vertical {
+  my ($self, $img, $labels, $chart_box) = @_;
+
   defined(my $padding = $self->_get_integer('legend.padding'))
     or return;
   my $patchsize = $self->_get_integer('legend.patchsize')
   defined(my $padding = $self->_get_integer('legend.padding'))
     or return;
   my $patchsize = $self->_get_integer('legend.patchsize')
@@ -1312,7 +1450,7 @@ sub _draw_legend {
             $height + $padding * 2 - $gap);
   my $outsidepadding = 0;
   if ($self->{_style}{legend}{border}) {
             $height + $padding * 2 - $gap);
   my $outsidepadding = 0;
   if ($self->{_style}{legend}{border}) {
-    defined($outsidepadding = $self->_get_number('legend.outsidepadding'))
+    defined($outsidepadding = $self->_get_integer('legend.outsidepadding'))
       or return;
     $box[2] += 2 * $outsidepadding;
     $box[3] += 2 * $outsidepadding;
       or return;
     $box[2] += 2 * $outsidepadding;
     $box[3] += 2 * $outsidepadding;
index 95204232a6d0925067c1650264d6ac9130d0a3a1..9e4ac2f19e3fd281496b3ffb015de0d5bb315fb3 100644 (file)
@@ -23,7 +23,7 @@ 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 @data = ( 100, 180, 80, 20, 2, 1, 0.5 );
 my @labels = qw(alpha beta gamma delta epsilon phi gi);
 
-plan tests => 11;
+plan tests => 13;
 
 my $pie = Imager::Graph::Pie->new;
 ok($pie, "creating pie chart object");
 
 my $pie = Imager::Graph::Pie->new;
 ok($pie, "creating pie chart object");
@@ -97,6 +97,24 @@ my ($im_version) = $Imager::VERSION =~ /(\d\.[\d_]+)/;
   $img5->write(file=>'testout/t10_mono.ppm')
     or die "Cannot save pie3: ",$img5->errstr,"\n";
   cmpimg($img5, "testimg/t10_mono.png", 550_000);
   $img5->write(file=>'testout/t10_mono.ppm')
     or die "Cannot save pie3: ",$img5->errstr,"\n";
   cmpimg($img5, "testimg/t10_mono.png", 550_000);
+
+  my $img6 = $pie->draw(data=>\@data, labels=>\@labels,
+                        font=>$font, style=>'fount_lin', 
+                        features=>[ 'allcallouts', 'labelspc', 'legend' ],
+                        legend=>
+                       {
+                        valign=>'top', 
+                        halign=>'center',
+                        orientation => 'horizontal',
+                        fill => { solid => Imager::Color->new(0, 0, 0, 32) },
+                        patchborder => undef,
+                        #size => 30,
+                       });
+  ok($img6, "sixth chart")
+    or print "# ",$pie->error,"\n";
+  $img6->write(file=>'testout/t10_hlegend.ppm')
+    or die "Cannot save pie6: ",$img5->errstr,"\n";
+  cmpimg($img6, "testimg/t10_hlegend.png", 550_000);
 }
 
 sub cmpimg {
 }
 
 sub cmpimg {
diff --git a/testimg/t10_hlegend.png b/testimg/t10_hlegend.png
new file mode 100644 (file)
index 0000000..f236336
Binary files /dev/null and b/testimg/t10_hlegend.png differ
index 0cb8d278f9e639b8b88da04aec2cbca19e76309b..4186cd0f50f36ad8c2a9cc2475482db9e1c7b45e 100644 (file)
Binary files a/testimg/t10_pie2.png and b/testimg/t10_pie2.png differ
index 5d4bfa6787b6603830782f210c7c926947228f51..bbfbc4fe55996aa135b695f2f31d4bf00f1b4f1a 100644 (file)
Binary files a/testimg/t10_rad_fount.png and b/testimg/t10_rad_fount.png differ