File Coverage

blib/lib/Chart/Pie.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             ## @file
2             # Implementation of Chart::Pie
3             #
4             # written and maintained by
5             # @author Chart Group at Geodetic Fundamental Station Wettzell (Chart@fs.wettzell.de)
6             # @date 2012-10-03
7             # @version 2.4.6
8             #
9              
10             ## @class Chart::Pie
11             # @brief Pie class derived class for Chart to implement pies
12             #
13             package Chart::Pie;
14              
15 11     11   57454 use Chart::Base '2.4.6';
  0            
  0            
16             use GD;
17             use Carp;
18             use Chart::Constants;
19             use strict;
20              
21             @Chart::Pie::ISA = qw(Chart::Base);
22             $Chart::Pie::VERSION = '2.4.6';
23              
24             #>>>>>>>>>>>>>>>>>>>>>>>>>>#
25             # public methods go here #
26             #<<<<<<<<<<<<<<<<<<<<<<<<<<#
27              
28             #>>>>>>>>>>>>>>>>>>>>>>>>>>>#
29             # private methods go here #
30             #<<<<<<<<<<<<<<<<<<<<<<<<<<<#
31              
32             ## @fn private _draw_data
33             # @brief
34             # finally get around to plotting the data
35             #
36             # @details
37             # The user may define the kind of labelling the data by setting\n
38             # 'label_values' to 'percent' if she wants to have the percentages\n
39             # 'label_values' to 'values' if she wants to have the absolut values\n
40             # 'label_values' to 'both' if she wants to have absolut values and percentages\n
41             # 'label_values' to 'none' if she wants to have neither absolute values nor percentages\n
42             # 'ring' to a number less then 1 to define a ring as output;
43             # if 'ring' is 1 ore greater a full pie is plotted\n
44             #
45             sub _draw_data
46             {
47             my $self = shift;
48             my $data = $self->{'dataref'};
49             my $misccolor = $self->_color_role_to_index('misc');
50             my $textcolor = $self->_color_role_to_index('text');
51             my $background = $self->_color_role_to_index('background');
52             my ( $width, $height, $centerX, $centerY, $diameter, $diameter_previous, $text_diameter );
53             my $dataset_sum;
54             my ( $start_degrees, $end_degrees, $label_degrees, $label_old_degrees, $labelY_repeat_count );
55             my ( $pi, $rd2dg, $dg2rd );
56             my ( $font, $fontW, $fontH, $labelX, $labelY );
57             my $label;
58             my ( $i, $j, $color );
59             my $label_length;
60             my $degrees = 0;
61             my $insidecolor;
62             my $forbidden_degrees = 0; # last occupied degree
63             my %labelinfo;
64             my $max_val_len = 0;
65             my $max_label_len = 0;
66              
67             # set up initial constant values
68             $pi = Chart::Constants::PI;
69              
70             $dg2rd = $pi / 180; # Degree to Radians
71             $rd2dg = 180 / $pi; # Radian to Degree
72             $start_degrees = 0;
73             $end_degrees = 0;
74             $font = $self->{'legend_font'};
75             $fontW = $self->{'legend_font'}->width;
76             $fontH = $self->{'legend_font'}->height;
77             $label_degrees = $labelY_repeat_count = 0;
78              
79             # init the imagemap data field if they wanted it
80             if ( $self->true( $self->{'imagemap'} ) )
81             {
82             $self->{'imagemap_data'} = [];
83             }
84              
85             # find width and height of the plotting area
86             $width = $self->{'curr_x_max'} - $self->{'curr_x_min'};
87             $height = $self->{'curr_y_max'} - $self->{'curr_y_min'};
88              
89             # okay, add up all the numbers of all the datasets, to get the
90             # sum total. This will be used to determine the percentage
91             # of each dataset. Obviously, negative numbers might be bad :)
92             $dataset_sum = 0;
93             for $j ( 0 .. $self->{'num_datapoints'} )
94             {
95              
96             if ( defined $data->[1][$j] )
97             {
98              
99             #add to sum
100             $dataset_sum += $data->[1][$j];
101              
102             #don't allow negativ values
103             if ( $data->[1][$j] < 0 )
104             {
105             croak "We need positiv data for a pie chart (which is not true for data[$j])!";
106             }
107             }
108             }
109              
110             # find the longest label
111             # first we need the length of the values
112             $max_label_len = 1;
113             for $j ( 0 .. ( $self->{'num_datapoints'} - 1 ) )
114             {
115              
116             # don't try to draw anything if there's no data
117             $labelinfo{$j}{data} = 'undefined';
118             if ( defined( $data->[1][$j] ) )
119             {
120             $labelinfo{$j}{data} = $data->[1][$j];
121             $label = $data->[0][$j];
122             $labelinfo{$j}{labeldata} = $label;
123              
124             if ( defined $self->{'label_values'} )
125             {
126             if ( $self->{'label_values'} =~ /^percent$/i )
127             {
128             $label = sprintf( "%s %4.2f%%", $label, $data->[1][$j] / ( $dataset_sum || 1 ) * 100 );
129             }
130             elsif ( $self->{'label_values'} =~ /^value$/i )
131             {
132             if ( $data->[1][$j] =~ /\./ )
133             {
134             $label = sprintf( "%s %.2f", $label, $data->[1][$j] );
135             }
136             else
137             {
138             $label = sprintf( "%s %d", $label, $data->[1][$j] );
139             }
140             }
141             elsif ( $self->{'label_values'} =~ /^both$/i )
142             {
143             if ( $data->[1][$j] =~ /\./ )
144             {
145             $label =
146             sprintf( "%s %4.2f%% %.2f", $label, $data->[1][$j] / ( $dataset_sum || 1 ) * 100, $data->[1][$j] );
147             }
148             else
149             {
150             $label =
151             sprintf( "%s %4.2f%% %d", $label, $data->[1][$j] / ( $dataset_sum || 1 ) * 100, $data->[1][$j] );
152             }
153             }
154             elsif ( $self->{'label_values'} =~ /^none$/i )
155             {
156             $label = sprintf( "%s", $label );
157             }
158             }
159             $label_length = length($label);
160             $labelinfo{$j}{labelstring} = $label, $labelinfo{$j}{labellength} = $label_length;
161              
162             }
163             $max_label_len = $label_length if ( $max_label_len < $label_length );
164             }
165             $max_label_len *= $fontW;
166              
167             # find center point, from which the pie will be drawn around
168             $centerX = int( $width / 2 ) + $self->{'curr_x_min'};
169             $centerY = int( $height / 2 ) + $self->{'curr_y_min'};
170              
171             # @details
172             # always draw a circle, which means the diameter will be the smaller
173             # of the width and height. let enough space for the labels.\n
174             # Calculate the space needed for the labels by taking
175             # into account the angles where the labels will be plotted
176              
177             my $labeldistance = 2 * $self->maximum( $fontW, $fontH );
178              
179             $start_degrees = 0;
180             $end_degrees = 0;
181             my $max_radius = $self->minimum( $width, $height ) / 2;
182             my $radius = $max_radius;
183              
184             for $j ( 0 .. ( $self->{'num_datapoints'} - 1 ) )
185             {
186              
187             # So, get the degree offset for this dataset
188             $end_degrees = $start_degrees + ( $data->[1][$j] / ( $dataset_sum || 1 ) * 360 );
189              
190             $degrees = $start_degrees + ( $end_degrees - $start_degrees ) / 2;
191              
192             # stick the label in the middle of the slice
193             $label_degrees = ( $start_degrees + $end_degrees ) / 2;
194              
195             $label = $labelinfo{$j}{labelstring};
196             $label_length = $labelinfo{$j}{labellength};
197              
198             if ( ( $label_degrees >= 270 && $label_degrees <= 360 )
199             || ( $label_degrees >= 0 && $label_degrees <= 90 ) )
200             {
201              
202             # right side of the circle
203             # as 0 degrees means east
204              
205             if ( abs($label_degrees) < 0.1 )
206             {
207              
208             # too small angle
209             $radius = $self->{'curr_x_max'} - $label_length * $fontW;
210             }
211             else
212             {
213              
214             # $x value in respect of label arc
215             my $x = $radius * sin( $dg2rd * $label_degrees );
216             my $y = $radius * cos( $dg2rd * $label_degrees );
217              
218             # i.e. the startpoint is at $centerX+$x, $centerY-$y
219             #test
220             #$self->{'gd_obj'}->rectangle( $centerX, $centerY, $centerX+$x, $centerY-$y, $misccolor );
221              
222             # theoretical right value in respect to radius and length of label
223             my $right_pos = $centerX + $x + $label_length * $fontW;
224             if ( $right_pos > $self->{'curr_x_max'} )
225             {
226              
227             # too far right, correct x
228             $right_pos = $self->{'curr_x_max'};
229             $x = $right_pos - $centerX - $label_length * $fontW;
230             }
231              
232             # theoretical top position in respect to radius and height of label
233             # (Remark: direction to the top of the picture counts backwards!)
234             my $top_pos = $centerY - $y - $fontH;
235             if ( $top_pos < $self->{'curr_y_min'} )
236             {
237              
238             # too far up, correct $y
239             $top_pos = $self->{'curr_y_min'};
240             $y = $centerY - $top_pos - $fontH;
241             }
242              
243             my $down_pos = $centerY + $y + $fontH;
244             if ( $down_pos > $self->{'curr_y_max'} )
245             {
246             $down_pos = $self->{'curr_y_max'};
247             $y = $down_pos - $centerY - $fontH;
248             }
249              
250             #test
251             #$self->{'gd_obj'}->rectangle( $centerX+$x, $centerY-$y, $right_pos, $top_pos, $textcolor );
252             #$self->{'gd_obj'}
253             # ->rectangle( $self->{'curr_x_min'}, $self->{'curr_y_min'}, $self->{'curr_x_max'}, $self->{'curr_y_max'}, $misccolor );
254             #return;
255              
256             $radius =
257             $self->minimum( $radius, $x / sin( $dg2rd * $label_degrees ), abs( $y / cos( $dg2rd * $label_degrees ) ) );
258             $radius = int( $radius + 0.5 );
259              
260             #test
261             #$self->{'gd_obj'}->line( $centerX, $centerY, $centerX+$radius, $centerY, gdBrushed );
262              
263             }
264             if ( $radius <= 0 ) { croak "radius < 0!"; }
265             }
266             else
267             { # left side of the circle
268             # as 0 degrees means east
269              
270             if ( abs($label_degrees) < 0.1 )
271             {
272              
273             # too small angle
274             $radius = $self->{'curr_x_max'} - $label_length * $fontW;
275             }
276             else
277             {
278              
279             # $x value in respect of label arc
280             my $x = $radius * sin( $dg2rd * $label_degrees );
281             my $y = $radius * cos( $dg2rd * $label_degrees );
282              
283             # i.e. the startpoint is at $centerX-$x, $centerY+$y
284             #test
285             #$self->{'gd_obj'}->rectangle( $centerX, $centerY, $centerX-$x, $centerY+$y, $misccolor );
286              
287             # theoretical right value in respect to radius and length of label
288             my $left_pos = $centerX - $x - $label_length * $fontW;
289             if ( $left_pos < $self->{'curr_x_min'} )
290             {
291              
292             # too far right, correct x
293             $left_pos = $self->{'curr_x_min'};
294             $x = $centerX - $left_pos - $label_length * $fontW;
295             }
296              
297             # theoretical top position in respect to radius and height of label
298             # (Remark: direction to the top of the picture counts backwards!)
299             my $top_pos = $centerY + $y - $fontH;
300             if ( $top_pos < $self->{'curr_y_min'} )
301             {
302              
303             # too far up, correct $y
304             $top_pos = $self->{'curr_y_min'};
305             $y = $centerY - $top_pos - $fontH;
306             }
307              
308             my $down_pos = $centerY - $y + $fontH;
309             if ( $down_pos > $self->{'curr_y_max'} )
310             {
311             $down_pos = $self->{'curr_y_max'};
312             $y = $centerY + $fontH - $down_pos;
313             }
314              
315             #test
316             #$self->{'gd_obj'}->rectangle( $centerX-$x, $centerY+$y, $left_pos, $top_pos, $textcolor );
317             #$self->{'gd_obj'}
318             # ->rectangle( $self->{'curr_x_min'}, $self->{'curr_y_min'}, $self->{'curr_x_max'}, $self->{'curr_y_max'}, $misccolor );
319             #return;
320              
321             $radius =
322             $self->minimum( $radius, $x / sin( $dg2rd * $label_degrees ), abs( $y / cos( $dg2rd * $label_degrees ) ) );
323             $radius = int( $radius + 0.5 );
324              
325             #test
326             #$self->{'gd_obj'}->line( $centerX, $centerY, $centerX+$radius, $centerY, gdBrushed );
327              
328             }
329             if ( $radius <= 0 ) { croak "radius < 0!"; }
330             }
331              
332             # reset starting point for next dataset and continue.
333             $start_degrees = $end_degrees;
334             }
335             $diameter = $radius * 2 - 2 * $labeldistance;
336              
337             $text_diameter = $diameter + $labeldistance;
338             $self->{'gd_obj'}->arc( $centerX, $centerY, $diameter, $diameter, 0, 360, $misccolor );
339              
340             # for DEBUG!!
341             #$self->{'gd_obj'}->arc($centerX, $centerY, $text_diameter, $text_diameter,
342             # 0, 360, $misccolor);
343              
344             # @details
345             # Plot the pies
346             $start_degrees = 0;
347             $end_degrees = 0;
348             for $j ( 0 .. ( $self->{'num_datapoints'} - 1 ) )
349             {
350              
351             #next if $labelinfo{$j}{data} eq 'undefined';
352             # get the color for this datapoint, take the color of the datasets
353             $color = $self->_color_role_to_index( 'dataset' . $j );
354              
355             $label = $labelinfo{$j}{labelstring};
356             $label_length = $labelinfo{$j}{labellength};
357              
358             # The first value starts at 0 degrees, each additional dataset
359             # stops where the previous left off, and since I've already
360             # calculated the sum_total for the whole graph, I know that
361             # the final pie slice will end at 360 degrees.
362              
363             # So, get the degree offset for this dataset
364             $end_degrees = $start_degrees + ( $data->[1][$j] / ( $dataset_sum || 1 ) * 360 );
365              
366             $degrees = ( $start_degrees + $end_degrees ) / 2;
367              
368             # stick the label in the middle of the slice
369             $label_degrees = $degrees;
370              
371             if ( $start_degrees != $end_degrees )
372             {
373              
374             # draw filled Arc
375             $self->{'gd_obj'}->filledArc( $centerX, $centerY, $diameter, $diameter, $start_degrees, $end_degrees, $color );
376             }
377              
378             # Figure out where to place the label
379             # $forbidden_degrees = angle of the center, representing the height of the label
380             if ( $j == 0 )
381             {
382             $forbidden_degrees = $rd2dg * atan2( $fontH, 0.5 * $text_diameter );
383             $label_old_degrees = 0;
384             }
385             else
386             {
387             my $winkel;
388             my $h;
389              
390             if ( ( $label_old_degrees <= 90.0 && $label_degrees > 90.0 )
391             || ( $label_old_degrees <= 270.0 && $label_degrees > 270.0 ) )
392             {
393              
394             # at 90 degrees there the reference point to the text changes
395             # from the beginning to the back
396             $forbidden_degrees = 0;
397             }
398             $label_degrees = $self->maximum( $label_degrees, $forbidden_degrees );
399             $label_old_degrees = $label_degrees; # remember old label_degrees
400              
401             $winkel = cos( $label_degrees * $dg2rd );
402              
403             $winkel = abs($winkel);
404             if ( abs($winkel) < 0.01 )
405             {
406             $h = 0;
407             }
408             else
409             {
410             $h = $fontH / $winkel;
411             }
412              
413             my $atan = atan2( $h, 0.5 * $text_diameter ); # -pi ... +pi
414              
415             $forbidden_degrees = $label_degrees + $rd2dg * $atan;
416              
417             # for debugging
418             #printf("Index=%2d winkel=%6.2f, H=%3d atan=%5.2f label=%6.2f forbidden=%6.2f\n",
419             # $j, $winkel*$dg2rd,$h, $atan*$rd2dg, $label_degrees,$forbidden_degrees);
420             # end for debugging
421              
422             }
423             $labelX = $centerX + $text_diameter * 0.5 * cos( $label_degrees * $dg2rd );
424             $labelY = $centerY + $text_diameter * 0.5 * sin( $label_degrees * $dg2rd );
425              
426             # # For debugging
427             # # Draw Point
428             # # reset the brush for points
429             # my $brush = $self->_prepare_brush($color, 'point',
430             # $self->{'pointStyle' . '0'});
431             # $self->{'gd_obj'}->setBrush($brush);
432             #
433             # # draw the point
434             # $self->{'gd_obj'}->line($labelX, $labelY, $labelX, $labelY, gdBrushed);
435             # # end for debugging
436              
437             # Okay, if a bunch of very small datasets are close together, they can
438             # overwrite each other. The following if statement is to help keep
439             # labels of neighbor datasets from being overlapped. It ain't perfect,
440             # but it does a pretty good job.
441              
442             if ( ( $label_degrees >= 270 && $label_degrees <= 360 )
443             || ( $label_degrees >= 0 && $label_degrees <= 90 ) )
444             {
445              
446             # right side of the circle
447             # as 0 degrees means east
448             # $textcolor marks everything black
449             $self->{'gd_obj'}->string( $font, $labelX, $labelY - $fontH * 0.5, $label, $textcolor );
450              
451             }
452             else
453             {
454              
455             # $textcolor marks everything black
456             $self->{'gd_obj'}->string( $font, $labelX - length($label) * $fontW, $labelY - $fontH * 0.5, $label, $textcolor );
457             }
458              
459             if ( $self->true( $self->{'legend_lines'} ) )
460             {
461             $self->{'gd_obj'}->line(
462             $centerX + 0.5 * $diameter * cos( $degrees * $dg2rd ),
463             $centerY + 0.5 * $diameter * sin( $degrees * $dg2rd ),
464             $labelX, $labelY, $color
465             );
466             }
467              
468             # reset starting point for next dataset and continue.
469             $start_degrees = $end_degrees;
470              
471             } # end for $j
472              
473             # print "Center $centerX, $centerY\n";
474             # print "Durchmesser $diameter\n";
475             # print "Hintergrund $background\n";
476              
477             if ( defined( $self->{'ring'} ) && abs( $self->{'ring'} ) < 1 )
478             {
479              
480             # print "bground $bground\n";
481             my $hole = ( 1 - abs( $self->{'ring'} ) );
482             if ( $self->true( $self->{'grey_background'} ) )
483             {
484             $insidecolor = $self->_color_role_to_index('grey_background');
485             }
486             else
487             {
488             $insidecolor = $background;
489             }
490              
491             $self->{'gd_obj'}->filledArc( $centerX, $centerY, $hole * $diameter, $hole * $diameter, 0, 360, $insidecolor );
492              
493             $self->{'gd_obj'}->arc( $centerX, $centerY, $hole * $diameter, $hole * $diameter, 0, 360, $misccolor );
494              
495             }
496              
497             # and finaly box it off
498              
499             $self->{'gd_obj'}
500             ->rectangle( $self->{'curr_x_min'}, $self->{'curr_y_min'}, $self->{'curr_x_max'}, $self->{'curr_y_max'}, $misccolor );
501             return;
502              
503             }
504              
505             ## @fn private _draw_right_legend
506             # Overwrite the legend methods to get the right legend
507             sub _draw_right_legend
508             {
509             my $self = shift;
510             my $data = $self->{'dataref'};
511             my @labels = @{ $data->[0] };
512             my ( $x1, $x2, $x3, $y1, $y2, $width, $color, $misccolor, $w, $h, $brush );
513             my $font = $self->{'legend_font'};
514             my $l1 = 0;
515             my $l2 = 0;
516             my ( $i, $j, $label, $dataset_sum );
517             my $max_label_len = 1;
518              
519             # make sure we're using a real font
520             unless ( ( ref($font) ) eq 'GD::Font' )
521             {
522             croak "The font you specified isn\'t a GD Font object";
523             }
524              
525             # get the size of the font
526             ( $h, $w ) = ( $font->height, $font->width );
527              
528             # get the miscellaneous color
529             $misccolor = $self->_color_role_to_index('misc');
530              
531             #find out what the sum of all datapoits is, needed for the Labels with percent
532             $dataset_sum = 0;
533             for my $j ( 0 .. $self->{'num_datapoints'} )
534             {
535             if ( defined $data->[1][$j] )
536             {
537             $dataset_sum += $data->[1][$j];
538             }
539             }
540              
541             # find out how who wide the largest label text is
542             foreach (@labels)
543             {
544             if ( length($_) > $l1 )
545             {
546             $l1 = length($_);
547             }
548             }
549             for ( my $i = 0 ; $i < ( $self->{'num_datapoints'} ) ; $i++ )
550             {
551             if ( length( $data->[1][$i] ) > $l2 )
552             {
553             $l2 = length( $data->[1][$i] );
554             }
555             }
556              
557             if ( $self->{'legend_label_values'} =~ /^value$/i )
558             {
559             $max_label_len = $l1 + $l2 + 1;
560             }
561             elsif ( $self->{'legend_label_values'} =~ /^percent$/i )
562             {
563             $max_label_len = $l1 + 7;
564             }
565             elsif ( $self->{'legend_label_values'} =~ /^both$/i )
566             {
567             $max_label_len = $l1 + $l2 + 9;
568             }
569             else
570             {
571             $max_label_len = $l1;
572             }
573              
574             # find out how wide the largest label is
575             $width = ( 2 * $self->{'text_space'} )
576              
577             #+ ($self->{'max_legend_label'} * $w)
578             + $max_label_len * $w + $self->{'legend_example_size'} + ( 2 * $self->{'legend_space'} );
579              
580             # get some starting x-y values
581             $x1 = $self->{'curr_x_max'} - $width;
582             $x2 = $self->{'curr_x_max'};
583             $y1 = $self->{'curr_y_min'} + $self->{'graph_border'};
584             $y2 =
585             $self->{'curr_y_min'} +
586             $self->{'graph_border'} +
587             $self->{'text_space'} +
588             ( $self->{'num_datapoints'} * ( $h + $self->{'text_space'} ) ) +
589             ( 2 * $self->{'legend_space'} );
590              
591             # box the legend off
592             $self->{'gd_obj'}->rectangle( $x1, $y1, $x2, $y2, $misccolor );
593              
594             # leave that nice space inside the legend box
595             $x1 += $self->{'legend_space'};
596             $y1 += $self->{'legend_space'} + $self->{'text_space'};
597              
598             # now draw the actual legend
599             for ( 0 .. $#labels )
600             {
601              
602             # get the color
603             $color = $self->_color_role_to_index( 'dataset' . $_ );
604              
605             # find the x-y coords
606             $x2 = $x1;
607             $x3 = $x2 + $self->{'legend_example_size'};
608             $y2 = $y1 + ( $_ * ( $self->{'text_space'} + $h ) ) + $h / 2;
609              
610             # do the line first
611             $self->{'gd_obj'}->line( $x2, $y2, $x3, $y2, $color );
612              
613             # reset the brush for points
614             $brush = $self->_prepare_brush( $color, 'point', $self->{ 'pointStyle' . $_ } );
615             $self->{'gd_obj'}->setBrush($brush);
616              
617             # draw the point
618             $self->{'gd_obj'}->line( int( ( $x3 + $x2 ) / 2 ), $y2, int( ( $x3 + $x2 ) / 2 ), $y2, gdBrushed );
619              
620             # now the label
621             $x2 = $x3 + ( 2 * $self->{'text_space'} );
622             $y2 -= $h / 2;
623             if ( defined $data->[1][$_] )
624             {
625             if ( $self->{'legend_label_values'} =~ /^value$/i )
626             {
627             $self->{'gd_obj'}->string( $font, $x2, $y2, $labels[$_] . ' ' . $data->[1][$_], $color );
628             }
629             elsif ( $self->{'legend_label_values'} =~ /^percent$/i )
630             {
631             $label = sprintf( "%s %4.2f%%", $labels[$_], $data->[1][$_] / ( $dataset_sum || 1 ) * 100 );
632             $self->{'gd_obj'}->string( $font, $x2, $y2, $label, $color );
633             }
634             elsif ( $self->{'legend_label_values'} =~ /^both$/i )
635             {
636             if ( $data->[1][$_] =~ /\./ )
637             {
638             $label =
639             sprintf( "%s %4.2f%% %.2f", $labels[$_], $data->[1][$_] / ( $dataset_sum || 1 ) * 100, $data->[1][$_] );
640             }
641             else
642             {
643             $label =
644             sprintf( "%s %4.2f%% %d", $labels[$_], $data->[1][$_] / ( $dataset_sum || 1 ) * 100, $data->[1][$_] );
645             }
646             $self->{'gd_obj'}->string( $font, $x2, $y2, $label, $color );
647             }
648             else
649             {
650             $self->{'gd_obj'}->string( $font, $x2, $y2, $labels[$_], $color );
651             }
652              
653             }
654             }
655              
656             # mark off the used space
657             $self->{'curr_x_max'} -= $width;
658              
659             # and return
660             return 1;
661             }
662              
663             ## @fn private _draw_left_legend
664             # put the legend on the left of the chart
665             sub _draw_left_legend
666             {
667             my $self = shift;
668              
669             my $data = $self->{'dataref'};
670             my @labels = @{ $data->[0] };
671             my ( $x1, $x2, $x3, $y1, $y2, $width, $color, $misccolor, $w, $h, $brush );
672             my $font = $self->{'legend_font'};
673             my $max_label_len = 1;
674             my $l1 = 0;
675             my $l2 = 0;
676             my ( $dataset_sum, $label );
677              
678             # make sure we're using a real font
679             unless ( ( ref($font) ) eq 'GD::Font' )
680             {
681             croak "The font you specified isn\'t a GD Font object";
682             }
683              
684             # get the size of the font
685             ( $h, $w ) = ( $font->height, $font->width );
686              
687             # get the miscellaneous color
688             $misccolor = $self->_color_role_to_index('misc');
689              
690             #find out what the sum of all datapoits is, needed for the Labels with percent
691             $dataset_sum = 0;
692             for my $j ( 0 .. $self->{'num_datapoints'} )
693             {
694             if ( defined $data->[1][$j] )
695             {
696             $dataset_sum += $data->[1][$j];
697             }
698             }
699              
700             # find out how who wide the largest label text is
701             foreach (@labels)
702             {
703             if ( length($_) > $l1 )
704             {
705             $l1 = length($_);
706             }
707             }
708             for ( my $i = 0 ; $i < ( $self->{'num_datapoints'} ) ; $i++ )
709             {
710             if ( length( $data->[1][$i] ) > $l2 )
711             {
712             $l2 = length( $data->[1][$i] );
713             }
714             }
715              
716             if ( $self->{'legend_label_values'} =~ /^value$/i )
717             {
718             $max_label_len = $l1 + $l2 + 1;
719             }
720             elsif ( $self->{'legend_label_values'} =~ /^percent$/i )
721             {
722             $max_label_len = $l1 + 7;
723             }
724             elsif ( $self->{'legend_label_values'} =~ /^both$/i )
725             {
726             $max_label_len = $l1 + $l2 + 9;
727             }
728             else
729             {
730             $max_label_len = $l1;
731             }
732              
733             # find out how wide the largest label is
734             $width =
735             ( 2 * $self->{'text_space'} ) +
736             ( $max_label_len * $w ) +
737             $self->{'legend_example_size'} +
738             ( 2 * $self->{'legend_space'} );
739              
740             # get some base x-y coordinates
741             $x1 = $self->{'curr_x_min'};
742             $x2 = $self->{'curr_x_min'} + $width;
743             $y1 = $self->{'curr_y_min'} + $self->{'graph_border'};
744             $y2 =
745             $self->{'curr_y_min'} +
746             $self->{'graph_border'} +
747             $self->{'text_space'} +
748             ( $self->{'num_datapoints'} * ( $h + $self->{'text_space'} ) ) +
749             ( 2 * $self->{'legend_space'} );
750              
751             # box the legend off
752             $self->{'gd_obj'}->rectangle( $x1, $y1, $x2, $y2, $misccolor );
753              
754             # leave that nice space inside the legend box
755             $x1 += $self->{'legend_space'};
756             $y1 += $self->{'legend_space'} + $self->{'text_space'};
757              
758             # now draw the actual legend
759             for ( 0 .. $#labels )
760             {
761              
762             # get the color
763             $color = $self->_color_role_to_index( 'dataset' . $_ );
764              
765             # find the x-y coords
766             $x2 = $x1;
767             $x3 = $x2 + $self->{'legend_example_size'};
768             $y2 = $y1 + ( $_ * ( $self->{'text_space'} + $h ) ) + $h / 2;
769              
770             # do the line first
771             $self->{'gd_obj'}->line( $x2, $y2, $x3, $y2, $color );
772              
773             # reset the brush for points
774             $brush = $self->_prepare_brush( $color, 'point', $self->{ 'pointStyle' . $_ } );
775             $self->{'gd_obj'}->setBrush($brush);
776              
777             # draw the point
778             $self->{'gd_obj'}->line( int( ( $x3 + $x2 ) / 2 ), $y2, int( ( $x3 + $x2 ) / 2 ), $y2, gdBrushed );
779              
780             # now the label
781             $x2 = $x3 + ( 2 * $self->{'text_space'} );
782             $y2 -= $h / 2;
783             if ( $self->{'legend_label_values'} =~ /^value$/i )
784             {
785             $self->{'gd_obj'}->string( $font, $x2, $y2, $labels[$_] . ' ' . $data->[1][$_], $color );
786             }
787             elsif ( $self->{'legend_label_values'} =~ /^percent$/i )
788             {
789             $label = sprintf( "%s %4.2f%%", $labels[$_], $data->[1][$_] / ( $dataset_sum || 1 ) * 100 );
790             $self->{'gd_obj'}->string( $font, $x2, $y2, $label, $color );
791             }
792             elsif ( $self->{'legend_label_values'} =~ /^both$/i )
793             {
794             if ( $data->[1][$_] =~ /\./ )
795             {
796             $label =
797             sprintf( "%s %4.2f%% %.2f", $labels[$_], $data->[1][$_] / ( $dataset_sum || 1 ) * 100, $data->[1][$_] );
798             }
799             else
800             {
801             $label = sprintf( "%s %4.2f%% %d", $labels[$_], $data->[1][$_] / ( $dataset_sum || 1 ) * 100, $data->[1][$_] );
802             }
803             $self->{'gd_obj'}->string( $font, $x2, $y2, $label, $color );
804             }
805             else
806             {
807             $self->{'gd_obj'}->string( $font, $x2, $y2, $labels[$_], $color );
808             }
809              
810             }
811              
812             # mark off the used space
813             $self->{'curr_x_min'} += $width;
814              
815             # and return
816             return 1;
817             }
818              
819             ## @fn private _draw_bottom_legend
820             # put the legend on the bottom of the chart
821             sub _draw_bottom_legend
822             {
823             my $self = shift;
824             my $data = $self->{'dataref'};
825             my @labels = @{ $data->[0] };
826             my ( $x1, $y1, $x2, $x3, $y2, $empty_width, $max_label_width, $cols, $rows, $color, $brush );
827             my ( $col_width, $row_height, $r, $c, $index, $x, $y, $w, $h );
828             my $font = $self->{'legend_font'};
829             my $max_label_len;
830             my $l1 = 0;
831             my $l2 = 0;
832             my ( $dataset_sum, $j );
833             my $label;
834              
835             # make sure we're using a real font
836             unless ( ( ref($font) ) eq 'GD::Font' )
837             {
838             croak "The font you specified isn\'t a GD Font object";
839             }
840              
841             # get the size of the font
842             ( $h, $w ) = ( $font->height, $font->width );
843              
844             # find the base x values
845             $x1 = $self->{'curr_x_min'} + $self->{'graph_border'};
846              
847             # + ($self->{'y_tick_label_length'} * $self->{'tick_label_font'}->width)
848             # + $self->{'tick_len'} + (3 * $self->{'text_space'});
849             $x2 = $self->{'curr_x_max'} - $self->{'graph_border'};
850             if ( $self->{'y_label'} )
851             {
852             $x1 += $self->{'label_font'}->height + 2 * $self->{'text_space'};
853             }
854             if ( $self->{'y_label2'} )
855             {
856             $x2 -= $self->{'label_font'}->height + 2 * $self->{'text_space'};
857             }
858              
859             #find out what the sum of all datapoits is, needed for the Labels with percent
860             $dataset_sum = 0;
861             for $j ( 0 .. $self->{'num_datapoints'} )
862             {
863             if ( defined $data->[1][$j] )
864             {
865             $dataset_sum += $data->[1][$j];
866             }
867             }
868              
869             # find out how who wide the largest label text is, especially look what kind of
870             # label is needed
871             foreach (@labels)
872             {
873             if ( length($_) > $l1 )
874             {
875             $l1 = length($_);
876             }
877             }
878             for ( my $i = 0 ; $i < ( $self->{'num_datapoints'} ) ; $i++ )
879             {
880             if ( length( $data->[1][$i] ) > $l2 )
881             {
882             $l2 = length( $data->[1][$i] );
883             }
884             }
885              
886             if ( $self->{'legend_label_values'} =~ /^value$/i )
887             {
888             $max_label_len = $l1 + $l2 + 1;
889             }
890             elsif ( $self->{'legend_label_values'} =~ /^percent$/i )
891             {
892             $max_label_len = $l1 + 7;
893             }
894             elsif ( $self->{'legend_label_values'} =~ /^both$/i )
895             {
896             $max_label_len = $l1 + $l2 + 9;
897             }
898             else
899             {
900             $max_label_len = $l1;
901             }
902              
903             # figure out how wide the columns need to be, and how many we
904             # can fit in the space available
905             $empty_width = ( $x2 - $x1 ) - ( 2 * $self->{'legend_space'} );
906             $max_label_width = $max_label_len * $w
907              
908             #$self->{'max_legend_label'} * $w
909             + ( 4 * $self->{'text_space'} ) + $self->{'legend_example_size'};
910             $cols = int( $empty_width / $max_label_width );
911             unless ($cols)
912             {
913             $cols = 1;
914             }
915             $col_width = $empty_width / $cols;
916              
917             # figure out how many rows we need, remember how tall they are
918             $rows = int( $self->{'num_datapoints'} / $cols );
919             unless ( ( $self->{'num_datapoints'} % $cols ) == 0 )
920             {
921             $rows++;
922             }
923             unless ($rows)
924             {
925             $rows = 1;
926             }
927             $row_height = $h + $self->{'text_space'};
928              
929             # box the legend off
930             $y1 = $self->{'curr_y_max'} - $self->{'text_space'} - ( $rows * $row_height ) - ( 2 * $self->{'legend_space'} );
931             $y2 = $self->{'curr_y_max'};
932             $self->{'gd_obj'}->rectangle( $x1, $y1, $x2, $y2, $self->_color_role_to_index('misc') );
933             $x1 += $self->{'legend_space'} + $self->{'text_space'};
934             $x2 -= $self->{'legend_space'};
935             $y1 += $self->{'legend_space'} + $self->{'text_space'};
936             $y2 -= $self->{'legend_space'} + $self->{'text_space'};
937              
938             # draw in the actual legend
939             for $r ( 0 .. $rows - 1 )
940             {
941             for $c ( 0 .. $cols - 1 )
942             {
943             $index = ( $r * $cols ) + $c; # find the index in the label array
944             if ( $labels[$index] )
945             {
946              
947             # get the color
948             $color = $self->_color_role_to_index( 'dataset' . $index );
949              
950             # get the x-y coordinate for the start of the example line
951             $x = $x1 + ( $col_width * $c );
952             $y = $y1 + ( $row_height * $r ) + $h / 2;
953              
954             # now draw the example line
955             $self->{'gd_obj'}->line( $x, $y, $x + $self->{'legend_example_size'}, $y, $color );
956              
957             # reset the brush for points
958             $brush = $self->_prepare_brush( $color, 'point', $self->{ 'pointStyle' . $index } );
959             $self->{'gd_obj'}->setBrush($brush);
960              
961             # draw the point
962             $x3 = int( $x + $self->{'legend_example_size'} / 2 );
963             $self->{'gd_obj'}->line( $x3, $y, $x3, $y, gdBrushed );
964              
965             # adjust the x-y coordinates for the start of the label
966             $x += $self->{'legend_example_size'} + ( 2 * $self->{'text_space'} );
967             $y = $y1 + ( $row_height * $r );
968              
969             # now draw the label
970             if ( $self->{'legend_label_values'} =~ /^value$/i )
971             {
972             $self->{'gd_obj'}->string( $font, $x, $y, $labels[$index] . ' ' . $data->[1][$index], $color );
973              
974             #$self->{'gd_obj'}->stringTTF($color, FONT, 10, 0, $x, $y+10, $labels[$index].' '.$data->[1][$index]); ############
975             }
976             elsif ( $self->{'legend_label_values'} =~ /^percent$/i )
977             {
978             $label = sprintf( "%s %4.2f%%", $labels[$index], $data->[1][$index] / ( $dataset_sum || 1 ) * 100 );
979             $self->{'gd_obj'}->string( $font, $x, $y, $label, $color );
980             }
981             elsif ( $self->{'legend_label_values'} =~ /^both$/i )
982             {
983             if ( $data->[1][$index] =~ /\./ )
984             {
985             $label = sprintf( "%s %4.2f%% %.2f",
986             $labels[$index],
987             $data->[1][$index] / ( $dataset_sum || 1 ) * 100,
988             $data->[1][$index] );
989             }
990             else
991             {
992             $label = sprintf( "%s %4.2f%% %d",
993             $labels[$index],
994             $data->[1][$index] / ( $dataset_sum || 1 ) * 100,
995             $data->[1][$index] );
996             }
997             $self->{'gd_obj'}->string( $font, $x, $y, $label, $color ); ###
998             # $self->{'gd_obj'}->stringTTF($color, FONT, 10, 0, $x, $y, $label);
999              
1000             }
1001             else
1002             {
1003             $self->{'gd_obj'}->string( $font, $x, $y, $labels[$index], $color );
1004             }
1005             }
1006             }
1007             }
1008              
1009             # mark off the space used
1010             $self->{'curr_y_max'} -= ( $rows * $row_height ) + $self->{'text_space'} + ( 2 * $self->{'legend_space'} );
1011              
1012             # now return
1013             return 1;
1014             }
1015              
1016             ## @fn private _draw_top_legend
1017             # put the legend on top of the chart
1018             sub _draw_top_legend
1019             {
1020             my $self = shift;
1021             my $data = $self->{'dataref'};
1022             my ($max_label_len);
1023             my $l1 = 0;
1024             my $l2 = 0;
1025             my @labels = @{ $data->[0] };
1026             my ( $x1, $y1, $x2, $x3, $y2, $empty_width, $max_label_width, $cols, $rows, $color, $brush );
1027             my ( $col_width, $row_height, $r, $c, $index, $x, $y, $w, $h, $dataset_sum, $label );
1028             my $font = $self->{'legend_font'};
1029              
1030             # make sure we're using a real font
1031             unless ( ( ref($font) ) eq 'GD::Font' )
1032             {
1033             croak "The font you specified isn\'t a GD Font object";
1034             }
1035              
1036             # get the size of the font
1037             ( $h, $w ) = ( $font->height, $font->width );
1038              
1039             #find out what the sum of all datapoits is, needed for the Labels with percent
1040             $dataset_sum = 0;
1041             for my $j ( 0 .. $self->{'num_datapoints'} )
1042             {
1043             if ( defined $data->[1][$j] )
1044             {
1045             $dataset_sum += $data->[1][$j];
1046             }
1047             }
1048              
1049             # get some base x coordinates
1050             $x1 = $self->{'curr_x_min'} + $self->{'graph_border'};
1051              
1052             # + $self->{'y_tick_label_length'} * $self->{'tick_label_font'}->width
1053             # + $self->{'tick_len'} + (3 * $self->{'text_space'});
1054             $x2 = $self->{'curr_x_max'} - $self->{'graph_border'};
1055             if ( $self->{'y_label'} )
1056             {
1057             $x1 += $self->{'label_font'}->height + 2 * $self->{'text_space'};
1058             }
1059             if ( $self->{'y_label2'} )
1060             {
1061             $x2 -= $self->{'label_font'}->height + 2 * $self->{'text_space'};
1062             }
1063              
1064             # find out how who wide the largest label text is
1065             foreach (@labels)
1066             {
1067             if ( length($_) > $l1 )
1068             {
1069             $l1 = length($_);
1070             }
1071             }
1072             for ( my $i = 0 ; $i < ( $self->{'num_datapoints'} ) ; $i++ )
1073             {
1074             if ( length( $data->[1][$i] ) > $l2 )
1075             {
1076             $l2 = length( $data->[1][$i] );
1077             }
1078             }
1079              
1080             if ( $self->{'legend_label_values'} =~ /^value$/i )
1081             {
1082             $max_label_len = $l1 + $l2 + 1;
1083             }
1084             elsif ( $self->{'legend_label_values'} =~ /^percent$/i )
1085             {
1086             $max_label_len = $l1 + 7;
1087             }
1088             elsif ( $self->{'legend_label_values'} =~ /^both$/i )
1089             {
1090             $max_label_len = $l1 + $l2 + 9;
1091             }
1092             else
1093             {
1094             $max_label_len = $l1;
1095             }
1096              
1097             # figure out how wide the columns can be, and how many will fit
1098             $empty_width = ( $x2 - $x1 ) - ( 2 * $self->{'legend_space'} );
1099             $max_label_width = ( 4 * $self->{'text_space'} ) + $max_label_len * $w + $self->{'legend_example_size'};
1100             $cols = int( $empty_width / $max_label_width );
1101              
1102             unless ($cols)
1103             {
1104             $cols = 1;
1105             }
1106             $col_width = $empty_width / $cols;
1107              
1108             # figure out how many rows we need and remember how tall they are
1109             $rows = int( $self->{'num_datapoints'} / $cols );
1110             unless ( ( $self->{'num_datapoints'} % $cols ) == 0 )
1111             {
1112             $rows++;
1113             }
1114             unless ($rows)
1115             {
1116             $rows = 1;
1117             }
1118             $row_height = $h + $self->{'text_space'};
1119              
1120             # box the legend off
1121             $y1 = $self->{'curr_y_min'};
1122             $y2 = $self->{'curr_y_min'} + $self->{'text_space'} + ( $rows * $row_height ) + ( 2 * $self->{'legend_space'} );
1123             $self->{'gd_obj'}->rectangle( $x1, $y1, $x2, $y2, $self->_color_role_to_index('misc') );
1124              
1125             # leave some space inside the legend
1126             $x1 += $self->{'legend_space'} + $self->{'text_space'};
1127             $x2 -= $self->{'legend_space'};
1128             $y1 += $self->{'legend_space'} + $self->{'text_space'};
1129             $y2 -= $self->{'legend_space'} + $self->{'text_space'};
1130              
1131             # draw in the actual legend
1132             for $r ( 0 .. $rows - 1 )
1133             {
1134             for $c ( 0 .. $cols - 1 )
1135             {
1136             $index = ( $r * $cols ) + $c; # find the index in the label array
1137             if ( $labels[$index] )
1138             {
1139              
1140             # get the color
1141             $color = $self->_color_role_to_index( 'dataset' . $index );
1142              
1143             # find the x-y coords
1144             $x = $x1 + ( $col_width * $c );
1145             $y = $y1 + ( $row_height * $r ) + $h / 2;
1146              
1147             # draw the line first
1148             $self->{'gd_obj'}->line( $x, $y, $x + $self->{'legend_example_size'}, $y, $color );
1149              
1150             # reset the brush for points
1151             $brush = $self->_prepare_brush( $color, 'point', $self->{ 'pointStyle' . $index } );
1152             $self->{'gd_obj'}->setBrush($brush);
1153              
1154             # draw the point
1155             $x3 = int( $x + $self->{'legend_example_size'} / 2 );
1156             $self->{'gd_obj'}->line( $x3, $y, $x3, $y, gdBrushed );
1157              
1158             # now the label
1159             $x += $self->{'legend_example_size'} + ( 2 * $self->{'text_space'} );
1160             $y -= $h / 2;
1161             if ( $self->{'legend_label_values'} =~ /^value$/i )
1162             {
1163             $self->{'gd_obj'}->string( $font, $x, $y, $labels[$index] . ' ' . $data->[1][$index], $color );
1164             }
1165             elsif ( $self->{'legend_label_values'} =~ /^percent$/i )
1166             {
1167             $label = sprintf( "%s %4.2f%%", $labels[$index], $data->[1][$index] / ( $dataset_sum || 1 ) * 100 );
1168             $self->{'gd_obj'}->string( $font, $x, $y, $label, $color );
1169             }
1170             elsif ( $self->{'legend_label_values'} =~ /^both$/i )
1171             {
1172             if ( $data->[1][$index] =~ /\./ )
1173             {
1174             $label = sprintf( "%s %4.2f%% %.2f",
1175             $labels[$index],
1176             $data->[1][$index] / ( $dataset_sum || 1 ) * 100,
1177             $data->[1][$index] );
1178             }
1179             else
1180             {
1181             $label = sprintf( "%s %4.2f%% %d",
1182             $labels[$index],
1183             $data->[1][$index] / ( $dataset_sum || 1 ) * 100,
1184             $data->[1][$index] );
1185             }
1186             $self->{'gd_obj'}->string( $font, $x, $y, $label, $color );
1187             }
1188             else
1189             {
1190             $self->{'gd_obj'}->string( $font, $x, $y, $labels[$index], $color );
1191             }
1192             }
1193             }
1194             }
1195              
1196             # mark off the space used
1197             $self->{'curr_y_min'} += ( $rows * $row_height ) + $self->{'text_space'} + 2 * $self->{'legend_space'};
1198              
1199             # now return
1200             return 1;
1201             }
1202              
1203             ## @fn private _draw_x_ticks
1204             # Override the ticks methods for the pie charts.\n
1205             # Here: do nothing
1206             sub _draw_x_ticks
1207             {
1208             my $self = shift;
1209              
1210             return;
1211             }
1212              
1213             ## @fn private _draw_y_ticks
1214             # Override the ticks methods for the pie charts.\n
1215             sub _draw_y_ticks
1216             {
1217             my $self = shift;
1218              
1219             return;
1220             }
1221              
1222             ## @fn private _find_y_scale
1223             # Override the find_y_scale methods for the pie charts.\n
1224             # Here: do nothing
1225             sub _find_y_scale
1226             {
1227             my $self = shift;
1228              
1229             return;
1230             }
1231              
1232             ## be a good module and return 1
1233             1;