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 2015-03-01
7             # @version 2.4.10
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   13706 use Chart::Base '2.4.10';
  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.10';
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 = 0;
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] && $data->[1][$j] > 0 )
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] ) && $data->[1][$j] > 0 )
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             #print "Vor Modulo: label_degrees = $label_degrees\n";
196             $label_degrees = $self->modulo( $label_degrees, 360.0 );
197              
198             #print "Nach Modulo: label_degrees = $label_degrees\n";
199              
200             $label = $labelinfo{$j}{labelstring};
201             $label_length = $labelinfo{$j}{labellength} || 0;
202              
203             #!!DEBUG!!!!
204             #print "Text=".$label.", StartDegrees=".$start_degrees.
205             # ", end=".$end_degrees.
206             # ", label_degree=".$label_degrees."\n";
207              
208             # 0 degrees means east
209             # 90 degrees means south
210             # 180 degrees means west
211             # 270 degrees means north
212             # 360 degrees means east again
213             if ( ( $label_degrees >= 270 && $label_degrees <= 360 )
214             || ( $label_degrees >= 0 && $label_degrees <= 90 ) )
215             {
216              
217             # right side of the circle
218             {
219              
220             # $x value in respect of label arc
221             my $x = $radius * sin( $dg2rd * $label_degrees );
222             my $y = $radius * cos( $dg2rd * $label_degrees );
223              
224             #!!DEBUG!!!!
225             #print "Text=".$label.": x=$x, y=$y\n";
226              
227             # i.e. the startpoint is at $centerX+$x, $centerY-$y
228             #test
229             #$self->{'gd_obj'}->rectangle( $centerX, $centerY, $centerX+$x, $centerY-$y, $misccolor );
230              
231             # theoretical right value in respect to radius and length of label
232             my $right_pos = $centerX + $x + $label_length * $fontW;
233             if ( $right_pos > $self->{'curr_x_max'} )
234             {
235              
236             # too far right, correct x
237             $right_pos = $self->{'curr_x_max'};
238             $x = $right_pos - $centerX - $label_length * $fontW;
239              
240             #!!DEBUG!!!!
241             #print "too far right: Text=".$label.": x=$x, y=$y\n";
242             }
243              
244             # theoretical top position in respect to radius and height of label
245             # (Remark: direction to the top of the picture counts backwards!)
246             my $top_pos = $centerY - $y - $fontH;
247             if ( $top_pos < $self->{'curr_y_min'} )
248             {
249              
250             # too far up, correct $y
251             $top_pos = $self->{'curr_y_min'};
252             $y = $centerY - $top_pos - $fontH;
253              
254             #!!DEBUG!!!!
255             #print "too far up: Text=".$label.": x=$x, y=$y\n";
256             }
257              
258             my $down_pos = $centerY + $y + $fontH;
259             if ( $down_pos > $self->{'curr_y_max'} )
260             {
261             $down_pos = $self->{'curr_y_max'};
262             $y = $down_pos - $centerY - $fontH;
263              
264             #!!DEBUG!!!!
265             #print "too far down: Text=".$label.": x=$x, y=$y\n";
266             }
267              
268             #test
269             #$self->{'gd_obj'}->rectangle( $centerX+$x, $centerY-$y, $right_pos, $top_pos, $textcolor );
270             #$self->{'gd_obj'}
271             # ->rectangle( $self->{'curr_x_min'}, $self->{'curr_y_min'}, $self->{'curr_x_max'}, $self->{'curr_y_max'}, $misccolor );
272             #return;
273              
274             #!!DEBUG!!!!
275             #print "before Line 270: label_degrees=$label_degrees, cos()=". cos( $dg2rd * $label_degrees ). ", sin()=". sin( $dg2rd * $label_degrees )."\n";
276             if ( $label_degrees == 0 )
277             {
278             $radius = $self->minimum( $radius, abs( $y / cos( $dg2rd * $label_degrees ) ) );
279             }
280             else
281             {
282             $radius =
283             $self->minimum( $radius, $x / sin( $dg2rd * $label_degrees ),
284             abs( $y / cos( $dg2rd * $label_degrees ) ) );
285             }
286             $radius = int( $radius + 0.5 );
287              
288             #!!DEBUG!!
289             #$self->{'gd_obj'}->line( $centerX, $centerY, $centerX+$radius, $centerY, gdBrushed );
290              
291             }
292             if ( $radius <= 0 ) { croak "radius < 0!"; }
293             }
294             else
295             { # left side of the circle
296             # as 0 degrees means east
297              
298             if ( abs($label_degrees) < 0.1 )
299             {
300              
301             # too small angle
302             $radius = $self->{'curr_x_max'} - $label_length * $fontW;
303             }
304             else
305             {
306              
307             # $x value in respect of label arc
308             my $x = $radius * sin( $dg2rd * $label_degrees );
309             my $y = $radius * cos( $dg2rd * $label_degrees );
310              
311             # i.e. the startpoint is at $centerX-$x, $centerY+$y
312             #test
313             #$self->{'gd_obj'}->rectangle( $centerX, $centerY, $centerX-$x, $centerY+$y, $misccolor );
314              
315             # theoretical right value in respect to radius and length of label
316             my $left_pos = $centerX - $x - $label_length * $fontW;
317             if ( $left_pos < $self->{'curr_x_min'} )
318             {
319              
320             # too far right, correct x
321             $left_pos = $self->{'curr_x_min'};
322             $x = $centerX - $left_pos - $label_length * $fontW;
323             }
324              
325             # theoretical top position in respect to radius and height of label
326             # (Remark: direction to the top of the picture counts backwards!)
327             my $top_pos = $centerY + $y - $fontH;
328             if ( $top_pos < $self->{'curr_y_min'} )
329             {
330              
331             # too far up, correct $y
332             $top_pos = $self->{'curr_y_min'};
333             $y = $centerY - $top_pos - $fontH;
334             }
335              
336             my $down_pos = $centerY - $y + $fontH;
337             if ( $down_pos > $self->{'curr_y_max'} )
338             {
339             $down_pos = $self->{'curr_y_max'};
340             $y = $centerY + $fontH - $down_pos;
341             }
342              
343             #test
344             #$self->{'gd_obj'}->rectangle( $centerX-$x, $centerY+$y, $left_pos, $top_pos, $textcolor );
345             #$self->{'gd_obj'}
346             # ->rectangle( $self->{'curr_x_min'}, $self->{'curr_y_min'}, $self->{'curr_x_max'}, $self->{'curr_y_max'}, $misccolor );
347             #return;
348              
349             $radius =
350             $self->minimum( $radius, $x / sin( $dg2rd * $label_degrees ), abs( $y / cos( $dg2rd * $label_degrees ) ) );
351             $radius = int( $radius + 0.5 );
352              
353             #test
354             #$self->{'gd_obj'}->line( $centerX, $centerY, $centerX+$radius, $centerY, gdBrushed );
355              
356             }
357             if ( $radius <= 0 ) { croak "radius < 0!"; }
358             }
359              
360             # reset starting point for next dataset and continue.
361             $start_degrees = $end_degrees;
362              
363             }
364             $diameter = $radius * 2 - 2 * $labeldistance;
365              
366             $text_diameter = $diameter + $labeldistance;
367             $self->{'gd_obj'}->arc( $centerX, $centerY, $diameter, $diameter, 0, 360, $misccolor );
368              
369             # for DEBUG!!
370             #$self->{'gd_obj'}->arc($centerX, $centerY, $text_diameter, $text_diameter,
371             # 0, 360, $misccolor);
372              
373             # for DEBUG!!
374             #print "-------------------------------------------\n";
375              
376             # @details
377             # Plot the pies
378             $start_degrees = 0;
379             $end_degrees = 0;
380             for $j ( 0 .. ( $self->{'num_datapoints'} - 1 ) )
381             {
382              
383             next if $labelinfo{$j}{data} eq 'undefined';
384              
385             # get the color for this datapoint, take the color of the datasets
386             $color = $self->_color_role_to_index( 'dataset' . $j );
387              
388             $label = $labelinfo{$j}{labelstring};
389             $label_length = $labelinfo{$j}{labellength};
390              
391             # The first value starts at 0 degrees, each additional dataset
392             # stops where the previous left off, and since I've already
393             # calculated the sum_total for the whole graph, I know that
394             # the final pie slice will end at 360 degrees.
395              
396             # So, get the degree offset for this dataset
397             $end_degrees = $start_degrees + ( $data->[1][$j] / ( $dataset_sum || 1 ) * 360 );
398              
399             $degrees = ( $start_degrees + $end_degrees ) / 2;
400              
401             # stick the label in the middle of the slice
402             $label_degrees = $degrees;
403             $label_degrees = $self->modulo( $label_degrees, 360.0 );
404              
405             if ( $start_degrees < $end_degrees )
406             {
407              
408             # draw filled Arc
409             # test
410             #print "centerX=$centerX, centerY=$centerY, diameter=$diameter, $start_degrees, ".int($start_degrees) . ", $end_degrees\n";
411             #$self->{'gd_obj'}->filledArc( $centerX, $centerY, $diameter, $diameter, $start_degrees, $end_degrees, $color );
412             $self->{'gd_obj'}->filledArc(
413             $centerX, $centerY, $diameter, $diameter,
414             int( $start_degrees - 0.5 ),
415             int( $end_degrees + 0.5 ), $color
416             );
417             }
418              
419             # Figure out where to place the label
420             # $forbidden_degrees = angle of the center, representing the height of the label
421             if ( $j == 0 )
422             {
423             $forbidden_degrees = $rd2dg * atan2( $fontH, 0.5 * $text_diameter );
424             $label_old_degrees = 0;
425             }
426             else
427             {
428             my $winkel;
429             my $h;
430              
431             if ( ( $label_old_degrees <= 90.0 && $label_degrees > 90.0 )
432             || ( $label_old_degrees <= 270.0 && $label_degrees > 270.0 ) )
433             {
434              
435             # at 90 degrees there the reference point to the text changes
436             # from the beginning to the back
437             $forbidden_degrees = 0;
438             }
439             $label_degrees = $self->maximum( $label_degrees, $forbidden_degrees );
440             $label_old_degrees = $label_degrees; # remember old label_degrees
441              
442             $winkel = cos( $label_degrees * $dg2rd );
443              
444             $winkel = abs($winkel);
445             if ( abs($winkel) < 0.01 )
446             {
447             $h = 0;
448             }
449             else
450             {
451             $h = $fontH / $winkel;
452             }
453              
454             my $atan = atan2( $h, 0.5 * $text_diameter ); # -pi ... +pi
455              
456             $forbidden_degrees = $label_degrees + $rd2dg * $atan;
457              
458             # for debugging
459             #printf("Index=%2d winkel=%6.2f, H=%3d atan=%5.2f label=%6.2f forbidden=%6.2f\n",
460             # $j, $winkel*$dg2rd,$h, $atan*$rd2dg, $label_degrees,$forbidden_degrees);
461             # end for debugging
462              
463             }
464             $labelX = $centerX + $text_diameter * 0.5 * cos( $label_degrees * $dg2rd );
465             $labelY = $centerY + $text_diameter * 0.5 * sin( $label_degrees * $dg2rd );
466              
467             #!!DEBUG!!!!
468             #print "Text=".$label.": labelX=$labelX, y=$labelY\n";
469             # # For debugging
470             # # Draw Point
471             # # reset the brush for points
472             # my $brush = $self->_prepare_brush($color, 'point',
473             # $self->{'pointStyle' . '0'});
474             # $self->{'gd_obj'}->setBrush($brush);
475             #
476             # # draw the point
477             # $self->{'gd_obj'}->line($labelX, $labelY, $labelX, $labelY, gdBrushed);
478             # # end for debugging
479              
480             # Okay, if a bunch of very small datasets are close together, they can
481             # overwrite each other. The following if statement is to help keep
482             # labels of neighbor datasets from being overlapped. It ain't perfect,
483             # but it does a pretty good job.
484              
485             if ( ( $label_degrees >= 270 && $label_degrees <= 360 )
486             || ( $label_degrees >= 0 && $label_degrees <= 90 ) )
487             {
488              
489             # right side of the circle
490             # as 0 degrees means east
491             # $textcolor marks everything black
492             $self->{'gd_obj'}->string( $font, $labelX, $labelY - $fontH * 0.5, $label, $textcolor );
493              
494             }
495             else
496             {
497              
498             # $textcolor marks everything black
499             $self->{'gd_obj'}->string( $font, $labelX - length($label) * $fontW, $labelY - $fontH * 0.5, $label, $textcolor );
500             }
501              
502             if ( $self->true( $self->{'legend_lines'} ) )
503             {
504             $self->{'gd_obj'}->line(
505             $centerX + 0.5 * $diameter * cos( $degrees * $dg2rd ),
506             $centerY + 0.5 * $diameter * sin( $degrees * $dg2rd ),
507             $labelX, $labelY, $color
508             );
509             }
510              
511             # reset starting point for next dataset and continue.
512             $start_degrees = $end_degrees;
513              
514             } # end for $j
515              
516             # print "Center $centerX, $centerY\n";
517             # print "Durchmesser $diameter\n";
518             # print "Hintergrund $background\n";
519              
520             if ( defined( $self->{'ring'} ) && abs( $self->{'ring'} ) < 1 )
521             {
522              
523             # print "bground $bground\n";
524             my $hole = ( 1 - abs( $self->{'ring'} ) );
525             if ( $self->true( $self->{'grey_background'} ) )
526             {
527             $insidecolor = $self->_color_role_to_index('grey_background');
528             }
529             else
530             {
531             $insidecolor = $background;
532             }
533              
534             $self->{'gd_obj'}->filledArc( $centerX, $centerY, $hole * $diameter, $hole * $diameter, 0, 360, $insidecolor );
535              
536             $self->{'gd_obj'}->arc( $centerX, $centerY, $hole * $diameter, $hole * $diameter, 0, 360, $misccolor );
537              
538             }
539              
540             # and finaly box it off
541              
542             $self->{'gd_obj'}
543             ->rectangle( $self->{'curr_x_min'}, $self->{'curr_y_min'}, $self->{'curr_x_max'}, $self->{'curr_y_max'}, $misccolor );
544             return;
545              
546             }
547              
548             ## @fn private _draw_right_legend
549             # Overwrite the legend methods to get the right legend
550             sub _draw_right_legend
551             {
552             my $self = shift;
553             my $data = $self->{'dataref'};
554             my @labels = @{ $data->[0] };
555             my ( $x1, $x2, $x3, $y1, $y2, $width, $color, $misccolor, $w, $h, $brush );
556             my $font = $self->{'legend_font'};
557             my $l1 = 0;
558             my $l2 = 0;
559             my ( $i, $j, $label, $dataset_sum );
560             my $max_label_len = 1;
561              
562             # make sure we're using a real font
563             unless ( ( ref($font) ) eq 'GD::Font' )
564             {
565             croak "The font you specified isn\'t a GD Font object";
566             }
567              
568             # get the size of the font
569             ( $h, $w ) = ( $font->height, $font->width );
570              
571             # get the miscellaneous color
572             $misccolor = $self->_color_role_to_index('misc');
573              
574             #find out what the sum of all datapoits is, needed for the Labels with percent
575             $dataset_sum = 0;
576             for my $j ( 0 .. $self->{'num_datapoints'} )
577             {
578             if ( defined $data->[1][$j] )
579             {
580             $dataset_sum += $data->[1][$j];
581             }
582             }
583              
584             # find out how who wide the largest label text is
585             foreach (@labels)
586             {
587             if ( length($_) > $l1 )
588             {
589             $l1 = length($_);
590             }
591             }
592             for ( my $i = 0 ; $i < ( $self->{'num_datapoints'} ) ; $i++ )
593             {
594             if ( length( $data->[1][$i] ) > $l2 )
595             {
596             $l2 = length( $data->[1][$i] );
597             }
598             }
599              
600             if ( $self->{'legend_label_values'} =~ /^value$/i )
601             {
602             $max_label_len = $l1 + $l2 + 1;
603             }
604             elsif ( $self->{'legend_label_values'} =~ /^percent$/i )
605             {
606             $max_label_len = $l1 + 7;
607             }
608             elsif ( $self->{'legend_label_values'} =~ /^both$/i )
609             {
610             $max_label_len = $l1 + $l2 + 9;
611             }
612             else
613             {
614             $max_label_len = $l1;
615             }
616              
617             # find out how wide the largest label is
618             $width = ( 2 * $self->{'text_space'} )
619              
620             #+ ($self->{'max_legend_label'} * $w)
621             + $max_label_len * $w + $self->{'legend_example_size'} + ( 2 * $self->{'legend_space'} );
622              
623             # get some starting x-y values
624             $x1 = $self->{'curr_x_max'} - $width;
625             $x2 = $self->{'curr_x_max'};
626             $y1 = $self->{'curr_y_min'} + $self->{'graph_border'};
627             $y2 =
628             $self->{'curr_y_min'} +
629             $self->{'graph_border'} +
630             $self->{'text_space'} +
631             ( $self->{'num_datapoints'} * ( $h + $self->{'text_space'} ) ) +
632             ( 2 * $self->{'legend_space'} );
633              
634             # box the legend off
635             $self->{'gd_obj'}->rectangle( $x1, $y1, $x2, $y2, $misccolor );
636              
637             # leave that nice space inside the legend box
638             $x1 += $self->{'legend_space'};
639             $y1 += $self->{'legend_space'} + $self->{'text_space'};
640              
641             # now draw the actual legend
642             for ( 0 .. $#labels )
643             {
644              
645             # get the color
646             $color = $self->_color_role_to_index( 'dataset' . $_ );
647              
648             # find the x-y coords
649             $x2 = $x1;
650             $x3 = $x2 + $self->{'legend_example_size'};
651             $y2 = $y1 + ( $_ * ( $self->{'text_space'} + $h ) ) + $h / 2;
652              
653             # do the line first
654             $self->{'gd_obj'}->line( $x2, $y2, $x3, $y2, $color );
655              
656             # reset the brush for points
657             $brush = $self->_prepare_brush( $color, 'point', $self->{ 'pointStyle' . $_ } );
658             $self->{'gd_obj'}->setBrush($brush);
659              
660             # draw the point
661             $self->{'gd_obj'}->line( int( ( $x3 + $x2 ) / 2 ), $y2, int( ( $x3 + $x2 ) / 2 ), $y2, gdBrushed );
662              
663             # now the label
664             $x2 = $x3 + ( 2 * $self->{'text_space'} );
665             $y2 -= $h / 2;
666             if ( defined $data->[1][$_] )
667             {
668             if ( $self->{'legend_label_values'} =~ /^value$/i )
669             {
670             $self->{'gd_obj'}->string( $font, $x2, $y2, $labels[$_] . ' ' . $data->[1][$_], $color );
671             }
672             elsif ( $self->{'legend_label_values'} =~ /^percent$/i )
673             {
674             $label = sprintf( "%s %4.2f%%", $labels[$_], $data->[1][$_] / ( $dataset_sum || 1 ) * 100 );
675             $self->{'gd_obj'}->string( $font, $x2, $y2, $label, $color );
676             }
677             elsif ( $self->{'legend_label_values'} =~ /^both$/i )
678             {
679             if ( $data->[1][$_] =~ /\./ )
680             {
681             $label =
682             sprintf( "%s %4.2f%% %.2f", $labels[$_], $data->[1][$_] / ( $dataset_sum || 1 ) * 100, $data->[1][$_] );
683             }
684             else
685             {
686             $label =
687             sprintf( "%s %4.2f%% %d", $labels[$_], $data->[1][$_] / ( $dataset_sum || 1 ) * 100, $data->[1][$_] );
688             }
689             $self->{'gd_obj'}->string( $font, $x2, $y2, $label, $color );
690             }
691             else
692             {
693             $self->{'gd_obj'}->string( $font, $x2, $y2, $labels[$_], $color );
694             }
695              
696             }
697             }
698              
699             # mark off the used space
700             $self->{'curr_x_max'} -= $width;
701              
702             # and return
703             return 1;
704             }
705              
706             ## @fn private _draw_left_legend
707             # put the legend on the left of the chart
708             sub _draw_left_legend
709             {
710             my $self = shift;
711              
712             my $data = $self->{'dataref'};
713             my @labels = @{ $data->[0] };
714             my ( $x1, $x2, $x3, $y1, $y2, $width, $color, $misccolor, $w, $h, $brush );
715             my $font = $self->{'legend_font'};
716             my $max_label_len = 1;
717             my $l1 = 0;
718             my $l2 = 0;
719             my ( $dataset_sum, $label );
720              
721             # make sure we're using a real font
722             unless ( ( ref($font) ) eq 'GD::Font' )
723             {
724             croak "The font you specified isn\'t a GD Font object";
725             }
726              
727             # get the size of the font
728             ( $h, $w ) = ( $font->height, $font->width );
729              
730             # get the miscellaneous color
731             $misccolor = $self->_color_role_to_index('misc');
732              
733             #find out what the sum of all datapoits is, needed for the Labels with percent
734             $dataset_sum = 0;
735             for my $j ( 0 .. $self->{'num_datapoints'} )
736             {
737             if ( defined $data->[1][$j] )
738             {
739             $dataset_sum += $data->[1][$j];
740             }
741             }
742              
743             # find out how who wide the largest label text is
744             foreach (@labels)
745             {
746             if ( length($_) > $l1 )
747             {
748             $l1 = length($_);
749             }
750             }
751             for ( my $i = 0 ; $i < ( $self->{'num_datapoints'} ) ; $i++ )
752             {
753             if ( length( $data->[1][$i] ) > $l2 )
754             {
755             $l2 = length( $data->[1][$i] );
756             }
757             }
758              
759             if ( $self->{'legend_label_values'} =~ /^value$/i )
760             {
761             $max_label_len = $l1 + $l2 + 1;
762             }
763             elsif ( $self->{'legend_label_values'} =~ /^percent$/i )
764             {
765             $max_label_len = $l1 + 7;
766             }
767             elsif ( $self->{'legend_label_values'} =~ /^both$/i )
768             {
769             $max_label_len = $l1 + $l2 + 9;
770             }
771             else
772             {
773             $max_label_len = $l1;
774             }
775              
776             # find out how wide the largest label is
777             $width =
778             ( 2 * $self->{'text_space'} ) +
779             ( $max_label_len * $w ) +
780             $self->{'legend_example_size'} +
781             ( 2 * $self->{'legend_space'} );
782              
783             # get some base x-y coordinates
784             $x1 = $self->{'curr_x_min'};
785             $x2 = $self->{'curr_x_min'} + $width;
786             $y1 = $self->{'curr_y_min'} + $self->{'graph_border'};
787             $y2 =
788             $self->{'curr_y_min'} +
789             $self->{'graph_border'} +
790             $self->{'text_space'} +
791             ( $self->{'num_datapoints'} * ( $h + $self->{'text_space'} ) ) +
792             ( 2 * $self->{'legend_space'} );
793              
794             # box the legend off
795             $self->{'gd_obj'}->rectangle( $x1, $y1, $x2, $y2, $misccolor );
796              
797             # leave that nice space inside the legend box
798             $x1 += $self->{'legend_space'};
799             $y1 += $self->{'legend_space'} + $self->{'text_space'};
800              
801             # now draw the actual legend
802             for ( 0 .. $#labels )
803             {
804              
805             # get the color
806             $color = $self->_color_role_to_index( 'dataset' . $_ );
807              
808             # find the x-y coords
809             $x2 = $x1;
810             $x3 = $x2 + $self->{'legend_example_size'};
811             $y2 = $y1 + ( $_ * ( $self->{'text_space'} + $h ) ) + $h / 2;
812              
813             # do the line first
814             $self->{'gd_obj'}->line( $x2, $y2, $x3, $y2, $color );
815              
816             # reset the brush for points
817             $brush = $self->_prepare_brush( $color, 'point', $self->{ 'pointStyle' . $_ } );
818             $self->{'gd_obj'}->setBrush($brush);
819              
820             # draw the point
821             $self->{'gd_obj'}->line( int( ( $x3 + $x2 ) / 2 ), $y2, int( ( $x3 + $x2 ) / 2 ), $y2, gdBrushed );
822              
823             # now the label
824             $x2 = $x3 + ( 2 * $self->{'text_space'} );
825             $y2 -= $h / 2;
826             if ( $self->{'legend_label_values'} =~ /^value$/i )
827             {
828             $self->{'gd_obj'}->string( $font, $x2, $y2, $labels[$_] . ' ' . $data->[1][$_], $color );
829             }
830             elsif ( $self->{'legend_label_values'} =~ /^percent$/i )
831             {
832             $label = sprintf( "%s %4.2f%%", $labels[$_], $data->[1][$_] / ( $dataset_sum || 1 ) * 100 );
833             $self->{'gd_obj'}->string( $font, $x2, $y2, $label, $color );
834             }
835             elsif ( $self->{'legend_label_values'} =~ /^both$/i )
836             {
837             if ( $data->[1][$_] =~ /\./ )
838             {
839             $label =
840             sprintf( "%s %4.2f%% %.2f", $labels[$_], $data->[1][$_] / ( $dataset_sum || 1 ) * 100, $data->[1][$_] );
841             }
842             else
843             {
844             $label = sprintf( "%s %4.2f%% %d", $labels[$_], $data->[1][$_] / ( $dataset_sum || 1 ) * 100, $data->[1][$_] );
845             }
846             $self->{'gd_obj'}->string( $font, $x2, $y2, $label, $color );
847             }
848             else
849             {
850             $self->{'gd_obj'}->string( $font, $x2, $y2, $labels[$_], $color );
851             }
852              
853             }
854              
855             # mark off the used space
856             $self->{'curr_x_min'} += $width;
857              
858             # and return
859             return 1;
860             }
861              
862             ## @fn private _draw_bottom_legend
863             # put the legend on the bottom of the chart
864             sub _draw_bottom_legend
865             {
866             my $self = shift;
867             my $data = $self->{'dataref'};
868             my @labels = @{ $data->[0] };
869             my ( $x1, $y1, $x2, $x3, $y2, $empty_width, $max_label_width, $cols, $rows, $color, $brush );
870             my ( $col_width, $row_height, $r, $c, $index, $x, $y, $w, $h );
871             my $font = $self->{'legend_font'};
872             my $max_label_len;
873             my $l1 = 0;
874             my $l2 = 0;
875             my ( $dataset_sum, $j );
876             my $label;
877              
878             # make sure we're using a real font
879             unless ( ( ref($font) ) eq 'GD::Font' )
880             {
881             croak "The font you specified isn\'t a GD Font object";
882             }
883              
884             # get the size of the font
885             ( $h, $w ) = ( $font->height, $font->width );
886              
887             # find the base x values
888             $x1 = $self->{'curr_x_min'} + $self->{'graph_border'};
889              
890             # + ($self->{'y_tick_label_length'} * $self->{'tick_label_font'}->width)
891             # + $self->{'tick_len'} + (3 * $self->{'text_space'});
892             $x2 = $self->{'curr_x_max'} - $self->{'graph_border'};
893             if ( $self->{'y_label'} )
894             {
895             $x1 += $self->{'label_font'}->height + 2 * $self->{'text_space'};
896             }
897             if ( $self->{'y_label2'} )
898             {
899             $x2 -= $self->{'label_font'}->height + 2 * $self->{'text_space'};
900             }
901              
902             #find out what the sum of all datapoits is, needed for the Labels with percent
903             $dataset_sum = 0;
904             for $j ( 0 .. $self->{'num_datapoints'} )
905             {
906             if ( defined $data->[1][$j] )
907             {
908             $dataset_sum += $data->[1][$j];
909             }
910             }
911              
912             # find out how who wide the largest label text is, especially look what kind of
913             # label is needed
914             foreach (@labels)
915             {
916             if ( length($_) > $l1 )
917             {
918             $l1 = length($_);
919             }
920             }
921             for ( my $i = 0 ; $i < ( $self->{'num_datapoints'} ) ; $i++ )
922             {
923             if ( length( $data->[1][$i] ) > $l2 )
924             {
925             $l2 = length( $data->[1][$i] );
926             }
927             }
928              
929             if ( $self->{'legend_label_values'} =~ /^value$/i )
930             {
931             $max_label_len = $l1 + $l2 + 1;
932             }
933             elsif ( $self->{'legend_label_values'} =~ /^percent$/i )
934             {
935             $max_label_len = $l1 + 7;
936             }
937             elsif ( $self->{'legend_label_values'} =~ /^both$/i )
938             {
939             $max_label_len = $l1 + $l2 + 9;
940             }
941             else
942             {
943             $max_label_len = $l1;
944             }
945              
946             # figure out how wide the columns need to be, and how many we
947             # can fit in the space available
948             $empty_width = ( $x2 - $x1 ) - ( 2 * $self->{'legend_space'} );
949             $max_label_width = $max_label_len * $w
950              
951             #$self->{'max_legend_label'} * $w
952             + ( 4 * $self->{'text_space'} ) + $self->{'legend_example_size'};
953             $cols = int( $empty_width / $max_label_width );
954             unless ($cols)
955             {
956             $cols = 1;
957             }
958             $col_width = $empty_width / $cols;
959              
960             # figure out how many rows we need, remember how tall they are
961             $rows = int( $self->{'num_datapoints'} / $cols );
962             unless ( ( $self->{'num_datapoints'} % $cols ) == 0 )
963             {
964             $rows++;
965             }
966             unless ($rows)
967             {
968             $rows = 1;
969             }
970             $row_height = $h + $self->{'text_space'};
971              
972             # box the legend off
973             $y1 = $self->{'curr_y_max'} - $self->{'text_space'} - ( $rows * $row_height ) - ( 2 * $self->{'legend_space'} );
974             $y2 = $self->{'curr_y_max'};
975             $self->{'gd_obj'}->rectangle( $x1, $y1, $x2, $y2, $self->_color_role_to_index('misc') );
976             $x1 += $self->{'legend_space'} + $self->{'text_space'};
977             $x2 -= $self->{'legend_space'};
978             $y1 += $self->{'legend_space'} + $self->{'text_space'};
979             $y2 -= $self->{'legend_space'} + $self->{'text_space'};
980              
981             # draw in the actual legend
982             for $r ( 0 .. $rows - 1 )
983             {
984             for $c ( 0 .. $cols - 1 )
985             {
986             $index = ( $r * $cols ) + $c; # find the index in the label array
987             if ( $labels[$index] )
988             {
989              
990             # get the color
991             $color = $self->_color_role_to_index( 'dataset' . $index );
992              
993             # get the x-y coordinate for the start of the example line
994             $x = $x1 + ( $col_width * $c );
995             $y = $y1 + ( $row_height * $r ) + $h / 2;
996              
997             # now draw the example line
998             $self->{'gd_obj'}->line( $x, $y, $x + $self->{'legend_example_size'}, $y, $color );
999              
1000             # reset the brush for points
1001             $brush = $self->_prepare_brush( $color, 'point', $self->{ 'pointStyle' . $index } );
1002             $self->{'gd_obj'}->setBrush($brush);
1003              
1004             # draw the point
1005             $x3 = int( $x + $self->{'legend_example_size'} / 2 );
1006             $self->{'gd_obj'}->line( $x3, $y, $x3, $y, gdBrushed );
1007              
1008             # adjust the x-y coordinates for the start of the label
1009             $x += $self->{'legend_example_size'} + ( 2 * $self->{'text_space'} );
1010             $y = $y1 + ( $row_height * $r );
1011              
1012             # now draw the label
1013             if ( $self->{'legend_label_values'} =~ /^value$/i )
1014             {
1015             $self->{'gd_obj'}->string( $font, $x, $y, $labels[$index] . ' ' . $data->[1][$index], $color );
1016              
1017             #$self->{'gd_obj'}->stringTTF($color, FONT, 10, 0, $x, $y+10, $labels[$index].' '.$data->[1][$index]); ############
1018             }
1019             elsif ( $self->{'legend_label_values'} =~ /^percent$/i )
1020             {
1021             $label = sprintf( "%s %4.2f%%", $labels[$index], $data->[1][$index] / ( $dataset_sum || 1 ) * 100 );
1022             $self->{'gd_obj'}->string( $font, $x, $y, $label, $color );
1023             }
1024             elsif ( $self->{'legend_label_values'} =~ /^both$/i )
1025             {
1026             if ( $data->[1][$index] =~ /\./ )
1027             {
1028             $label = sprintf( "%s %4.2f%% %.2f",
1029             $labels[$index],
1030             $data->[1][$index] / ( $dataset_sum || 1 ) * 100,
1031             $data->[1][$index] );
1032             }
1033             else
1034             {
1035             $label = sprintf( "%s %4.2f%% %d",
1036             $labels[$index],
1037             $data->[1][$index] / ( $dataset_sum || 1 ) * 100,
1038             $data->[1][$index] );
1039             }
1040             $self->{'gd_obj'}->string( $font, $x, $y, $label, $color ); ###
1041             # $self->{'gd_obj'}->stringTTF($color, FONT, 10, 0, $x, $y, $label);
1042              
1043             }
1044             else
1045             {
1046             $self->{'gd_obj'}->string( $font, $x, $y, $labels[$index], $color );
1047             }
1048             }
1049             }
1050             }
1051              
1052             # mark off the space used
1053             $self->{'curr_y_max'} -= ( $rows * $row_height ) + $self->{'text_space'} + ( 2 * $self->{'legend_space'} );
1054              
1055             # now return
1056             return 1;
1057             }
1058              
1059             ## @fn private _draw_top_legend
1060             # put the legend on top of the chart
1061             sub _draw_top_legend
1062             {
1063             my $self = shift;
1064             my $data = $self->{'dataref'};
1065             my ($max_label_len);
1066             my $l1 = 0;
1067             my $l2 = 0;
1068             my @labels = @{ $data->[0] };
1069             my ( $x1, $y1, $x2, $x3, $y2, $empty_width, $max_label_width, $cols, $rows, $color, $brush );
1070             my ( $col_width, $row_height, $r, $c, $index, $x, $y, $w, $h, $dataset_sum, $label );
1071             my $font = $self->{'legend_font'};
1072              
1073             # make sure we're using a real font
1074             unless ( ( ref($font) ) eq 'GD::Font' )
1075             {
1076             croak "The font you specified isn\'t a GD Font object";
1077             }
1078              
1079             # get the size of the font
1080             ( $h, $w ) = ( $font->height, $font->width );
1081              
1082             #find out what the sum of all datapoits is, needed for the Labels with percent
1083             $dataset_sum = 0;
1084             for my $j ( 0 .. $self->{'num_datapoints'} )
1085             {
1086             if ( defined $data->[1][$j] )
1087             {
1088             $dataset_sum += $data->[1][$j];
1089             }
1090             }
1091              
1092             # get some base x coordinates
1093             $x1 = $self->{'curr_x_min'} + $self->{'graph_border'};
1094              
1095             # + $self->{'y_tick_label_length'} * $self->{'tick_label_font'}->width
1096             # + $self->{'tick_len'} + (3 * $self->{'text_space'});
1097             $x2 = $self->{'curr_x_max'} - $self->{'graph_border'};
1098             if ( $self->{'y_label'} )
1099             {
1100             $x1 += $self->{'label_font'}->height + 2 * $self->{'text_space'};
1101             }
1102             if ( $self->{'y_label2'} )
1103             {
1104             $x2 -= $self->{'label_font'}->height + 2 * $self->{'text_space'};
1105             }
1106              
1107             # find out how who wide the largest label text is
1108             foreach (@labels)
1109             {
1110             if ( length($_) > $l1 )
1111             {
1112             $l1 = length($_);
1113             }
1114             }
1115             for ( my $i = 0 ; $i < ( $self->{'num_datapoints'} ) ; $i++ )
1116             {
1117             if ( length( $data->[1][$i] ) > $l2 )
1118             {
1119             $l2 = length( $data->[1][$i] );
1120             }
1121             }
1122              
1123             if ( $self->{'legend_label_values'} =~ /^value$/i )
1124             {
1125             $max_label_len = $l1 + $l2 + 1;
1126             }
1127             elsif ( $self->{'legend_label_values'} =~ /^percent$/i )
1128             {
1129             $max_label_len = $l1 + 7;
1130             }
1131             elsif ( $self->{'legend_label_values'} =~ /^both$/i )
1132             {
1133             $max_label_len = $l1 + $l2 + 9;
1134             }
1135             else
1136             {
1137             $max_label_len = $l1;
1138             }
1139              
1140             # figure out how wide the columns can be, and how many will fit
1141             $empty_width = ( $x2 - $x1 ) - ( 2 * $self->{'legend_space'} );
1142             $max_label_width = ( 4 * $self->{'text_space'} ) + $max_label_len * $w + $self->{'legend_example_size'};
1143             $cols = int( $empty_width / $max_label_width );
1144              
1145             unless ($cols)
1146             {
1147             $cols = 1;
1148             }
1149             $col_width = $empty_width / $cols;
1150              
1151             # figure out how many rows we need and remember how tall they are
1152             $rows = int( $self->{'num_datapoints'} / $cols );
1153             unless ( ( $self->{'num_datapoints'} % $cols ) == 0 )
1154             {
1155             $rows++;
1156             }
1157             unless ($rows)
1158             {
1159             $rows = 1;
1160             }
1161             $row_height = $h + $self->{'text_space'};
1162              
1163             # box the legend off
1164             $y1 = $self->{'curr_y_min'};
1165             $y2 = $self->{'curr_y_min'} + $self->{'text_space'} + ( $rows * $row_height ) + ( 2 * $self->{'legend_space'} );
1166             $self->{'gd_obj'}->rectangle( $x1, $y1, $x2, $y2, $self->_color_role_to_index('misc') );
1167              
1168             # leave some space inside the legend
1169             $x1 += $self->{'legend_space'} + $self->{'text_space'};
1170             $x2 -= $self->{'legend_space'};
1171             $y1 += $self->{'legend_space'} + $self->{'text_space'};
1172             $y2 -= $self->{'legend_space'} + $self->{'text_space'};
1173              
1174             # draw in the actual legend
1175             for $r ( 0 .. $rows - 1 )
1176             {
1177             for $c ( 0 .. $cols - 1 )
1178             {
1179             $index = ( $r * $cols ) + $c; # find the index in the label array
1180             if ( $labels[$index] )
1181             {
1182              
1183             # get the color
1184             $color = $self->_color_role_to_index( 'dataset' . $index );
1185              
1186             # find the x-y coords
1187             $x = $x1 + ( $col_width * $c );
1188             $y = $y1 + ( $row_height * $r ) + $h / 2;
1189              
1190             # draw the line first
1191             $self->{'gd_obj'}->line( $x, $y, $x + $self->{'legend_example_size'}, $y, $color );
1192              
1193             # reset the brush for points
1194             $brush = $self->_prepare_brush( $color, 'point', $self->{ 'pointStyle' . $index } );
1195             $self->{'gd_obj'}->setBrush($brush);
1196              
1197             # draw the point
1198             $x3 = int( $x + $self->{'legend_example_size'} / 2 );
1199             $self->{'gd_obj'}->line( $x3, $y, $x3, $y, gdBrushed );
1200              
1201             # now the label
1202             $x += $self->{'legend_example_size'} + ( 2 * $self->{'text_space'} );
1203             $y -= $h / 2;
1204             if ( $self->{'legend_label_values'} =~ /^value$/i )
1205             {
1206             $self->{'gd_obj'}->string( $font, $x, $y, $labels[$index] . ' ' . $data->[1][$index], $color );
1207             }
1208             elsif ( $self->{'legend_label_values'} =~ /^percent$/i )
1209             {
1210             $label = sprintf( "%s %4.2f%%", $labels[$index], $data->[1][$index] / ( $dataset_sum || 1 ) * 100 );
1211             $self->{'gd_obj'}->string( $font, $x, $y, $label, $color );
1212             }
1213             elsif ( $self->{'legend_label_values'} =~ /^both$/i )
1214             {
1215             if ( $data->[1][$index] =~ /\./ )
1216             {
1217             $label = sprintf( "%s %4.2f%% %.2f",
1218             $labels[$index],
1219             $data->[1][$index] / ( $dataset_sum || 1 ) * 100,
1220             $data->[1][$index] );
1221             }
1222             else
1223             {
1224             $label = sprintf( "%s %4.2f%% %d",
1225             $labels[$index],
1226             $data->[1][$index] / ( $dataset_sum || 1 ) * 100,
1227             $data->[1][$index] );
1228             }
1229             $self->{'gd_obj'}->string( $font, $x, $y, $label, $color );
1230             }
1231             else
1232             {
1233             $self->{'gd_obj'}->string( $font, $x, $y, $labels[$index], $color );
1234             }
1235             }
1236             }
1237             }
1238              
1239             # mark off the space used
1240             $self->{'curr_y_min'} += ( $rows * $row_height ) + $self->{'text_space'} + 2 * $self->{'legend_space'};
1241              
1242             # now return
1243             return 1;
1244             }
1245              
1246             ## @fn private _draw_x_ticks
1247             # Override the ticks methods for the pie charts.\n
1248             # Here: do nothing
1249             sub _draw_x_ticks
1250             {
1251             my $self = shift;
1252              
1253             return;
1254             }
1255              
1256             ## @fn private _draw_y_ticks
1257             # Override the ticks methods for the pie charts.\n
1258             sub _draw_y_ticks
1259             {
1260             my $self = shift;
1261              
1262             return;
1263             }
1264              
1265             ## @fn private _find_y_scale
1266             # Override the find_y_scale methods for the pie charts.\n
1267             # Here: do nothing
1268             sub _find_y_scale
1269             {
1270             my $self = shift;
1271              
1272             return;
1273             }
1274              
1275             ## be a good module and return 1
1276             1;