File Coverage

blib/lib/Chart/Composite.pm
Criterion Covered Total %
statement 515 624 82.5
branch 117 188 62.2
condition 7 21 33.3
subroutine 22 24 91.7
pod 0 2 0.0
total 661 859 76.9


line stmt bran cond sub pod time code
1             #====================================================================
2             # Chart::Composite
3             #
4             # written by david bonner
5             # dbonner@cs.bu.edu
6             #
7             # maintained by the Chart Group
8             # Chart@wettzell.ifag.de
9             #
10             #
11             #---------------------------------------------------------------------
12             # History:
13             #----------
14             # $RCSfile: Composite.pm,v $ $Revision: 1.4 $ $Date: 2003/02/14 13:25:30 $
15             # $Author: dassing $
16             # $Log: Composite.pm,v $
17             # Revision 1.4 2003/02/14 13:25:30 dassing
18             # Circumvent division of zeros
19             #
20             #====================================================================
21              
22             package Chart::Composite;
23              
24 8     8   238 use Chart::Base 2.3;
  8         215  
  8         180  
25 8     8   162 use GD;
  8         77  
  8         147  
26 8     8   155 use Carp;
  8         152  
  8         139  
27 8     8   150 use strict;
  8         75  
  8         114  
28              
29             @Chart::Composite::ISA = qw(Chart::Base);
30             $Chart::Composite::VERSION = '2.3';
31              
32             #>>>>>>>>>>>>>>>>>>>>>>>>>>#
33             # public methods go here #
34             #<<<<<<<<<<<<<<<<<<<<<<<<<<#
35              
36             ## have to override set, so we can pass the options to the
37             ## sub-objects later
38             sub set {
39 21     21 0 563   my $self = shift;
40 21         8081   my %opts = @_;
41              
42             # basic error checking on the options, just warn 'em
43 21 50       288   unless ($#_ % 2) {
44 0         0     carp "Whoops, some option to be set didn't have a value.\n",
45                      "You might want to look at that.\n";
46               }
47              
48             # store the options they gave us
49 21 100       301   unless ($self->{'opts'}) {
50 8         92     $self->{'opts'} = {};
51               }
52              
53             # now set 'em
54 21         256   for (keys %opts) {
55 87         1600     $self->{$_} = $opts{$_};
56 87         2333     $self->{'opts'}{$_} = $opts{$_};
57               }
58              
59             # now return
60 21         452   return;
61             }
62              
63              
64             ## get the information to turn the chart into an imagemap
65             ## had to override it to reassemble the @data array correctly
66             sub imagemap_dump {
67 1     1 0 14   my $self = shift;
68 1         10   my ($i, $j);
69 1         11   my @map;
70 1         10   my $dataset_count = 0;
71              
72             # croak if they didn't ask me to remember the data, or if they're asking
73             # for the data before I generate it
74 1 50 33     29   unless (($self->{'imagemap'} =~ /^true$/i) && $self->{'imagemap_data'}) {
75 0         0     croak "You need to set the imagemap option to true, and then call the png method, before you can get the imagemap data";
76               }
77              
78             #make a copy of the imagemap data
79             #this is the data of the first component
80 1         134   for $i (1..$#{$self->{'sub_0'}->{'imagemap_data'}}) {
  1         25  
81 1         11     for $j (0..$#{$self->{'sub_0'}->{'imagemap_data'}->[$i]}-1) {
  1         18  
82 6         48        $map[$i][$j] = \@{$self->{'sub_0'}->{'imagemap_data'}->[$i][$j]} ;
  6         75  
83                 }
84 1         13     $dataset_count++;
85               }
86             #and add the data of the second component
87 1         10   for $i (1..$#{$self->{'sub_1'}->{'imagemap_data'}}) {
  1         14  
88 1         9     for $j (0..$#{$self->{'sub_1'}->{'imagemap_data'}->[$i]}-1) {
  1         13  
89 6         50       $map[$i+$dataset_count][$j] = \@{$self->{'sub_1'}->{'imagemap_data'}->[$i][$j]} ;
  6         68  
90                 }
91               }
92               
93              
94             # return their copy
95 1         17   return \@map;
96              
97             }
98              
99             sub __print_array {
100 0     0   0    my @a = @_;
101 0         0    my $i;
102                
103 0         0    my $li = $#a;
104                
105 0         0    $li++;
106 0         0    print STDERR "Anzahl der Elemente = $li\n"; $li--;
  0         0  
107                
108                for ($i=0; $i<=$li; $i++) {
109 0         0       print STDERR "\t$i\t$a[$i]\n";
110 0         0    }
111             }
112                
113             #>>>>>>>>>>>>>>>>>>>>>>>>>>>#
114             # private methods go here #
115             #<<<<<<<<<<<<<<<<<<<<<<<<<<<#
116              
117             ## make sure the data isn't really weird
118             ## and collect some basic info about it
119             sub _check_data {
120 8     8   83   my $self = shift;
121 8         78   my $length = 0;
122              
123             # first things first, make sure we got the composite_info
124 8 50 33     175   unless (($self->{'composite_info'}) && ($#{$self->{'composite_info'}} == 1)) {
  8         180  
125 0         0     croak "Chart::Composite needs to be told what kind of components to use";
126               }
127              
128             # make sure we don't end up dividing by zero if they ask for
129             # just one y_tick
130 8 50       115   if ($self->{'y_ticks'} == 1) {
131 0         0     $self->{'y_ticks'} = 2;
132 0         0     carp "The number of y_ticks displayed must be at least 2";
133               }
134              
135             # remember the number of datasets
136 8         75   $self->{'num_datasets'} = $#{$self->{'dataref'}};
  8         124  
137              
138             # remember the number of points in the largest dataset
139 8         84   $self->{'num_datapoints'} = 0;
140 8         91   for (0..$self->{'num_datasets'}) {
141 47 100       421     if (scalar(@{$self->{'dataref'}[$_]}) > $self->{'num_datapoints'}) {
  47         616  
142 8         108       $self->{'num_datapoints'} = scalar(@{$self->{'dataref'}[$_]});
  8         92  
143                 }
144               }
145              
146             # find the longest x-tick label, and remember how long it is
147 8         80   for (@{$self->{'dataref'}[0]}) {
  8         88  
148 60 100       2045     if (length ($_) > $length) {
149 10         142       $length = length ($_);
150                 }
151               }
152 8         91   $self->{'x_tick_label_length'} = $length;
153              
154             # now split the data into sub-objects
155 8         94   $self->_split_data;
156              
157 8         96   return;
158             }
159              
160              
161             ## create sub-objects for each type, store the appropriate
162             ## data sets in each one, and stick the correct values into
163             ## them (ie. 'gd_obj');
164             sub _split_data {
165 8     8   77   my $self = shift;
166 8         110   my @types = ($self->{'composite_info'}[0][0],$self->{'composite_info'}[1][0]);
167 8         76   my ($ref, $i, $j);
168              
169             ## Already checked for number of components in _check_data, above.
170             # # we can only do two at a time
171             # if ($self->{'composite_info'}[2]) {
172             # croak "Sorry, Chart::Composite can only do two chart types at a time";
173             # }
174              
175             # load the individual modules
176 8         406   require "Chart/".$types[0].".pm";
177 8         1288   require "Chart/".$types[1].".pm";
178              
179             # create the sub-objects
180 8         251   $self->{'sub_0'} = ("Chart::".$types[0])->new();
181 8         401   $self->{'sub_1'} = ("Chart::".$types[1])->new();
182              
183             # set the options (set the min_val, max_val, and y_ticks
184             # options intelligently so that the sub-objects don't get
185             # confused)
186 8         110   $self->{'sub_0'}->set (%{$self->{'opts'}});
  8         249  
187 8         97   $self->{'sub_1'}->set (%{$self->{'opts'}});
  8         228  
188 8 50       124   if (defined ($self->{'opts'}{'min_val1'})) {
189 0         0     $self->{'sub_0'}->set ('min_val' => $self->{'opts'}{'min_val1'});
190               }
191 8 100       106   if (defined ($self->{'opts'}{'max_val1'})) {
192 2         29     $self->{'sub_0'}->set ('max_val' => $self->{'opts'}{'max_val1'});
193               }
194 8 50       100   if (defined ($self->{'opts'}{'min_val2'})) {
195 0         0     $self->{'sub_1'}->set ('min_val' => $self->{'opts'}{'min_val2'});
196               }
197 8 100       158   if (defined ($self->{'opts'}{'max_val2'})) {
198 2         42     $self->{'sub_1'}->set ('max_val' => $self->{'opts'}{'max_val2'});
199               }
200 8 50       108   if ($self->{'opts'}{'y_ticks1'}) {
201 0         0     $self->{'sub_0'}->set ('y_ticks' => $self->{'opts'}{'y_ticks1'});
202               }
203 8 50       119   if ($self->{'opts'}{'y_ticks2'}) {
204 0         0     $self->{'sub_1'}->set ('y_ticks' => $self->{'opts'}{'y_ticks2'});
205               }
206             # f_y_tick for left and right axis
207 8 50       126   if (defined ($self->{'opts'}{'f_y_tick1'})) {
208 0         0     $self->{'sub_0'}->set ('f_y_tick' => $self->{'opts'}{'f_y_tick1'});
209               }
210 8 50       114   if (defined ($self->{'opts'}{'f_y_tick2'})) {
211 0         0     $self->{'sub_1'}->set ('f_y_tick' => $self->{'opts'}{'f_y_tick2'});
212               }
213              
214             # replace the gd_obj fields
215 8         93   $self->{'sub_0'}->{'gd_obj'} = $self->{'gd_obj'};
216 8         1270   $self->{'sub_1'}->{'gd_obj'} = $self->{'gd_obj'};
217              
218             # let the sub-objects know they're sub-objects
219 8         650   $self->{'sub_0'}->{'component'} = 'true';
220 8         93   $self->{'sub_1'}->{'component'} = 'true';
221              
222             # give each sub-object its data
223 8         115   $self->{'component_datasets'} = [];
224 8         95   for $i (0..1) {
225 16         147     $ref = [];
226 16         1043     $self->{'component_datasets'}[$i] = $self->{'composite_info'}[$i][1];
227 16         140     push @{$ref}, $self->{'dataref'}[0];
  16         179  
228 16         146     for $j (@{$self->{'composite_info'}[$i][1]}) {
  16         171  
229 39         571       $self->_color_role_to_index('dataset'.($j-1)); # allocate color index
230 39         347       push @{$ref}, $self->{'dataref'}[$j];
  39         468  
231                 }
232 16         387     $self->{'sub_'.$i}->_copy_data ($ref);
233               }
234              
235             # and let them check it
236 8         380   $self->{'sub_0'}->_check_data;
237 8         175   $self->{'sub_1'}->_check_data;
238              
239             # realign the y-axes if they want
240 8 100       115   if ($self->{'same_y_axes'} =~ /^true$/i) {
241 3 50       41     if ($self->{'sub_0'}{'min_val'} < $self->{'sub_1'}{'min_val'}) {
242 0         0       $self->{'sub_1'}{'min_val'} = $self->{'sub_0'}{'min_val'};
243                 }
244                 else {
245 3         35       $self->{'sub_0'}{'min_val'} = $self->{'sub_1'}{'min_val'};
246                 }
247              
248 3 50       40     if ($self->{'sub_0'}{'max_val'} > $self->{'sub_1'}{'max_val'}) {
249 0         0       $self->{'sub_1'}{'max_val'} = $self->{'sub_0'}{'max_val'};
250                 }
251                 else {
252 3         34       $self->{'sub_0'}{'max_val'} = $self->{'sub_1'}{'max_val'};
253                 }
254              
255 3         43     $self->{'sub_0'}->_check_data;
256 3         45     $self->{'sub_1'}->_check_data;
257               }
258            
259             # find out how big the y-tick labels will be from sub_0 and sub_1
260 8         101   $self->{'y_tick_label_length1'} = $self->{'sub_0'}->{'y_tick_label_length'};
261 8         98   $self->{'y_tick_label_length2'} = $self->{'sub_1'}->{'y_tick_label_length'};
262              
263             # now return
264 8         141   return;
265             }
266              
267             sub _draw_legend {
268 8     8   78   my $self = shift;
269 8         73   my ($length);
270              
271             # check to see if they have as many labels as datasets,
272             # warn them if not
273 8 50 66     74   if (($#{$self->{'legend_labels'}} >= 0) &&
  8         136  
  4         63  
274                    ((scalar(@{$self->{'legend_labels'}})) != $self->{'num_datasets'})) {
275 0         0     carp "The number of legend labels and datasets doesn\'t match";
276               }
277              
278             # init a field to store the length of the longest legend label
279 8 50       102   unless ($self->{'max_legend_label'}) {
280 8         116     $self->{'max_legend_label'} = 0;
281               }
282              
283             # fill in the legend labels, find the longest one
284 8         87   for (1..$self->{'num_datasets'}) {
285 39 100       472     unless ($self->{'legend_labels'}[$_-1]) {
286 15         175       $self->{'legend_labels'}[$_-1] = "Dataset $_";
287                 }
288 39         429     $length = length($self->{'legend_labels'}[$_-1]);
289 39 100       419     if ($length > $self->{'max_legend_label'}) {
290 12         118       $self->{'max_legend_label'} = $length;
291                 }
292               }
293              
294             # different legend types
295 8 100       139   if ($self->{'legend'} eq 'bottom') {
    100          
    100          
    50          
    0          
296 3         35     $self->_draw_bottom_legend;
297               }
298               elsif ($self->{'legend'} eq 'right') {
299 2         28     $self->_draw_right_legend;
300               }
301               elsif ($self->{'legend'} eq 'left') {
302 1         11     $self->_draw_left_legend;
303               }
304               elsif ($self->{'legend'} eq 'top') {
305 2         23     $self->_draw_top_legend;
306               } elsif ($self->{'legend'} eq 'none') {
307 0         0     $self->_draw_none_legend;
308               } else {
309 0         0     carp "I can't put a legend there\n";
310               }
311              
312             # and return
313 8         98   return 1;
314             }
315              
316             ## put the legend on the top of the data plot
317             sub _draw_top_legend {
318 2     2   21   my $self = shift;
319 2         17   my @labels = @{$self->{'legend_labels'}};
  2         29  
320 2         20   my ($x1, $y1, $x2, $y2, $empty_width, $max_label_width, $cols, $rows, $color);
321 2         67   my ($col_width, $row_height, $i, $j, $r, $c, $index, $x, $y, $sub, $w, $h);
322 2         19   my ($yh,$yi); # for boxing legends
323 2         19   my $font = $self->{'legend_font'};
324 2         19   my (%colors, @datasets);
325 2         19   my $max_legend_example=0;
326 2         20   $yh=0;
327              
328             # copy the current boundaries into the sub-objects
329 2         24   $self->_sub_update;
330               
331             # init the legend_example_height
332 2         23   $self->_legend_example_height_init;
333              
334             ## Make datasetI numbers match indexes of @{ $self->{'dataref'} }[1.....].
335             # # modify the dataset color table entries to avoid duplicating
336             # # dataset colors (this limits the number of possible data sets
337             # # for each component to 8)
338             # for (0..7) {
339             # $self->{'sub_1'}{'color_table'}{'dataset'.$_}
340             # = $self->{'color_table'}{'dataset'.($_+8)};
341             # }
342             # modify the dataset color table entries to avoid duplicating
343             # dataset colors.
344 2         24   my ($n0, $n1) = map { scalar @{ $self->{'composite_info'}[$_][1] } } 0..1;
  4         36  
  4         48  
345 2         25   for (0..$n1-1) {
346 6         81     $self->{'sub_1'}{'color_table'}{'dataset'.$_}
347                   = $self->{'color_table'}{'dataset'.($_+$n0)};
348               }
349              
350             # make sure we use the right colors for the legend
351 2         20   @datasets = @{$self->{'composite_info'}[0][1]};
  2         27  
352 2         20   $i = 0;
353 2         24   for (0..$#datasets) {
354 6         74     $colors{$datasets[$_]-1} = $self->{'color_table'}{'dataset'.($i)};
355 6         53     $i++;
356               }
357 2         21   @datasets = @{$self->{'composite_info'}[1][1]};
  2         25  
358 2         19   $i = 0;
359 2         22   for (0..$#datasets) {
360 6         74     $colors{$datasets[$_]-1} = $self->{'color_table'}{'dataset'.($i+$n0)};
361 6         54     $i++;
362               }
363              
364             # make sure we're using a real font
365 2 50       28   unless ((ref ($font)) eq 'GD::Font') {
366 0         0     croak "The subtitle font you specified isn\'t a GD Font object";
367               }
368              
369             # get the size of the font
370 2         39   ($h, $w) = ($font->height, $font->width);
371              
372             # get some base x coordinates
373 2         36   $x1 = $self->{'curr_x_min'} + $self->{'graph_border'}
374                       + $self->{'y_tick_label_length1'} * $self->{'tick_label_font'}->width
375             + $self->{'tick_len'} + (3 * $self->{'text_space'});
376 2         31   $x2 = $self->{'curr_x_max'} - $self->{'graph_border'}
377                       - $self->{'y_tick_label_length2'} * $self->{'tick_label_font'}->width
378             - $self->{'tick_len'} - (3 * $self->{'text_space'});
379 2 100       24   if ($self->{'y_label'}) {
380 1         14     $x1 += $self->{'label_font'}->height + 2 * $self->{'text_space'};
381               }
382 2 50       25   if ($self->{'y_label2'}) {
383 0         0     $x2 -= $self->{'label_font'}->height + 2 * $self->{'text_space'};
384               }
385              
386             # figure out how wide the widest label is, then figure out how many
387             # columns we can fit into the allotted space
388 2         177   $empty_width = $x2 - $x1 - (2 * $self->{'legend_space'});
389 2         30   $max_label_width = $self->{'max_legend_label'}
390                 * $self->{'legend_font'}->width + 4 * $self->{'text_space'}
391                 + $self->{'legend_example_size'};
392 2         21   $cols = int ($empty_width / $max_label_width);
393 2 50       22   unless ($cols) {
394 0         0     $cols = 1;
395               }
396 2         17   $col_width = $empty_width / $cols;
397              
398             # figure out how many rows we need and how tall they are
399 2         20   $rows = int ($self->{'num_datasets'} / $cols);
400 2 50       25   unless (($self->{'num_datasets'} % $cols) == 0) {
401 0         0     $rows++;
402               }
403 2 50       22   unless ($rows) {
404 0         0     $rows = 1;
405               }
406 2         19   $row_height = $h + $self->{'text_space'};
407              
408             # box the legend off
409 2         19   $y1 = $self->{'curr_y_min'};
410 2         517   $y2 = $self->{'curr_y_min'} + $self->{'text_space'}
411                       + ($rows * $row_height) + (2 * $self->{'legend_space'});
412 2         31   $self->{'gd_obj'}->rectangle($x1, $y1, $x2, $y2,
413                                            $self->_color_role_to_index('misc'));
414            
415 2         74   $max_legend_example = $y2-$y1;
416             # leave some space inside the legend
417 2         40   $x1 += $self->{'legend_space'} + $self->{'text_space'};
418 2         24   $x2 -= $self->{'legend_space'};
419 2         21   $y1 += $self->{'legend_space'} + $self->{'text_space'};
420 2         20   $y2 -= $self->{'legend_space'} + $self->{'text_space'};
421              
422             # draw in the actual legend
423 2         19   $r = 0; # current row
424 2         18   $c = 0; # current column
425 2         18   $yi= 0; # current dataset
426                
427 2         25   for $i (0..1) {
428 4         33     for $j (0..$#{$self->{'component_datasets'}[$i]}) {
  4         56  
429             # get the color
430 12         149       $color = $self->{'sub_'.$i}->{'color_table'}{'dataset'.$j};
431 12         113       $index = $self->{'component_datasets'}[$i][$j] - 1; # index in label list
432              
433             # find the x-y coordinates for the beginning of the example line
434 12         142       $x = $x1 + ($col_width * $c);
435 12         111       $y = $y1 + ($row_height * $r) + $h/2;
436              
437             # draw the example line if legend_example_height==1 or ==0
438 12 100       113 if ($rows==1) {
439 3 50       35 if ($self->{'legend_example_height'.$yi}<$max_legend_example) {
440 3         31 $yh = $self->{'legend_example_height'.$yi};
441             }
442             else {
443 0         0 $yh = $max_legend_example;
444             }
445             }
446             else {
447 9 100       105 if ($self->{'legend_example_height'.$yi}<$row_height) {
448 5         49 $yh = $self->{'legend_example_height'.$yi};
449             }
450             else {
451 4         34 $yh = $row_height;
452             }
453             } 
454 12         120 $yi++;
455 12 50       111 if ($yh <= 1) {
456 0         0       $self->{'gd_obj'}->line ($x, $y,
457                                            $x + $self->{'legend_example_size'}, $y,
458             $color);
459             } else {
460             # draw the example bar if legend_example_height > 1
461 12         102 $yh = int($yh / 2);
462 12         510       $self->{'gd_obj'}->filledRectangle ($x, $y-$yh,
463                                            $x + $self->{'legend_example_size'}, $y+$yh,
464             $color);
465             }
466              
467             # find the x-y coordinates for the beginning of the label
468 12         131       $x += $self->{'legend_example_size'} + 2 * $self->{'text_space'};
469 12         100       $y -= $h/2;
470                   
471             # now draw the label
472 12         441       $self->{'gd_obj'}->string($font, $x, $y,
473                                             $labels[$index], $color);
474              
475             # keep track of which row/column we're using
476 12         128       $r = ($r + 1) % $rows;
477 12 100       178       if ($r == 0) {
478 6         70 $c++;
479                   }
480                 }
481               }
482               
483                   
484             # mark of the space used
485 2         26   $self->{'curr_y_min'} += ($rows * $row_height)
486                + $self->{'text_space'}
487             + 2 * $self->{'legend_space'};
488              
489 2         42   return;
490             }
491              
492              
493             ## put the legend on the right of the chart
494             sub _draw_right_legend {
495 2     2   24   my $self = shift;
496 2         19   my @labels = @{$self->{'legend_labels'}};
  2         29  
497 2         21   my ($x1, $x2, $x3, $y1, $y2, $width, $color, $misccolor, $w, $h);
498 2         21   my ($yh)=0; # for boxing legend
499 2         22   my $font = $self->{'legend_font'};
500 2         19   my (%colors, @datasets, $i);
501 2         24   my $max_legend_example = 0;
502               
503               
504             # copy the current boundaries and colors into the sub-objects
505 2         27   $self->_sub_update;
506               
507             # init the legend exapmle height
508 2         23   $self->_legend_example_height_init;
509               
510             # # modify the dataset color table entries to avoid duplicating
511             # # dataset colors (this limits the number of possible data sets
512             # # for each component to 8)
513             # for (0..7) {
514             # $self->{'sub_1'}{'color_table'}{'dataset'.$_}
515             # = $self->{'color_table'}{'dataset'.($_+8)};
516             # }
517             # modify the dataset color table entries to avoid duplicating
518             # dataset colors.
519 2         20   my ($n0, $n1) = map { scalar @{ $self->{'composite_info'}[$_][1] } } 0..1;
  4         34  
  4         48  
520               
521 2         22   for (0..$n1-1) {
522 3         48     $self->{'sub_1'}{'color_table'}{'dataset'.$_}
523                   = $self->{'color_table'}{'dataset'.($_+$n0)};
524               }
525               
526               
527               
528             # make sure we use the right colors for the legend
529 2         20   @datasets = @{$self->{'composite_info'}[0][1]};
  2         24  
530 2         19   $i = 0;
531 2         26   for (0..$#datasets) {
532 5         60    $colors{$datasets[$_]-1} = $self->{'color_table'}{'dataset'.($_)};
533 5         46     $i++;
534               }
535                
536 2         20   @datasets = @{$self->{'composite_info'}[1][1]};
  2         26  
537 2         18   $i = 0;
538 2         22   for (0..$#datasets) {
539 3         141     $colors{$datasets[$_]-1} = $self->{'color_table'}{'dataset'.($i+$n0)};
540 3         42     $i++;
541               }
542                   
543             # make sure we're using a real font
544 2 50       31   unless ((ref ($font)) eq 'GD::Font') {
545 0         0     croak "The subtitle font you specified isn\'t a GD Font object";
546               }
547              
548             # get the size of the font
549 2         42   ($h, $w) = ($font->height, $font->width);
550              
551             # get the miscellaneous color
552 2         31   $misccolor = $self->_color_role_to_index('misc');
553              
554             # find out how wide the largest label is
555 2         28   $width = (2 * $self->{'text_space'})
556                 + ($self->{'max_legend_label'} * $w)
557                 + $self->{'legend_example_size'}
558                 + (2 * $self->{'legend_space'});
559              
560             # box the thing off
561 2         19   $x1 = $self->{'curr_x_max'} - $width;
562 2         19   $x2 = $self->{'curr_x_max'};
563 2         20   $y1 = $self->{'curr_y_min'} + $self->{'graph_border'} ;
564 2         28   $y2 = $self->{'curr_y_min'} + $self->{'graph_border'} + $self->{'text_space'}
565                       + ($self->{'num_datasets'} * ($h + $self->{'text_space'}))
566                       + (2 * $self->{'legend_space'});
567 2         184   $self->{'gd_obj'}->rectangle ($x1, $y1, $x2, $y2, $misccolor);
568              
569             # leave that nice space inside the legend box
570 2         1963   $x1 += $self->{'legend_space'};
571 2         43   $y1 += $self->{'legend_space'} + $self->{'text_space'};
572              
573             # now draw the actual legend
574 2         31   for (0..$#labels) {
575             # get the color
576 8         406     $color = $colors{$_};
577                 
578             # find the max_legend_example
579 8         76     $max_legend_example = $self->{'legend_space'}+$h;
580                
581             # find the x-y coords
582 8         65     $x2 = $x1;
583 8         72     $x3 = $x2 + $self->{'legend_example_size'};
584 8         83     $y2 = $y1 + ($_ * ($self->{'text_space'} + $h)) + $h/2;
585              
586             # draw the example line if legend_example_height==1 or ==0
587 8 50       92 if ($self->{'legend_example_height'.$_}<$max_legend_example) {
588 8         80 $yh = $self->{'legend_example_height'.$_};
589             }
590             else {
591 0         0 $yh = $max_legend_example;
592             }   
593 8 50       79 if ($yh <= 1) {
594 8         187       $self->{'gd_obj'}->line ($x2, $y2, $x3, $y2, $color);
595             } else {
596 0         0 $yh = int($yh / 2);
597 0         0 $self->{'gd_obj'}->filledRectangle ($x2, $y2-$yh, $x3, $y2+$yh, $color);
598             }
599              
600             # now the label
601 8         73     $x2 = $x3 + (2 * $self->{'text_space'});
602 8         159     $y2 -= $h/2;
603 8         231     $self->{'gd_obj'}->string ($font, $x2, $y2, $labels[$_], $color);
604                
605               }
606              
607             # mark off the used space
608 2         23   $self->{'curr_x_max'} -= $width;
609              
610             # and return
611 2         35   return;
612             }
613              
614             ## draw the legend at the left of the data plot
615             sub _draw_left_legend {
616 1     1   11   my $self = shift;
617 1         9   my @labels = @{$self->{'legend_labels'}};
  1         13  
618 1         10   my ($x1, $x2, $x3, $y1, $y2, $width, $color, $misccolor, $w, $h);
619 1         8   my $yh; # for boxing legend
620 1         10   my $font = $self->{'legend_font'};
621 1         10   my (%colors, @datasets, $i);
622 1         11   my $max_legend_example=0;
623              
624             # copy the current boundaries and colors into the sub-objects
625 1         11   $self->_sub_update;
626               
627             # init the legend_example height
628 1         11   $self->_legend_example_height_init;
629             # # modify the dataset color table entries to avoid duplicating
630             # # dataset colors (this limits the number of possible data sets
631             # # for each component to 8)
632             # for (0..7) {
633             # $self->{'sub_1'}{'color_table'}{'dataset'.$_}
634             # = $self->{'color_table'}{'dataset'.($_+8)};
635             # }
636             # modify the dataset color table entries to avoid duplicating
637             # dataset colors.
638 1         9   my ($n0, $n1) = map { scalar @{ $self->{'composite_info'}[$_][1] } } 0..1;
  2         17  
  2         23  
639 1         13   for (0..$n1-1) {
640 2         29     $self->{'sub_1'}{'color_table'}{'dataset'.$_}
641                   = $self->{'color_table'}{'dataset'.($_+$n0)};
642               }
643              
644             # make sure we use the right colors for the legend
645 1         10   @datasets = @{$self->{'composite_info'}[0][1]};
  1         11  
646 1         10   $i = 0;
647 1         12   for (0..$#datasets) {
648 2         25     $colors{$datasets[$_]-1} = $self->{'color_table'}{'dataset'.($i)};
649 2         19     $i++;
650               }
651 1         10   @datasets = @{$self->{'composite_info'}[1][1]};
  1         12  
652 1         10   $i = 0;
653 1         1031   for (0..$#datasets) {
654 2         45     $colors{$datasets[$_]-1} = $self->{'color_table'}{'dataset'.($i+$n0)};
655 2         22     $i++;
656               }
657              
658             # make sure we're using a real font
659 1 50       18   unless ((ref ($font)) eq 'GD::Font') {
660 0         0     croak "The subtitle font you specified isn\'t a GD Font object";
661               }
662              
663             # get the size of the font
664 1         24   ($h, $w) = ($font->height, $font->width);
665              
666             # get the miscellaneous color
667 1         227   $misccolor = $self->_color_role_to_index('misc');
668              
669             # find out how wide the largest label is
670 1         14   $width = (2 * $self->{'text_space'})
671                 + ($self->{'max_legend_label'} * $w)
672                 + $self->{'legend_example_size'}
673                 + (2 * $self->{'legend_space'});
674              
675             # get some base x-y coordinates
676 1         9   $x1 = $self->{'curr_x_min'};
677 1         10   $x2 = $self->{'curr_x_min'} + $width;
678 1         11   $y1 = $self->{'curr_y_min'} + $self->{'graph_border'} ;
679 1         16   $y2 = $self->{'curr_y_min'} + $self->{'graph_border'} + $self->{'text_space'
680             }
681                       + ($self->{'num_datasets'} * ($h + $self->{'text_space'}))
682                       + (2 * $self->{'legend_space'});
683              
684             # box the legend off
685 1         106   $self->{'gd_obj'}->rectangle ($x1, $y1, $x2, $y2, $misccolor);
686              
687             # leave that nice space inside the legend box
688 1         10   $x1 += $self->{'legend_space'};
689 1         11   $y1 += $self->{'legend_space'} + $self->{'text_space'};
690              
691             # now draw the actual legend
692 1         14   for (0..$#labels) {
693             # get the color
694 4         39     $color = $colors{$_};
695                
696             # find the max_legend_example
697 4         33     $max_legend_example = $self->{'legend_space'} + $h;
698                 
699             # find the x-y coords
700 4         34     $x2 = $x1;
701 4         33     $x3 = $x2 + $self->{'legend_example_size'};
702 4         37     $y2 = $y1 + ($_ * ($self->{'text_space'} + $h)) + $h/2;
703              
704             # draw the example line if legend_example_height==1 or ==0
705 4 50       50 if ($self->{'legend_example_height'.$_}<$max_legend_example) {
706 4         38 $yh = $self->{'legend_example_height'.$_};
707             }
708             else {
709 0         0 $yh = $max_legend_example;
710             }   
711 4 50       38 if ($yh <= 1) {
712 4         162       $self->{'gd_obj'}->line ($x2, $y2, $x3, $y2, $color);
713             } else {
714             # draw the example bar if legend_example_height > 1
715 0         0 $yh = int($yh / 2);
716 0         0       $self->{'gd_obj'}->filledRectangle ($x2, $y2-$yh, $x3, $y2+$yh, $color);
717             }
718              
719             # now the label
720 4         37     $x2 = $x3 + (2 * $self->{'text_space'});
721 4         34     $y2 -= $h/2;
722 4         111     $self->{'gd_obj'}->string ($font, $x2, $y2, $labels[$_], $color);
723               }
724              
725             # mark off the used space
726 1         10   $self->{'curr_x_min'} += $width;
727              
728             # and return
729 1         16   return 1;
730             }
731              
732              
733             ## draw the legend on the bottom of the data plot
734             sub _draw_bottom_legend {
735 3     3   32   my $self = shift;
736 3         213   my @labels = @{$self->{'legend_labels'}};
  3         44  
737 3         30   my ($x1, $y1, $x2, $y2, $empty_width, $max_label_width, $cols, $rows, $color);
738 3         28   my ($col_width, $row_height, $i, $j, $r, $c, $index, $x, $y, $sub, $w, $h);
739 3         28   my ($yh,$yi); # for boxing legend
740 3         150   my $font = $self->{'legend_font'};
741 3         38   my (%colors, @datasets);
742 3         27   my $max_legend_example=0;
743 3         27   $yh=0;
744              
745             # copy the current boundaries and colors into the sub-objects
746 3         38   $self->_sub_update;
747             # init the legend example height
748 3         35   $self->_legend_example_height_init;
749               
750             # # modify the dataset color table entries to avoid duplicating
751             # # dataset colors (this limits the number of possible data sets
752             # # for each component to 8)
753             # for (0..7) {
754             # $self->{'sub_1'}{'color_table'}{'dataset'.$_}
755             # = $self->{'color_table'}{'dataset'.($_+8)};
756             # }
757             # modify the dataset color table entries to avoid duplicating
758             # dataset colors.
759 3         33   my ($n0, $n1) = map { scalar @{ $self->{'composite_info'}[$_][1] } } 0..1;
  6         52  
  6         75  
760 3         34   for (0..$n1-1) {
761 7         102     $self->{'sub_1'}{'color_table'}{'dataset'.$_}
762                   = $self->{'color_table'}{'dataset'.($_+$n0)};
763               }
764              
765 3         30   @datasets = @{$self->{'composite_info'}[0][1]};
  3         38  
766 3         30   $i = 0;
767 3         36   for (0..$#datasets) {
768 8         103     $colors{$datasets[$_]-1} = $self->{'color_table'}{'dataset'.($i)};
769 8         73     $i++;
770               }
771 3         30   @datasets = @{$self->{'composite_info'}[1][1]};
  3         35  
772 3         31   $i = 0;
773 3         33   for (0..$#datasets) {
774 7         89     $colors{$datasets[$_]-1} = $self->{'color_table'}{'dataset'.($i+$n0)};
775 7         556     $i++;
776               }
777              
778             # make sure we're using a real font
779 3 50       68   unless ((ref ($font)) eq 'GD::Font') {
780 0         0     croak "The subtitle font you specified isn\'t a GD Font object";
781               }
782              
783             # get the size of the font
784 3         68   ($h, $w) = ($font->height, $font->width);
785               
786              
787             # figure out how many columns we can fit
788 3         88   $x1 = $self->{'curr_x_min'} + $self->{'graph_border'}
789                       + $self->{'y_tick_label_length1'} * $self->{'tick_label_font'}->width
790             + $self->{'tick_len'} + (3 * $self->{'text_space'});
791 3         50   $x2 = $self->{'curr_x_max'} - $self->{'graph_border'}
792                       - $self->{'y_tick_label_length2'} * $self->{'tick_label_font'}->width
793             - $self->{'tick_len'} - (3 * $self->{'text_space'});
794 3 100       39   if ($self->{'y_label'}) {
795 2         29     $x1 += $self->{'label_font'}->height + 2 * $self->{'text_space'};
796               }
797 3 100       39   if ($self->{'y_label2'}) {
798 1         14     $x2 -= $self->{'label_font'}->height + 2 * $self->{'text_space'};
799               }
800 3         32   $empty_width = $x2 - $x1 - (2 * $self->{'legend_space'});
801 3         46   $max_label_width = $self->{'max_legend_label'}
802                 * $self->{'legend_font'}->width + 4 * $self->{'text_space'}
803                 + $self->{'legend_example_size'};
804 3         33   $cols = int ($empty_width / $max_label_width);
805 3 50       71   unless ($cols) {
806 0         0     $cols = 1;
807               }
808 3         29   $col_width = $empty_width / $cols;
809              
810             # figure out how many rows we need
811 3         31   $rows = int ($self->{'num_datasets'} / $cols);
812 3 100       39   unless (($self->{'num_datasets'} % $cols) == 0) {
813 2         18     $rows++;
814                 }
815 3 50       34   unless ($rows) {
816 0         0     $rows = 1;
817               }
818 3         29   $row_height = $h + $self->{'text_space'};
819              
820             # box it off
821 3         34   $y1 = $self->{'curr_y_max'} - $self->{'text_space'}
822                       - ($rows * $row_height) - (2 * $self->{'legend_space'});
823 3         29   $y2 = $self->{'curr_y_max'};
824 3         50   $self->{'gd_obj'}->rectangle($x1, $y1, $x2, $y2,
825                                            $self->_color_role_to_index('misc'));
826            
827             # get the max_legend_example_height
828 3         28   $max_legend_example = $y2-$y1;
829              
830 3         74   $x1 += $self->{'legend_space'} + $self->{'text_space'};
831 3         33   $x2 -= $self->{'legend_space'};
832 3         30   $y1 += $self->{'legend_space'} + $self->{'text_space'};
833 3         30   $y2 -= $self->{'legend_space'} + $self->{'text_space'};
834              
835             # draw in the actual legend
836 3         29   $r = 0;
837 3         26   $c = 0;
838 3         27   $yi= 0; # current dataset
839 3         32     for $i (0..1) {
840 6         51       for $j (0..$#{$self->{'component_datasets'}[$i]}) {
  6         77  
841 15         179       $color = $self->{'sub_'.$i}->{'color_table'}{'dataset'.$j};
842 15         137       $index = $self->{'component_datasets'}[$i][$j] - 1;
843              
844 15         133       $x = $x1 + ($col_width * $c);
845 15         135       $y = $y1 + ($row_height * $r) + $h/2;
846            
847             # draw the example line if legend_example_height==1 or ==0
848 15 100       317 if ($rows == 1) {
849 6 50       67 if ($self->{'legend_example_height'.$yi} < $max_legend_example) {
850 6         60 $yh = $self->{'legend_example_height'.$yi};
851             }
852             else {
853 0         0 $yh = $max_legend_example;
854             }
855             }
856             else {
857 9 100       97 if ($self->{'legend_example_height'.$yi} < $row_height) {
858 5         50 $yh = $self->{'legend_example_height'.$yi};
859             }
860             else {
861 4         35 $yh = $row_height;
862             }
863                      }
864 15         120 $yi++;
865 15 100       136 if ($yh <= 1) {
866 2         55           $self->{'gd_obj'}->line ($x, $y,
867             $x + $self->{'legend_example_size'}, $y,
868             $color);
869             } 
870             else {
871             # draw the example bar if legend_example_height > 1
872 13         109 $yh = int($yh / 2);
873 13         339         $self->{'gd_obj'}->filledRectangle ($x, $y-$yh,
874             $x + $self->{'legend_example_size'}, $y+$yh,
875             $color);
876                }
877              
878 15         138       $x += $self->{'legend_example_size'} + 2 * $self->{'text_space'};
879 15         127       $y -= $h/2;
880 15         497       $self->{'gd_obj'}->string($font, $x, $y,
881                                             $labels[$index], $color);
882              
883             # keep track of which row/column we're using
884 15         124       $r = ($r + 1) % $rows;
885 15 100       151       if ($r == 0) {
886 7         71 $c++;
887                   }
888                 }
889               }
890             # mark of the space used
891 3         38   $self->{'curr_y_max'} -= ($rows * $row_height)
892                + $self->{'text_space'}
893             + 2 * $self->{'legend_space'};
894              
895 3         52   return;
896             }
897              
898             # no legend to draw.. just update the color tables for subs
899             sub _draw_none_legend {
900 0     0   0   my $self = shift;
901              
902 0         0   $self->_sub_update();
903              
904             # for (0..7) {
905             # $self->{'sub_1'}{'color_table'}{'dataset'.$_}
906             # = $self->{'color_table'}{'dataset'.($_+8)};
907             # }
908             # modify the dataset color table entries to avoid duplicating
909             # dataset colors.
910 0         0   my ($n0, $n1) = map { scalar @{ $self->{'composite_info'}[$_][1] } } 0..1;
  0         0  
  0         0  
911 0         0   for (0..$n1-1) {
912 0         0     $self->{'sub_1'}{'color_table'}{'dataset'.$_}
913                   = $self->{'color_table'}{'dataset'.($_+$n0)};
914               }
915             }
916              
917              
918             ## draw the ticks and tick labels
919             sub _draw_ticks {
920 8     8   141   my $self = shift;
921              
922             # draw the x ticks
923 8         98   $self->_draw_x_ticks;
924              
925             # update the boundaries in the sub-objects
926 8         105   $self->_boundary_update ($self, $self->{'sub_0'});
927 8         98   $self->_boundary_update ($self, $self->{'sub_1'});
928              
929             # now the y ticks
930 8         97   $self->_draw_y_ticks;
931              
932             # then return
933 8         88   return;
934             }
935              
936              
937             ## draw the x-ticks and their labels
938             sub _draw_x_ticks {
939 8     8   138   my $self = shift;
940 8         111   my $data = $self->{'dataref'};
941 8         84   my $font = $self->{'tick_label_font'};
942 8         102   my $textcolor = $self->_color_role_to_index('text');
943 8         104   my $misccolor = $self->_color_role_to_index('misc');
944 8         79   my ($h, $w);
945 8         76   my ($x1, $x2, $y1, $y2);
946 8         71   my ($width, $delta);
947 8         73   my ($stag);
948              
949 8         178   $self->{'grid_data'}->{'x'} = [];
950              
951             # make sure we got a real font
952 8 50       163   unless ((ref $font) eq 'GD::Font') {
953 0         0     croak "The tick label font you specified isn\'t a GD Font object";
954               }
955              
956             # get the height and width of the font
957 8         179   ($h, $w) = ($font->height, $font->width);
958              
959             # allow for the amount of space the y-ticks will push the
960             # axes over to the right and to the left
961             ## _draw_y_ticks allows 3 * text_space, not 2 * ; this caused mismatch between
962             ## the ticks (and grid lines) and the data.
963             # $x1 = $self->{'curr_x_min'} + ($w * $self->{'y_tick_label_length1'})
964             # + (2 * $self->{'text_space'}) + $self->{'tick_len'};
965             # $x2 = $self->{'curr_x_max'} - ($w * $self->{'y_tick_label_length2'})
966             # - (2 * $self->{'text_space'}) - $self->{'tick_len'};
967 8         113   $x1 = $self->{'curr_x_min'} + ($w * $self->{'y_tick_label_length1'})
968                      + (3 * $self->{'text_space'}) + $self->{'tick_len'};
969 8         94   $x2 = $self->{'curr_x_max'} - ($w * $self->{'y_tick_label_length2'})
970                      - (3 * $self->{'text_space'}) - $self->{'tick_len'};
971 8         82   $y1 = $self->{'curr_y_max'} - $h - $self->{'text_space'};
972              
973             # get the delta value, figure out how to draw the labels
974 8         72   $width = $x2 - $x1;
975 8 50       108   $delta = $width / ( $self->{'num_datapoints'} > 0 ? $self->{'num_datapoints'} : 1 );
976 8 100       115   if ($delta <= ($self->{'x_tick_label_length'} * $w)) {
977 1 50       18     unless ($self->{'x_ticks'} =~ /^vertical$/i) {
978 0         0       $self->{'x_ticks'} = 'staggered';
979                 }
980               }
981              
982             # now draw the labels
983 8 100       136   if ($self->{'x_ticks'} =~ /^normal$/i) { # normal ticks
    50          
    50          
984 5 50       67     if ($self->{'skip_x_ticks'}) {
    50          
985 0         0       for (0..int(($self->{'num_datapoints'}-1)/$self->{'skip_x_ticks'})) {
986 0         0         $x2 = $x1 + ($delta/2) + ($delta*($_*$self->{'skip_x_ticks'}))
987             - ($w*length( $self->{'f_x_tick'}->($data->[0][$_* $self->{'skip_x_ticks'}]))) / 2;
988 0         0         $self->{'gd_obj'}->string($font, $x2, $y1,
989             $self->{'f_x_tick'}->($data->[0][$_*$self->{'skip_x_ticks'}]),
990             $textcolor);
991                   }
992                 }
993                 elsif ($self->{'custom_x_ticks'}) {
994 0         0       for (@{$self->{'custom_x_ticks'}}) {
  0         0  
995 0         0         $x2 = $x1 + ($delta/2) + ($delta*$_) - ($w*length( $self->{'f_x_tick'}->($data->[0][$_]))) / 2;
996 0         0         $self->{'gd_obj'}->string($font, $x2, $y1,
997                                               $self->{'f_x_tick'}->($data->[0][$_]), $textcolor);
998                   }
999                 }
1000                 else {
1001 5         59       for (0..$self->{'num_datapoints'}-1) {
1002 28         422         $x2 = $x1 + ($delta/2) + ($delta*$_) - ($w*length($self->{'f_x_tick'}->($data->[0][$_]))) / 2;
1003 28         419         $self->{'gd_obj'}->string($font, $x2, $y1, $self->{'f_x_tick'}->($data->[0][$_]), $textcolor);
1004                   }
1005                 }
1006               }
1007               elsif ($self->{'x_ticks'} =~ /^staggered$/i) { # staggered ticks
1008 0 0       0     if ($self->{'skip_x_ticks'}) {
    0          
1009 0         0       $stag = 0;
1010 0         0       for (0..int(($self->{'num_datapoints'}-1)/$self->{'skip_x_ticks'})) {
1011 0         0         $x2 = $x1 + ($delta/2) + ($delta*($_*$self->{'skip_x_ticks'}))
1012             - ($w*length( $self->{'f_x_tick'}->($data->[0][$_*$self->{'skip_x_ticks'}]))) / 2;
1013 0 0       0         if (($stag % 2) == 1) {
1014 0         0           $y1 -= $self->{'text_space'} + $h;
1015                     }
1016 0         0         $self->{'gd_obj'}->string($font, $x2, $y1,
1017                                               $self->{'f_x_tick'}->($data->[0][$_*$self->{'skip_x_ticks'}]),
1018                                               $textcolor);
1019 0 0       0         if (($stag % 2) == 1) {
1020 0         0           $y1 += $self->{'text_space'} + $h;
1021                     }
1022 0         0 $stag++;
1023                   }
1024                 }
1025                 elsif ($self->{'custom_x_ticks'}) {
1026 0         0       $stag = 0;
1027 0         0       for (sort (@{$self->{'custom_x_ticks'}})) {
  0         0  
1028 0         0         $x2 = $x1 + ($delta/2) + ($delta*$_) - ($w*length( $self->{'f_x_tick'}->($data->[0][$_]))) / 2;
1029 0 0       0         if (($stag % 2) == 1) {
1030 0         0           $y1 -= $self->{'text_space'} + $h;
1031                     }
1032 0         0         $self->{'gd_obj'}->string($font, $x2, $y1, $self->{'f_x_tick'}->($data->[0][$_]), $textcolor);
1033 0 0       0         if (($stag % 2) == 1) {
1034 0         0           $y1 += $self->{'text_space'} + $h;
1035                     }
1036 0         0 $stag++;
1037                   }
1038                 }
1039                 else {
1040 0         0       for (0..$self->{'num_datapoints'}-1) {
1041 0         0         $x2 = $x1 + ($delta/2) + ($delta*$_) - ($w*length( $self->{'f_x_tick'}->($data->[0][$_]))) / 2;
1042 0 0       0         if (($_ % 2) == 1) {
1043 0         0           $y1 -= $self->{'text_space'} + $h;
1044                     }
1045 0         0         $self->{'gd_obj'}->string($font, $x2, $y1, $self->{'f_x_tick'}->($data->[0][$_]), $textcolor);
1046 0 0       0         if (($_ % 2) == 1) {
1047 0         0           $y1 += $self->{'text_space'} + $h;
1048                     }
1049                   }
1050                 }
1051               }
1052               elsif ($self->{'x_ticks'} =~ /^vertical$/i) { # vertical ticks
1053 3         30     $y1 = $self->{'curr_y_max'} - $self->{'text_space'};
1054 3 50 33     49     if ( defined($self->{'skip_x_ticks'}) && $self->{'skip_x_ticks'} > 1) {
    50          
1055 0         0       for (0..int(($self->{'num_datapoints'}-1)/$self->{'skip_x_ticks'})) {
1056 0         0         $x2 = $x1 + ($delta/2) + ($delta*($_*$self->{'skip_x_ticks'})) - $h/2;
1057 0         0         $y2 = $y1 - (($self->{'x_tick_label_length'}
1058             - length( $self->{'f_x_tick'}->($data->[0][$_*$self->{'skip_x_ticks'}]))) * $w);
1059 0         0         $self->{'gd_obj'}->stringUp($font, $x2, $y2,
1060                                                 $self->{'f_x_tick'}->($data->[0][$_*$self->{'skip_x_ticks'}]),
1061             $textcolor);
1062                   }
1063                 }
1064                 elsif ($self->{'custom_x_ticks'}) {
1065 0         0       for (@{$self->{'custom_x_ticks'}}) {
  0         0  
1066 0         0         $x2 = $x1 + ($delta/2) + ($delta*$_) - $h/2;
1067 0         0         $y2 = $y1 - (($self->{'x_tick_label_length'} - length( $self->{'f_x_tick'}->($data->[0][$_])))
1068                                   * $w);
1069 0         0         $self->{'gd_obj'}->stringUp($font, $x2, $y2,
1070                                                 $self->{'f_x_tick'}->($data->[0][$_]), $textcolor);
1071                   }
1072                 }
1073                 else {
1074 3         38       for (0..$self->{'num_datapoints'}-1) {
1075 32         473         $x2 = $x1 + ($delta/2) + ($delta*$_) - $h/2;
1076 32         414         $y2 = $y1 - (($self->{'x_tick_label_length'} - length( $self->{'f_x_tick'}->($data->[0][$_])))
1077                                   * $w);
1078 32         387         $self->{'gd_obj'}->stringUp($font, $x2, $y2,
1079             $self->{'f_x_tick'}->($data->[0][$_]), $textcolor);
1080                   }
1081                 }
1082               }
1083               else { # error time
1084 0         0     carp "I don't understand the type of x-ticks you specified";
1085               }
1086              
1087             # update the current y-max value
1088 8 100       166   if ($self->{'x_ticks'} =~ /^normal$/i) {
    50          
    50          
1089 5         54      $self->{'curr_y_max'} -= $h + (2 * $self->{'text_space'});
1090               }
1091               elsif ($self->{'x_ticks'} =~ /^staggered$/i) {
1092 0         0     $self->{'curr_y_max'} -= (2 * $h) + (3 * $self->{'text_space'});
1093               }
1094               elsif ($self->{'x_ticks'} =~ /^vertical$/i) {
1095 3         35     $self->{'curr_y_max'} -= ($w * $self->{'x_tick_label_length'})
1096                                            + (2 * $self->{'text_space'});
1097               }
1098              
1099             # now plot the ticks
1100 8         75   $y1 = $self->{'curr_y_max'};
1101 8         83   $y2 = $self->{'curr_y_max'} - $self->{'tick_len'};
1102 8 50       121   if ($self->{'skip_x_ticks'}) {
    50          
1103 0         0     for (0..int(($self->{'num_datapoints'}-1)/$self->{'skip_x_ticks'})) {
1104 0         0       $x2 = $x1 + ($delta/2) + ($delta*($_*$self->{'skip_x_ticks'}));
1105 0         0       $self->{'gd_obj'}->line($x2, $y1, $x2, $y2, $misccolor);
1106 0 0 0     0       if ($self->{'grid_lines'} =~ /^true$/i
1107                     or $self->{'x_grid_lines'} =~ /^true$/i) {
1108 0         0         $self->{'grid_data'}->{'x'}->[$_] = $x2;
1109                   }
1110                 }
1111               }
1112               elsif ($self->{'custom_x_ticks'}) {
1113 0         0     for (@{$self->{'custom_x_ticks'}}) {
  0         0  
1114 0         0       $x2 = $x1 + ($delta/2) + ($delta*$_);
1115 0         0       $self->{'gd_obj'}->line($x2, $y1, $x2, $y2, $misccolor);
1116 0 0 0     0       if ($self->{'grid_lines'} =~ /^true$/i
1117                     or $self->{'x_grid_lines'} =~ /^true$/i) {
1118 0         0         $self->{'grid_data'}->{'x'}->[$_] = $x2;
1119                   }
1120                 }
1121               }
1122               else {
1123 8         94     for (0..$self->{'num_datapoints'}-1) {
1124 60         527       $x2 = $x1 + ($delta/2) + ($delta*$_);
1125 60         984       $self->{'gd_obj'}->line($x2, $y1, $x2, $y2, $misccolor);
1126 60 100 66     872       if ($self->{'grid_lines'} =~ /^true$/i
1127                     or $self->{'x_grid_lines'} =~ /^true$/i) {
1128 6         80         $self->{'grid_data'}->{'x'}->[$_] = $x2;
1129                   }
1130                 }
1131               }
1132              
1133             # update the current y-max value
1134 8         2035   $self->{'curr_y_max'} -= $self->{'tick_len'};
1135              
1136             # and return
1137 8         89   return;
1138             }
1139              
1140              
1141             ## draw the y-ticks and their labels
1142             sub _draw_y_ticks {
1143 8     8   157   my $self = shift;
1144              
1145             # let the first guy do his
1146 8         213   $self->{'sub_0'}->_draw_y_ticks ('left');
1147              
1148             # and update the other two objects
1149 8         112   $self->_boundary_update ($self->{'sub_0'}, $self);
1150 8         96   $self->_boundary_update ($self->{'sub_0'}, $self->{'sub_1'});
1151              
1152             # now draw the other ones
1153 8         180   $self->{'sub_1'}->_draw_y_ticks ('right');
1154              
1155             # and update the other two objects
1156 8         105   $self->_boundary_update ($self->{'sub_1'}, $self);
1157 8         92