File Coverage

blib/lib/Chart/Pie.pm
Criterion Covered Total %
statement 285 394 72.3
branch 84 168 50.0
condition 9 15 60.0
subroutine 11 12 91.7
pod n/a
total 389 589 66.0


line stmt bran cond sub pod time code
1             #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>#
2             # Chart::Pie #
3             # #
4             # written by Chart Group #
5             # #
6             # maintained by the Chart Group #
7             # Chart@wettzell.ifag.de #
8             # #
9             # #
10             #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<#
11              
12             package Chart::Pie;
13              
14 5     5   189 use Chart::Base 2.3;
  5         276  
  5         99  
15 5     5   128 use GD;
  5         48  
  5         96  
16 5     5   235 use Carp;
  5         75  
  5         104  
17 5     5   85 use strict;
  5         59  
  5         4428  
18              
19             @Chart::Pie::ISA = qw(Chart::Base);
20             $Chart::Pie::VERSION = '2.3';
21              
22             #>>>>>>>>>>>>>>>>>>>>>>>>>>#
23             # public methods go here #
24             #<<<<<<<<<<<<<<<<<<<<<<<<<<#
25              
26              
27              
28             #>>>>>>>>>>>>>>>>>>>>>>>>>>>#
29             # private methods go here #
30             #<<<<<<<<<<<<<<<<<<<<<<<<<<<#
31              
32             #Overwrite the legend methods to get the right legend
33             sub _draw_right_legend {
34 1     1   10   my $self = shift;
35 1         11   my $data = $self->{'dataref'};
36 1         10   my @labels = @{$data->[0]};
  1         15  
37 1         10   my ($x1, $x2, $x3, $y1, $y2, $width, $color, $misccolor, $w, $h, $brush);
38 1         9   my $font = $self->{'legend_font'};
39 1         10   my $l1 = 0;
40 1         9   my $l2 =0;
41 1         8   my ($i, $j, $label, $dataset_sum);
42 1         10   my $max_label_len = 1;
43               
44             # make sure we're using a real font
45 1 50       12   unless ((ref ($font)) eq 'GD::Font') {
46 0         0     croak "The subtitle font you specified isn\'t a GD Font object";
47               }
48              
49             # get the size of the font
50 1         21   ($h, $w) = ($font->height, $font->width);
51              
52             # get the miscellaneous color
53 1         13   $misccolor = $self->_color_role_to_index('misc');
54              
55             #find out what the sum of all datapoits is, needed for the Labels with percent
56 1         14   for my $j (0..$self->{'num_datapoints'}) {
57 11 100       108      if(defined $data->[1][$j])
58                     {
59 10         129           $dataset_sum += $data->[1][$j];
60                     }
61               }
62               
63             # find out how who wide the largest label text is
64 1         11   foreach (@labels) {
65 10 100       101    if ( length($_) > $l1) {
66 2         77      $l1 = length($_);
67                }
68               }
69               for (my $i =0 ; $i < ($self->{'num_datapoints'}) ; $i++) {
70 10 100       119    if ( length($data->[1][$i]) > $l2 ) {
71 1         12       $l2 = length($data->[1][$i]);
72              
73                }
74 1         10   }
75               
76 1 50       20   if ($self->{'legend_label_values'} =~ /^value$/i ) {
    50          
    0          
77 0         0     $max_label_len = $l1 + $l2 +1;
78               }
79               elsif ($self->{'legend_label_values'} =~ /^percent$/i ) {
80 1         9     $max_label_len = $l1 +7;
81               }
82               elsif ($self->{'legend_label_values'} =~ /^both$/i ) {
83 0         0     $max_label_len = $l1 + $l2 +9;
84               }
85               else {
86 0         0     $max_label_len = $l1;
87               }
88              
89             # find out how wide the largest label is
90 1         13   $width = (2 * $self->{'text_space'})
91             #+ ($self->{'max_legend_label'} * $w)
92                 + $max_label_len *$w
93                 + $self->{'legend_example_size'}
94                 + (2 * $self->{'legend_space'});
95              
96             # get some starting x-y values
97 1         11   $x1 = $self->{'curr_x_max'} - $width;
98 1         9   $x2 = $self->{'curr_x_max'};
99 1         11   $y1 = $self->{'curr_y_min'} + $self->{'graph_border'} ;
100 1         12   $y2 = $self->{'curr_y_min'} + $self->{'graph_border'} + $self->{'text_space'}
101                       + ($self->{'num_datapoints'} * ($h + $self->{'text_space'}))
102             + (2 * $self->{'legend_space'});
103              
104             # box the legend off
105 1         89   $self->{'gd_obj'}->rectangle ($x1, $y1, $x2, $y2, $misccolor);
106              
107             # leave that nice space inside the legend box
108 1         9   $x1 += $self->{'legend_space'};
109 1         10   $y1 += $self->{'legend_space'} + $self->{'text_space'};
110              
111             # now draw the actual legend
112 1         14   for (0..$#labels) {
113             # get the color
114 10         167     $color = $self->_color_role_to_index('dataset'.$_);
115              
116             # find the x-y coords
117 10         86     $x2 = $x1;
118 10         119     $x3 = $x2 + $self->{'legend_example_size'};
119 10         99     $y2 = $y1 + ($_ * ($self->{'text_space'} + $h)) + $h/2;
120              
121             # do the line first
122 10         152     $self->{'gd_obj'}->line ($x2, $y2, $x3, $y2, $color);
123              
124             # reset the brush for points
125 10         151     $brush = $self->_prepare_brush($color, 'point',
126             $self->{'pointStyle' . $_});
127 10         249     $self->{'gd_obj'}->setBrush($brush);
128             # draw the point
129 10         135     $self->{'gd_obj'}->line(int(($x3+$x2)/2), $y2,
130             int(($x3+$x2)/2), $y2, gdBrushed);
131              
132             # now the label
133 10         99     $x2 = $x3 + (2 * $self->{'text_space'});
134 10         88     $y2 -= $h/2;
135 10 50       108     if (defined $data->[1][$_]) {
136 10 50       171        if ( $self->{'legend_label_values'} =~ /^value$/i ) {
    50          
    0          
137 0         0         $self->{'gd_obj'}->string($font, $x2, $y2, $labels[$_].' '.$data->[1][$_], $color);
138                    }
139                    elsif ( $self->{'legend_label_values'} =~ /^percent$/i ) {
140 10         223         $label = sprintf("%s %4.2f%%",$labels[$_], $data->[1][$_] / $dataset_sum * 100);
141 10         363         $self->{'gd_obj'}->string($font, $x2, $y2, $label, $color);
142                    }
143                    elsif ( $self->{'legend_label_values'} =~ /^both$/i ) {
144 0 0       0           if ( $data->[1][$_] =~ /\./ ) {
145 0         0             $label = sprintf("%s %4.2f%% %.2f",$labels[$_], $data->[1][$_] / $dataset_sum * 100, $data->[1][$_]);
146                       }
147                       else {
148 0         0             $label = sprintf("%s %4.2f%% %d",$labels[$_], $data->[1][$_] / $dataset_sum * 100, $data->[1][$_]);
149                       }
150 0         0         $self->{'gd_obj'}->string($font, $x2, $y2, $label, $color);
151                    }
152                    else {
153 0         0         $self->{'gd_obj'}->string($font, $x2, $y2, $labels[$_], $color);
154                   }
155              
156                 }
157               }
158              
159             # mark off the used space
160 1         13   $self->{'curr_x_max'} -= $width;
161              
162             # and return
163 1         19   return 1;
164             }
165              
166              
167             ## put the legend on the left of the chart
168             sub _draw_left_legend {
169 0     0   0   my $self = shift;
170 0         0   my $data = $self->{'dataref'};
171 0         0   my @labels = @{$data->[0]};
  0         0  
172 0         0   my ($x1, $x2, $x3, $y1, $y2, $width, $color, $misccolor, $w, $h, $brush);
173 0         0   my $font = $self->{'legend_font'};
174 0         0   my $max_label_len= 1;;
175 0         0   my $l1 = 0;
176 0         0   my $l2 = 0;
177 0         0   my ($dataset_sum, $label);
178             # make sure we're using a real font
179 0 0       0   unless ((ref ($font)) eq 'GD::Font') {
180 0         0     croak "The subtitle font you specified isn\'t a GD Font object";
181               }
182              
183             # get the size of the font
184 0         0   ($h, $w) = ($font->height, $font->width);
185              
186             # get the miscellaneous color
187 0         0   $misccolor = $self->_color_role_to_index('misc');
188              
189             #find out what the sum of all datapoits is, needed for the Labels with percent
190 0         0   for my $j (0..$self->{'num_datapoints'}) {
191 0 0       0      if(defined $data->[1][$j]) {
192 0         0        $dataset_sum += $data->[1][$j];
193                  }
194               }
195              
196             # find out how who wide the largest label text is
197 0         0   foreach (@labels) {
198 0 0       0    if ( length($_) > $l1) {
199 0         0      $l1 = length($_);
200                }
201               }
202               for (my $i =0 ; $i < ($self->{'num_datapoints'}) ; $i++) {
203 0 0       0    if ( length($data->[1][$i]) > $l2 ) {
204 0         0       $l2 = length($data->[1][$i]);
205                }
206 0         0   }
207              
208 0 0       0   if ($self->{'legend_label_values'} =~ /^value$/i ) {
    0          
    0          
209 0         0     $max_label_len = $l1 + $l2 +1;
210               }
211               elsif ($self->{'legend_label_values'} =~ /^percent$/i ) {
212 0         0     $max_label_len = $l1 +7;
213               }
214               elsif ($self->{'legend_label_values'} =~ /^both$/i ) {
215 0         0     $max_label_len = $l1 + $l2 +9;
216               }
217               else {
218 0         0     $max_label_len = $l1;
219               }
220              
221             # find out how wide the largest label is
222 0         0   $width = (2 * $self->{'text_space'})
223                 + ($max_label_len * $w)
224                 + $self->{'legend_example_size'}
225                 + (2 * $self->{'legend_space'});
226              
227             # get some base x-y coordinates
228 0         0   $x1 = $self->{'curr_x_min'};
229 0         0   $x2 = $self->{'curr_x_min'} + $width;
230 0         0   $y1 = $self->{'curr_y_min'} + $self->{'graph_border'} ;
231 0         0   $y2 = $self->{'curr_y_min'} + $self->{'graph_border'} + $self->{'text_space'}
232                       + ($self->{'num_datapoints'} * ($h + $self->{'text_space'}))
233             + (2 * $self->{'legend_space'});
234              
235             # box the legend off
236 0         0   $self->{'gd_obj'}->rectangle ($x1, $y1, $x2, $y2, $misccolor);
237              
238             # leave that nice space inside the legend box
239 0         0   $x1 += $self->{'legend_space'};
240 0         0   $y1 += $self->{'legend_space'} + $self->{'text_space'};
241              
242             # now draw the actual legend
243 0         0   for (0..$#labels) {
244             # get the color
245 0         0     $color = $self->_color_role_to_index('dataset'.$_);
246              
247             # find the x-y coords
248 0         0     $x2 = $x1;
249 0         0     $x3 = $x2 + $self->{'legend_example_size'};
250 0         0     $y2 = $y1 + ($_ * ($self->{'text_space'} + $h)) + $h/2;
251              
252             # do the line first
253 0         0     $self->{'gd_obj'}->line ($x2, $y2, $x3, $y2, $color);
254              
255             # reset the brush for points
256 0         0     $brush = $self->_prepare_brush($color, 'point',
257             $self->{'pointStyle' . $_});
258 0         0     $self->{'gd_obj'}->setBrush($brush);
259             # draw the point
260 0         0     $self->{'gd_obj'}->line(int(($x3+$x2)/2), $y2,
261             int(($x3+$x2)/2), $y2, gdBrushed);
262              
263             # now the label
264 0         0     $x2 = $x3 + (2 * $self->{'text_space'});
265 0         0     $y2 -= $h/2;
266 0 0       0     if ( $self->{'legend_label_values'} =~ /^value$/i ) {
    0          
    0          
267 0         0         $self->{'gd_obj'}->string($font, $x2, $y2, $labels[$_].' '.$data->[1][$_], $color);
268                 }
269                 elsif ( $self->{'legend_label_values'} =~ /^percent$/i ) {
270 0         0         $label = sprintf("%s %4.2f%%",$labels[$_], $data->[1][$_] / $dataset_sum * 100);
271 0         0         $self->{'gd_obj'}->string($font, $x2, $y2, $label, $color);
272                 }
273                 elsif ( $self->{'legend_label_values'} =~ /^both$/i ) {
274 0 0       0         if ($data->[1][$_] =~ /\./) {
275 0         0            $label = sprintf("%s %4.2f%% %.2f",$labels[$_], $data->[1][$_] / $dataset_sum * 100, $data->[1][$_]);
276                     }
277                     else {
278 0         0            $label = sprintf("%s %4.2f%% %d",$labels[$_], $data->[1][$_] / $dataset_sum * 100, $data->[1][$_]);
279                     }
280 0         0         $self->{'gd_obj'}->string($font, $x2, $y2, $label, $color);
281                 }
282                 else {
283 0         0         $self->{'gd_obj'}->string($font, $x2, $y2, $labels[$_], $color);
284                 }
285              
286               }
287              
288             # mark off the used space
289 0         0   $self->{'curr_x_min'} += $width;
290              
291             # and return
292 0         0   return 1;
293             }
294              
295              
296             ## put the legend on the bottom of the chart
297             sub _draw_bottom_legend {
298 2     2   20   my $self = shift;
299 2         21   my $data = $self->{'dataref'};
300 2         17   my @labels =@{$data->[0]};
  2         29  
301 2         18   my ($x1, $y1, $x2, $x3, $y2, $empty_width, $max_label_width, $cols, $rows, $color, $brush);
302 2         20   my ($col_width, $row_height, $r, $c, $index, $x, $y, $w, $h);
303 2         18   my $font = $self->{'legend_font'};
304 2         17   my $max_label_len;
305 2         19   my $l1 = 0;
306 2         17   my $l2 = 0;
307 2         17   my ($dataset_sum, $j);
308 2         17   my $label;
309             # make sure we're using a real font
310 2 50       25   unless ((ref ($font)) eq 'GD::Font') {
311 0         0     croak "The subtitle font you specified isn\'t a GD Font object";
312               }
313              
314             # get the size of the font
315 2         32   ($h, $w) = ($font->height, $font->width);
316              
317             # find the base x values
318 2         21   $x1 = $self->{'curr_x_min'} + $self->{'graph_border'} ;
319             # + ($self->{'y_tick_label_length'} * $self->{'tick_label_font'}->width)
320             # + $self->{'tick_len'} + (3 * $self->{'text_space'});
321 2         42   $x2 = $self->{'curr_x_max'} - $self->{'graph_border'};
322 2 50       44   if ($self->{'y_label'}) {
323 0         0     $x1 += $self->{'label_font'}->height + 2 * $self->{'text_space'};
324               }
325 2 50       24   if ($self->{'y_label2'}) {
326 0         0     $x2 -= $self->{'label_font'}->height + 2 * $self->{'text_space'};
327               }
328              
329             #find out what the sum of all datapoits is, needed for the Labels with percent
330 2         77     for $j (0..$self->{'num_datapoints'}) {
331 16 100       202         if(defined $data->[1][$j])
332 14         164         { $dataset_sum += $data->[1][$j];
333             # $sum_total += $data->[1][$j];
334                     }
335                  }
336              
337             # find out how who wide the largest label text is, especially look what kind of
338             # label is needed
339 2         27   foreach (@labels) {
340 14 100       140    if ( length($_) > $l1) {
341 4         35      $l1 = length($_);
342                }
343               }
344               for (my $i =0 ; $i < ($self->{'num_datapoints'}) ; $i++) {
345 14 100       1234    if ( length($data->[1][$i]) > $l2 ) {
346 3         35       $l2 = length($data->[1][$i]);
347                }
348 2         21   }
349              
350              
351 2 50       38   if ($self->{'legend_label_values'} =~ /^value$/i ) {
    0          
    0          
352 2         19     $max_label_len = $l1 + $l2 +1;
353               }
354               elsif ($self->{'legend_label_values'} =~ /^percent$/i ) {
355 0         0     $max_label_len = $l1 +7;
356               }
357               elsif ($self->{'legend_label_values'} =~ /^both$/i ) {
358 0         0     $max_label_len = $l1 + $l2 +9;
359               }
360               else {
361 0         0     $max_label_len = $l1;
362               }
363               
364             # figure out how wide the columns need to be, and how many we
365             # can fit in the space available
366 2         24   $empty_width = ($x2 - $x1) - (2 * $self->{'legend_space'});
367 2         22   $max_label_width = $max_label_len * $w
368             #$self->{'max_legend_label'} * $w
369                 + (4 * $self->{'text_space'}) + $self->{'legend_example_size'};
370 2         22   $cols = int ($empty_width / $max_label_width);
371 2 50       327   unless ($cols) {
372 0         0     $cols = 1;
373               }
374 2         19   $col_width = $empty_width / $cols;
375              
376             # figure out how many rows we need, remember how tall they are
377 2         21   $rows = int ($self->{'num_datapoints'} / $cols);
378 2 100       28   unless (($self->{'num_datapoints'} % $cols) == 0) {
379 1         9     $rows++;
380               }
381 2 50       22   unless ($rows) {
382 0         0     $rows = 1;
383               }
384 2         44   $row_height = $h + $self->{'text_space'};
385              
386             # box the legend off
387 2         60   $y1 = $self->{'curr_y_max'} - $self->{'text_space'}
388                       - ($rows * $row_height) - (2 * $self->{'legend_space'});
389 2         25   $y2 = $self->{'curr_y_max'};
390 2         34   $self->{'gd_obj'}->rectangle($x1, $y1, $x2, $y2,
391                                            $self->_color_role_to_index('misc'));
392 2         22   $x1 += $self->{'legend_space'} + $self->{'text_space'};
393 2         20   $x2 -= $self->{'legend_space'};
394 2         21   $y1 += $self->{'legend_space'} + $self->{'text_space'};
395 2         21   $y2 -= $self->{'legend_space'} + $self->{'text_space'};
396              
397             # draw in the actual legend
398 2         24   for $r (0..$rows-1) {
399 3         30     for $c (0..$cols-1) {
400 16         152       $index = ($r * $cols) + $c; # find the index in the label array
401 16 100       160       if ($labels[$index]) {
402             # get the color
403 14         204         $color = $self->_color_role_to_index('dataset'.$index);
404              
405             # get the x-y coordinate for the start of the example line
406 14         130 $x = $x1 + ($col_width * $c);
407 14         154         $y = $y1 + ($row_height * $r) + $h/2;
408              
409             # now draw the example line
410 14         270         $self->{'gd_obj'}->line($x, $y,
411                                             $x + $self->{'legend_example_size'}, $y,
412                                             $color);
413              
414             # reset the brush for points
415 14         219         $brush = $self->_prepare_brush($color, 'point',
416             $self->{'pointStyle' . $index});
417 14         363         $self->{'gd_obj'}->setBrush($brush);
418             # draw the point
419 14         141         $x3 = int($x + $self->{'legend_example_size'}/2);
420 14         163         $self->{'gd_obj'}->line($x3, $y, $x3, $y, gdBrushed);
421              
422             # adjust the x-y coordinates for the start of the label
423 14         142 $x += $self->{'legend_example_size'} + (2 * $self->{'text_space'});
424 14         118         $y = $y1 + ($row_height * $r);
425              
426             # now draw the label
427 14 50       171         if ( $self->{'legend_label_values'} =~ /^value$/i ) {
    0          
    0          
428 14         1115            $self->{'gd_obj'}->string($font, $x, $y, $labels[$index].' '.$data->[1][$index], $color);
429             #$self->{'gd_obj'}->stringTTF($color, FONT, 10, 0, $x, $y+10, $labels[$index].' '.$data->[1][$index]); ############
430                     }
431                     elsif ( $self->{'legend_label_values'} =~ /^percent$/i ) {
432 0         0            $label = sprintf("%s %4.2f%%",$labels[$index], $data->[1][$index] / $dataset_sum * 100);
433 0         0            $self->{'gd_obj'}->string($font, $x, $y, $label, $color);
434                     }
435                     elsif ( $self->{'legend_label_values'} =~ /^both$/i ) {
436 0 0       0            if ($data->[1][$index] =~ /\./) {
437 0         0               $label = sprintf("%s %4.2f%% %.2f",$labels[$index], $data->[1][$index] / $dataset_sum * 100, $data->[1][$index]);
438                        }
439                        else {
440 0         0               $label = sprintf("%s %4.2f%% %d",$labels[$index], $data->[1][$index] / $dataset_sum * 100, $data->[1][$index]);
441                        }
442 0         0             $self->{'gd_obj'}->string($font, $x, $y, $label, $color); ###
443             # $self->{'gd_obj'}->stringTTF($color, FONT, 10, 0, $x, $y, $label);
444               
445                     }
446                     else {
447 0         0            $self->{'gd_obj'}->string($font, $x, $y, $labels[$index], $color);
448                     }
449                   }
450                 }
451               }
452              
453             # mark off the space used
454 2         28   $self->{'curr_y_max'} -= ($rows * $row_height) + $self->{'text_space'}
455             + (2 * $self->{'legend_space'});
456              
457             # now return
458 2         36   return 1;
459             }
460              
461              
462             ## put the legend on top of the chart
463             sub _draw_top_legend {
464 1     1   10   my $self = shift;
465 1         10   my $data = $self->{'dataref'};
466 1         9   my ($max_label_len);
467 1         9   my $l1 = 0;
468 1         9   my $l2 = 0;
469 1         9   my @labels = @{$data->[0]};
  1         14  
470 1         10   my ($x1, $y1, $x2, $x3, $y2, $empty_width, $max_label_width, $cols, $rows, $color, $brush);
471 1         9   my ($col_width, $row_height, $r, $c, $index, $x, $y, $w, $h, $dataset_sum, $label);
472 1         9   my $font = $self->{'legend_font'};
473              
474             # make sure we're using a real font
475 1 50       12   unless ((ref ($font)) eq 'GD::Font') {
476 0         0     croak "The subtitle font you specified isn\'t a GD Font object";
477               }
478              
479             # get the size of the font
480 1         16   ($h, $w) = ($font->height, $font->width);
481              
482             #find out what the sum of all datapoits is, needed for the Labels with percent
483 1         12     for my $j (0..$self->{'num_datapoints'}) {
484 6 100       65         if(defined $data->[1][$j])
485                     {
486 5         45            $dataset_sum += $data->[1][$j];
487                     }
488                  }
489                  
490             # get some base x coordinates
491 1         11   $x1 = $self->{'curr_x_min'}
492                       + $self->{'graph_border'};
493             # + $self->{'y_tick_label_length'} * $self->{'tick_label_font'}->width
494             # + $self->{'tick_len'} + (3 * $self->{'text_space'});
495 1         10   $x2 = $self->{'curr_x_max'} - $self->{'graph_border'};
496 1 50       10   if ($self->{'y_label'}) {
497 0         0     $x1 += $self->{'label_font'}->height + 2 * $self->{'text_space'};
498               }
499 1 50       12   if ($self->{'y_label2'}) {
500 0         0     $x2 -= $self->{'label_font'}->height + 2 * $self->{'text_space'};
501               }
502              
503             # find out how who wide the largest label text is
504 1         10   foreach (@labels) {
505 5 100       49    if ( length($_) > $l1) {
506 3         25      $l1 = length($_);
507                }
508               }
509               for (my $i =0 ; $i < ($self->{'num_datapoints'}) ; $i++) {
510 5 100       61    if ( length($data->[1][$i]) > $l2 ) {
511 1         11       $l2 = length($data->[1][$i]);
512                }
513 1         10   }
514              
515 1 50       13   if ($self->{'legend_label_values'} =~ /^value$/i ) {
    0          
    0          
516 1         9     $max_label_len = $l1 + $l2 +1;
517               }
518               elsif ($self->{'legend_label_values'} =~ /^percent$/i ) {
519 0         0     $max_label_len = $l1 +7;
520               }
521               elsif ($self->{'legend_label_values'} =~ /^both$/i ) {
522 0         0     $max_label_len = $l1 + $l2 +9;
523               }
524               else {
525 0         0     $max_label_len = $l1;
526               }
527              
528             # figure out how wide the columns can be, and how many will fit
529 1         10   $empty_width = ($x2 - $x1) - (2 * $self->{'legend_space'});
530 1         11   $max_label_width = (4 * $self->{'text_space'})
531             # + ($self->{'max_legend_label'} * $w)
532                 + $max_label_len * $w
533                 + $self->{'legend_example_size'};
534 1         10   $cols = int ($empty_width / $max_label_width);
535 1 50       11   unless ($cols) {
536 0         0     $cols = 1;
537               }
538 1         9   $col_width = $empty_width / $cols;
539              
540             # figure out how many rows we need and remember how tall they are
541 1         10   $rows = int ($self->{'num_datapoints'} / $cols);
542 1 50       12   unless (($self->{'num_datapoints'} % $cols) == 0) {
543 1         9     $rows++;
544               }
545 1 50       11   unless ($rows) {
546 0         0     $rows = 1;
547               }
548 1         9   $row_height = $h + $self->{'text_space'};
549              
550             # box the legend off
551 1         50   $y1 = $self->{'curr_y_min'};
552 1         10   $y2 = $self->{'curr_y_min'} + $self->{'text_space'}
553                       + ($rows * $row_height) + (2 * $self->{'legend_space'});
554 1         471   $self->{'gd_obj'}->rectangle($x1, $y1, $x2, $y2,
555                                            $self->_color_role_to_index('misc'));
556              
557             # leave some space inside the legend
558 1         10   $x1 += $self->{'legend_space'} + $self->{'text_space'};
559 1         11   $x2 -= $self->{'legend_space'};
560 1         11   $y1 += $self->{'legend_space'} + $self->{'text_space'};
561 1         10   $y2 -= $self->{'legend_space'} + $self->{'text_space'};
562              
563             # draw in the actual legend
564 1         11   for $r (0..$rows-1) {
565 2         20     for $c (0..$cols-1) {
566 8         68       $index = ($r * $cols) + $c; # find the index in the label array
567 8 100       80       if ($labels[$index]) {
568             # get the color
569 5         66         $color = $self->_color_role_to_index('dataset'.$index);
570              
571             # find the x-y coords
572 5         44 $x = $x1 + ($col_width * $c);
573 5         46         $y = $y1 + ($row_height * $r) + $h/2;
574              
575             # draw the line first
576 5         94         $self->{'gd_obj'}->line($x, $y,
577                                             $x + $self->{'legend_example_size'}, $y,
578                                             $color);
579              
580             # reset the brush for points
581 5         75         $brush = $self->_prepare_brush($color, 'point',
582             $self->{'pointStyle' . $index});
583 5         172         $self->{'gd_obj'}->setBrush($brush);
584             # draw the point
585 5         53         $x3 = int($x + $self->{'legend_example_size'}/2);
586 5         84         $self->{'gd_obj'}->line($x3, $y, $x3, $y, gdBrushed);
587              
588             # now the label
589 5         52 $x += $self->{'legend_example_size'} + (2 * $self->{'text_space'});
590 5         42 $y -= $h/2;
591 5 50       71         if ( $self->{'legend_label_values'} =~ /^value$/i ) {
    0          
    0          
592 5         173          $self->{'gd_obj'}->string($font, $x, $y, $labels[$index].' '.$data->[1][$index], $color);
593                     }
594                     elsif ( $self->{'legend_label_values'} =~ /^percent$/i ) {
595 0         0          $label = sprintf("%s %4.2f%%",$labels[$index], $data->[1][$index] / $dataset_sum * 100);
596 0         0          $self->{'gd_obj'}->string($font, $x, $y, $label, $color);
597                     }
598                     elsif ( $self->{'legend_label_values'} =~ /^both$/i ) {
599 0 0       0           if ( $data->[1][$index] =~ /\./) {
600 0         0              $label = sprintf("%s %4.2f%% %.2f",$labels[$index], $data->[1][$index] / $dataset_sum * 100, $data->[1][$index]);
601                       }
602                       else {
603 0         0              $label = sprintf("%s %4.2f%% %d",$labels[$index], $data->[1][$index] / $dataset_sum * 100, $data->[1][$index]);
604                       }
605 0         0           $self->{'gd_obj'}->string($font, $x, $y, $label, $color);
606                     }
607                     else {
608 0         0          $self->{'gd_obj'}->string($font, $x, $y, $labels[$index], $color);
609                     }
610                    }
611                 }
612               }
613              
614             # mark off the space used
615 1         14   $self->{'curr_y_min'} += ($rows * $row_height) + $self->{'text_space'}
616             + 2 * $self->{'legend_space'};
617              
618             # now return
619 1         18   return 1;
620             }
621              
622             # Override the ticks methods for the pie charts
623             # as they do not always make sense.
624             sub _draw_x_ticks {
625 5     5   50   my $self = shift;
626              
627 5         62   return;
628             }
629             sub _draw_y_ticks {
630 5     5   46   my $self = shift;
631              
632 5         54   return;
633             }
634              
635             sub _find_y_scale {
636 5     5   71   my $self = shift;
637              
638 5         56   return;
639             }
640              
641              
642              
643             ## finally get around to plotting the data
644             sub _draw_data {
645 5     5   47   my $self = shift;
646 5         50   my $data = $self->{'dataref'};
647 5         65   my $misccolor = $self->_color_role_to_index('misc');
648 5         62   my $textcolor = $self->_color_role_to_index('text');
649 5         61   my $background = $self->_color_role_to_index('background');
650 5         47   my ($width, $height, $centerX, $centerY, $diameter);
651 5         44   my $sum_total;
652 5         68   my $dataset_sum;
653 5         47   my ($start_degrees, $end_degrees, $label_degrees, $max_label_len);
654 5         48   my ($pi, $font, $fontW, $fontH, $labelX, $labelY, $label_offset);
655 5         45   my ($last_labelX, $last_labelY, $label, $max_val_len);
656 5         44   my ($i, $j, $color);
657 5         45   my $label_length;
658              
659             # set up initial constant values
660 5         45   $pi = 3.14159265;
661 5         46   $start_degrees=0;
662 5         46   $end_degrees=0;
663 5         47   $font = $self->{'legend_font'};
664 5         47   $label_offset = .55;
665 5         67   $fontW = $self->{'legend_font'}->width;
666 5         127   $fontH = $self->{'legend_font'}->height;
667 5         45   $last_labelX = 0;
668 5         45   $last_labelY = 0;
669              
670             # init the imagemap data field if they wanted it
671 5 50       66   if ($self->{'imagemap'} =~ /^true$/i) {
672 0         0     $self->{'imagemap_data'} = [];
673               }
674              
675             # find width and height
676 5         49   $width = $self->{'curr_x_max'} - $self->{'curr_x_min'};
677 5         1179   $height = $self->{'curr_y_max'} - $self->{'curr_y_min'};
678              
679              
680             # find the longest label
681             # first we need the length of the values
682 5         68   $max_val_len = 1;
683 5         63   for (0..$self->{'num_datapoints'}) {
684 40 100       411      if (defined $data->[1][$_]) {
685 35 100       400         if ( length($data->[1][$_]) > $max_val_len) {
686 6         136          $max_val_len = length($data->[1][$_]);
687                  }
688                }
689              
690               }
691              
692             # now the whole label
693 5         52   $max_label_len = 1;
694 5         55   for $j (0..$self->{'num_datapoints'}) {
695 40 100       398      if(defined $data->[0][$j]) {
696 35 100       357         if( length($data->[0][$j]) > $max_label_len) {
697 11         130            $max_label_len = length($data->[0][$j]);
698                    }
699                  }
700                
701               }
702              
703 5 50       69   if ( defined $self->{'label_values'} ) {
704 5 50       106     if ($self->{'label_values'} =~ /^value$/i) {
    100          
    100          
705 0         0       $max_label_len += $max_val_len + 1;
706                 }
707                 elsif ( $self->{'label_values'} =~ /^both$/i ){
708 1         9       $max_label_len += $max_val_len + 7;
709                 }
710                 elsif ( $self->{'label_values'} =~ /^percent$/i ) {
711 3         32       $max_label_len += 6;
712                 }
713               }
714 5         49   $max_label_len *= $fontW;
715              
716             # find center point, from which the pie will be drawn around
717 5         61   $centerX = int($width/2) + $self->{'curr_x_min'};
718 5         77   $centerY = int($height/2) + $self->{'curr_y_min'};
719              
720             # always draw a circle, which means the diameter will be the smaller
721             # of the width and height. let enougth space for the label.
722               
723              
724               
725 5 50       58   if ($width < $height) {
726 0         0    $diameter = $width - 2*$max_label_len -20;
727                 }
728               else {
729 5         47     $diameter = $height - 2*$fontH -20 ;
730 5 100       155       if ( $width < ($diameter + 2 * $max_label_len) ) {
731 2         21     $diameter = $width - 2*$max_label_len -20 ;
732                   }
733               }
734              
735             # make sure, that we have a positiv diameter
736 5 50       58   if ($diameter < 0) {
737 0         0    croak "I have calculated a negative diameter for the pie chart, maybe your labels are to long or the picture is to small.";
738               }
739               
740             # okay, add up all the numbers of all the datasets, to get the
741             # sum total. This will be used to determine the percentage
742             # of each dataset. Obviously, negative numbers might be bad :)
743 5         50   $sum_total=0;
744              
745 5         54    for $j (0..$self->{'num_datapoints'}) {
746 40 100       405         if(defined $data->[1][$j])
747                     { #add to sum
748 35         299            $dataset_sum += $data->[1][$j];
749             #don't allow negativ values
750 35 50       378            if ($data->[1][$j] < 0) {
751 0         0              croak "We need positiv data for a pie chart!";
752                        }
753                     }
754                }
755                
756 5         56    for $j (0..($self->{'num_datapoints'}-1)) {
757             # get the color for this datapoint, take the color of the datasets
758 35         529       $color = $self->_color_role_to_index('dataset'.$j);
759             # don't try to draw anything if there's no data
760 35 50       1297       if (defined ($data->[1][$j])) {
761 35         327          $label = $data->[0][$j];
762 35 50       575          if(defined $self->{'label_values'}) {
763 35 100       475               if($self->{'label_values'} =~ /^percent$/i)
    50          
    100          
    50          
764                            {
765 19         1145                  $label = sprintf("%s %4.2f%%",$label, $data->[1][$j] / $dataset_sum * 100);
766                            }
767                           elsif($self->{'label_values'} =~ /^value$/i)
768                            {
769 0 0       0                  if ($data->[1][$j] =~ /\./) {
770 0         0                    $label = sprintf("%s %.2f",$label, $data->[1][$j]);
771                              }
772                              else {
773 0         0                    $label = sprintf("%s %d",$label,$data->[1][$j]);
774                              }
775                            }
776                           elsif($self->{'label_values'} =~ /^both$/i)
777                            {
778 6 50       64                  if ($data->[1][$j] =~ /\./) {
779 0         0                    $label = sprintf("%s %4.2f%% %.2f",$label,
780                                                       $data->[1][$j] / $dataset_sum * 100,
781                                                       $data->[1][$j]);
782                              }
783                              else {
784 6         179                    $label = sprintf("%s %4.2f%% %d",$label,
785                                                       $data->[1][$j] / $dataset_sum * 100,
786                                                       $data->[1][$j]);
787                              }
788                            }
789                            elsif($self->{'label_values'} =~ /^none$/i)
790                            {
791 10         97                  $label = sprintf("%s",$label);
792                            }
793                     }
794            
795 35         312 $label_length = length($label);
796            
797            
798                   }
799               
800              
801             # The first value starts at 0 degrees, each additional dataset
802             # stops where the previous left off, and since I've already
803             # calculated the sum_total for the whole graph, I know that
804             # the final pie slice will end at 360 degrees.
805              
806             # So, get the degree offset for this dataset
807 35         590     $end_degrees = $start_degrees + ($data->[1][$j] / $dataset_sum * 360);
808              
809             # stick the label in the middle of the slice
810 35         293     $label_degrees = ($start_degrees + $end_degrees) / 2;
811              
812             # The following drawings are in a very specific ordering, and are not
813             # intuitive as to why they are being done this way, but it is basically
814             # because the GD module doesn't provide a filledArc() method. So, I
815             # developed my own, below.
816              
817             # First, draw an arc, in black, from the starting offset, all the
818             # way to 360 degrees.
819 35         3374     $self->{'gd_obj'}->arc($centerX,$centerY,
820                                 $diameter, $diameter,
821                                 $start_degrees, 360,
822                                 $misccolor);
823              
824             # This is tricky, but draw a short line in the desired color, along the
825             # path that will be the end of this pie slice of data. But, make sure not
826             # to extend this line to intersect with the boundary of the arc. This
827             # is crucial.
828 35         974     $self->{'gd_obj'}->line($centerX, $centerY,
829                                 $centerX + .4*$diameter*cos($end_degrees*$pi/180),
830                                 $centerY + .4*$diameter*sin($end_degrees*$pi/180),
831                                 $color);
832              
833             # Draw the radius of the beginning side of the pie slice, in black
834 35         638     $self->{'gd_obj'}->line($centerX,$centerY,
835                                 $centerX + .5*$diameter*cos($start_degrees*$pi/180),
836                                 $centerY + .5*$diameter*sin($start_degrees*$pi/180),
837                                 $misccolor);
838              
839             # Now, execute fillToBorder, starting from a point on the end line, in the
840             # desired pie slice color, and fill until a black pixel if encountered.
841             # What this means, is that a series of pie slices is drawn, each starting
842             # at the correct location, but each ending at 360 degrees.
843 35         60702     $self->{'gd_obj'}->fillToBorder(
844                                 $centerX + .4*$diameter*cos($end_degrees*$pi/180),
845                                 $centerY + .4*$diameter*sin($end_degrees*$pi/180),
846                                 $misccolor,$color);
847              
848             # Figure out where to place the label
849 35         516     $labelX = $centerX+$label_offset*$diameter*cos($label_degrees*$pi/180);
850 35         781     $labelY = $centerY+$label_offset*$diameter*sin($label_degrees*$pi/180);
851              
852                
853             # If label is to the left of the pie chart, make sure the label doesn't
854             # bleed into the chart. So, back it up the length of the label
855 35 100       430     if($labelX < $centerX)
856                 {
857 15         190        $labelX -= (length($label) * $fontW);
858                 }
859              
860             # Same thing if the label is above the chart. Don't go too low.
861 35 100       399     if($labelY < $centerY)
862                 {
863 19         443        $labelY -= $fontH;
864                 }
865              
866             # Okay, if a bunch of very small datasets are close together, they can
867             # overwrite each other. The following if statement is to help keep
868             # labels of neighbor datasets from beong overlapped. It ain't perfect,
869             # but it des a pretty good job.
870 35 100 100     629     if($label_degrees <= 90 || $label_degrees >= 270)
871                 {
872 20 50 66     384        if(($labelY - $last_labelY) < $fontH &&
      33        
873                       sqrt(($labelY-$last_labelY)**2 + ($labelX-$last_labelX)**2) < $fontH*2 &&
874                        $last_labelY > 0)
875                    {
876 0         0           $labelY = $last_labelY + $fontH;
877                    }
878                 }
879                 else
880                 {
881 15 50 66     227        if(($last_labelY - $labelY) < $fontH &&
      33        
882                       sqrt(($labelY-$last_labelY)**2 + ($labelX-$last_labelX)**2) < $fontH*2 &&
883                       $last_labelY > 0)
884                    {
885 0         0           $labelY = $last_labelY - $fontH;
886                    }
887                 }
888              
889             # Now, draw the label for this pie slice
890                 
891             # Is enought space inside the border for the labels?
892                 
893 35         447   while ($labelX-($label_length*1)<$self->{'curr_x_min'}) {
894 45         497        $labelX+=1;
895               }
896               
897 35         572   while ($labelX+($label_length*$fontW)>$self->{'curr_x_max'}) {
898 4         44         $labelX-=1;
899               }
900              
901 35         401   while (($labelY-$fontH)<=$self->{'curr_y_min'}) {
902 27         269         $labelY+=1;
903               }
904              
905 35         386   while(($labelY+$fontH)>=$self->{'curr_y_max'}) {
906 2         23         $labelY-=1;
907               }
908            
909 35         1047   $self->{'gd_obj'}->string($font, $labelX, $labelY, $label, $textcolor);
910                   
911             # reset starting point for next dataset and continue.
912 35         450     $start_degrees = $end_degrees;
913 35         401     $last_labelX = $labelX;
914 35         317     $last_labelY = $labelY;
915               }
916               
917             # and finaly box it off
918 5         353   $self->{'gd_obj'}->rectangle ($self->{'curr_x_min'},
919                                             $self->{'curr_y_min'},
920                                             $self->{'curr_x_max'},
921                                             $self->{'curr_y_max'},
922                                             $misccolor);
923 5         70   return;
924              
925             }
926              
927              
928             ## be a good module and return 1
929             1;
930