File Coverage

blib/lib/Chart/HorizontalBars.pm
Criterion Covered Total %
statement 255 284 89.8
branch 73 112 65.2
condition 7 17 41.2
subroutine 8 8 100.0
pod n/a
total 343 421 81.5


line stmt bran cond sub pod time code
1             #====================================================================
2             # Chart::HorizontalBars
3             #
4             # written by Chart-Group
5             #
6             # maintained by the Chart Group
7             # Chart@wettzell.ifag.de
8             #
9             #---------------------------------------------------------------------
10             # History:
11             #----------
12             # $RCSfile: HorizontalBars.pm,v $ $Revision: 1.2 $ $Date: 2003/02/14 14:04:40 $
13             # $Author: dassing $
14             # $Log: HorizontalBars.pm,v $
15             # Revision 1.2 2003/02/14 14:04:40 dassing
16             # First setup
17             #
18             #====================================================================
19              
20             package Chart::HorizontalBars;
21              
22 4     4   139 use Chart::Base 2.3;
  4         105  
  4         76  
23 4     4   98 use GD;
  4         38  
  4         83  
24 4     4   71 use Carp;
  4         38  
  4         66  
25 4     4   60 use strict;
  4         37  
  4         60  
26              
27             @Chart::HorizontalBars::ISA = qw(Chart::Base);
28             $Chart::HorizontalBars::VERSION = '2.3';
29              
30             #>>>>>>>>>>>>>>>>>>>>>>>>>>#
31             # public methods go here #
32             #<<<<<<<<<<<<<<<<<<<<<<<<<<#
33              
34              
35              
36             #>>>>>>>>>>>>>>>>>>>>>>>>>>>#
37             # private methods go here #
38             #<<<<<<<<<<<<<<<<<<<<<<<<<<<#
39             #draw x_ticks and their labels
40             sub _draw_x_ticks {
41 4     4   39  my $self = shift;
42 4         43  my $data = $self->{'dataref'};
43 4         44  my $font = $self->{'tick_label_font'};
44 4         50  my $textcolor = $self->_color_role_to_index('text');
45 4         52  my $misccolor = $self->_color_role_to_index('misc');
46 4         43  my ($h, $w, $x1, $y1, ,$y2, $x2, $delta, $width, $label);
47 4         37  my @labels = @{$self->{'y_tick_labels'}};
  4         122  
48              
49 4         86  $self->{'grid_data'}->{'x'} = [];
50              
51             #make sure we have a real font
52 4 50       53  unless ((ref $font) eq 'GD::Font') {
53 0         0   croak "The tick label font you specified isn't a GD font object";
54              }
55              
56             #get height and width of the font
57 4         66  ($h, $w) = ($font->height, $font->width);
58              
59             #get the right x-value and width
60 4 100       139  if ( $self->{'y_axes'} =~ /^right$/i ){
    100          
61 1         10   $x1 = $self->{'curr_x_min'};
62 1         13   $width = $self->{'curr_x_max'} - $x1 -$self->{'tick_len'} - $self->{'text_space'}
63                        - $w * $self->{'x_tick_label_length'};
64              }
65              elsif ( $self->{'y_axes'} =~ /^both$/i) {
66 2         68   $x1 = $self->{'curr_x_min'} + $self->{'text_space'} + $w* $self->{'x_tick_label_length'}
67                     + $self->{'tick_len'};
68 2         25   $width = $self->{'curr_x_max'} - $x1 - $self->{'tick_len'} - $self->{'text_space'}
69                        - $w * $self->{'x_tick_label_length'};
70              }
71              else {
72 1         12   $x1 = $self->{'curr_x_min'} + $self->{'text_space'} + $w* $self->{'x_tick_label_length'}
73                     + $self->{'tick_len'};
74 1         10   $width = $self->{'curr_x_max'} - $x1;
75              }
76              
77             #get the delta value
78 4         41  $delta = $width / ($self->{'y_ticks'} -1) ;
79              
80             #draw the labels
81 4         37  $y2 =$y1;
82              
83 4 100       59  if ($self->{'x_ticks'} =~ /^normal/i ) { #just normal ticks
    100          
    50          
84             #get the point for updating later
85 2         47    $y1 = $self->{'curr_y_max'} - 2*$self->{'text_space'} -$h - $self->{'tick_len'};
86             #get the start point
87 2         21    $y2 = $y1 + $self->{'tick_len'} + $self->{'text_space'};
88 2         26    for (0..$#labels){
89 27         244      $label = $self->{'y_tick_labels'}[$_];
90 27         250      $x2 = $x1 + ($delta * $_) - ($w* length( $label)/2) ;
91 27         380      $self->{'gd_obj'}->string($font, $x2, $y2 , $label , $textcolor);
92                }
93              }
94              elsif ($self->{'x_ticks'} =~ /^staggered/i ) { #staggered ticks
95             #get the point for updating later
96 1         11    $y1 = $self->{'curr_y_max'} - 3*$self->{'text_space'} - 2*$h - $self->{'tick_len'};
97              
98 1         13    for (0..$#labels) {
99 6         58    $label = $self->{'y_tick_labels'}[$_];
100 6         167      $x2 = $x1 + ($delta * $_) - ($w* length( $label)/2);
101 6 100       58      unless ($_%2) {
102 3         33       $y2 = $y1 + $self->{'text_space'} + $self->{'tick_len'};
103 3         49        $self->{'gd_obj'}->string($font, $x2, $y2 , $label, $textcolor);
104                  }
105                  else {
106 3         32      $y2 = $y1 + $h + 2*$self->{'text_space'} + $self->{'tick_len'};
107 3         68        $self->{'gd_obj'}->string($font, $x2, $y2 , $label, $textcolor);
108                  }
109                }
110              
111              }
112              
113              elsif ($self->{'x_ticks'} =~ /^vertical/i ) { #vertical ticks
114             #get the point for updating later
115 1         11    $y1 = $self->{'curr_y_max'} - 2*$self->{'text_space'} -$w* $self->{'y_tick_label_length'} - $self->{'tick_len'};
116              
117              
118 1         15    for (0..$#labels){
119 11         113      $label = $self->{'y_tick_labels'}[$_];
120             #get the start point
121 11         105      $y2 = $y1 + $self->{'tick_len'} + $w* length($label) + $self->{'text_space'};
122              
123 11         128      $x2 = $x1 + ($delta * $_) - ($h /2);
124 11         329      $self->{'gd_obj'}->stringUp($font, $x2, $y2 , $label , $textcolor);
125                }
126              
127              }
128              
129              else {
130 0         0   carp "I don't understand the type of x-ticks you specified";
131              }
132             #update the curr x and y max value
133 4         45  $self->{'curr_y_max'} = $y1;
134 4         41  $self->{'curr_x_max'} = $x1 + $width;
135              
136             #draw the ticks
137 4         67  $y1 =$self->{'curr_y_max'};
138 4         42  $y2 =$self->{'curr_y_max'} + $self->{'tick_len'};
139 4         46  for(0..$#labels ) {
140 44         379    $x2 = $x1 + ($delta * $_);
141 44         692    $self->{'gd_obj'}->line($x2, $y1, $x2, $y2, $misccolor);
142 44 50 66     557      if (($self->{'grid_lines'} =~ /^true$/i) or ($self->{'x_grid_lines'} =~ /^true$/i)) {
143 44         858         $self->{'grid_data'}->{'x'}->[$_] = $x2;
144                  }
145              }
146              
147 4         92  return 1;
148             }
149              
150              
151              
152             sub _draw_y_ticks {
153 4     4   41   my $self = shift;
154 4   50     53   my $side = shift || 'left';
155 4         88   my $data = $self->{'dataref'};
156 4         44   my $font = $self->{'tick_label_font'};
157 4         53   my $textcolor = $self->_color_role_to_index ('text');
158 4         49   my $misccolor = $self->_color_role_to_index ('misc');
159 4         47   my ($h, $w, $x1, $x2, $y1, $y2);
160 4         39   my ($width, $height, $delta);
161              
162 4         53   $self->{'grid_data'}->{'y'} =[];
163               
164             #make sure that is a real font
165 4 50       57   unless ((ref $font) eq 'GD::Font') {
166 0         0     croak "The tick label font isn't a GD Font object!";
167               }
168              
169             #get the size of the font
170 4         144   ($h, $w) = ($font->height, $font->width);
171              
172             #figure out, where to draw
173 4 100       64   if ($side =~ /^right$/i) {
    100          
174             #get the right startposition
175 1         11     $x1 = $self->{'curr_x_max'};
176 1         12     $y1 = $self->{'curr_y_max'} - $h/2;
177                 
178             #get the delta values
179 1         11     $height = $self->{'curr_y_max'} - $self->{'curr_y_min'} ;
180 1 50       14     $delta = ($height) / ($self->{'num_datapoints'} > 0 ? $self->{'num_datapoints'} : 1);
181 1         10     $y1 -= ($delta/2 );
182              
183             #look if skipping is desired
184 1 50       13     if (!defined($self->{'skip_y_ticks'})) {
185 0         0        $self->{'skip_y_ticks'} =1;
186                 }
187                 
188             #draw the labels
189 1         13     for(0.. int (($self->{'num_datapoints'} - 1) / $self->{'skip_y_ticks'})) {
190 5         48        $y2 = $y1 - ($delta) * ($_ * $self->{'skip_y_ticks'});
191 5         45        $x2 = $x1 + $self->{'tick_len'} + $self->{'text_space'};
192 5         64        $self->{'gd_obj'}->string($font, $x2, $y2,
193                                           $self->{f_y_tick}->($data->[0][$_*$self->{'skip_y_ticks'}]), $textcolor);
194                 }
195                 
196             #draw the ticks
197 1         10     $x1 = $self->{'curr_x_max'};
198 1         10     $x2 = $self->{'curr_x_max'} + $self->{'tick_len'};
199 1         10     $y1 += $h/2;
200 1         12     for(0..($self->{'num_datapoints'} -1 / $self->{'skip_y_ticks'})) {
201 5         42            $y2 = $y1 - ($delta * $_);
202 5         64            $self->{'gd_obj'}->line($x1,$y2,$x2,$y2,$misccolor);
203 5 50 33     75            if ($self->{'grid_lines'} =~ /^true$/i or $self->{'x_grid_lines'} =~ /^true$/i ) {
204 5         53               $self->{'grid_data'}->{'y'}->[$_] = $y2;
205                        }
206                 }
207                 
208               }
209               elsif ($side =~ /^both$/i) {
210             #get the right startposition
211 2         22     $x1 = $self->{'curr_x_max'};
212 2         70     $y1 = $self->{'curr_y_max'} - $h/2;
213              
214             #get the delta values
215 2         22     $height = $self->{'curr_y_max'} - $self->{'curr_y_min'} ;
216 2 50       26     $delta = ($height) / ($self->{'num_datapoints'} > 0 ? $self->{'num_datapoints'} : 1);
217 2         20     $y1 -= ($delta/2 );
218              
219             #look if skipping is desired
220 2 50       24     if (!defined($self->{'skip_y_ticks'})) {
221 0         0        $self->{'skip_y_ticks'} =1;
222                 }
223              
224             #first draw the right labels
225 2         28     for(0.. int (($self->{'num_datapoints'} - 1) / $self->{'skip_y_ticks'})) {
226 17         157        $y2 = $y1 - ($delta) * ($_ * $self->{'skip_y_ticks'});
227 17         154        $x2 = $x1 + $self->{'tick_len'} + $self->{'text_space'};
228 17         206        $self->{'gd_obj'}->string($font, $x2, $y2,
229                                           $self->{f_y_tick}->($data->[0][$_*$self->{'skip_y_ticks'}]), $textcolor);
230                 }
231              
232             #then draw the right ticks
233 2         21     $x1 = $self->{'curr_x_max'};
234 2         21     $x2 = $self->{'curr_x_max'} + $self->{'tick_len'};
235 2         327     $y1 += $h/2;
236 2         27     for(0..($self->{'num_datapoints'} -1 / $self->{'skip_y_ticks'})) {
237 17         147            $y2 = $y1 - ($delta * $_);
238 17         228            $self->{'gd_obj'}->line($x1,$y2,$x2,$y2,$misccolor);
239 17 50 33     595            if ($self->{'grid_lines'} =~ /^true$/i or $self->{'x_grid_lines'} =~ /^true$/i ) {
240 17         191               $self->{'grid_data'}->{'y'}->[$_] = $y2;
241                        }
242                 }
243              
244             #get the right startposition
245 2         26     $x1 = $self->{'curr_x_min'} ;
246 2         67     $y1 = $self->{'curr_y_max'} -$h/2 ;
247              
248             #get the delta values for positioning
249 2         22     $height = $self->{'curr_y_max'} - $self->{'curr_y_min'} ;
250 2 50       28     $delta = ($height) / ($self->{'num_datapoints'} > 0 ? $self->{'num_datapoints'} : 1);
251 2         21     $y1 -= ($delta/2 );
252              
253             #then draw the left labels
254 2         29     for(0.. int (($self->{'num_datapoints'} - 1) / $self->{'skip_y_ticks'})) {
255 17         156        $y2 = $y1 - ($delta) * ($_ * $self->{'skip_y_ticks'});
256 17         202        $x2 = $x1 - $w * length($self->{f_y_tick}->($data->[0][$_*$self->{'skip_y_ticks'}])) #print the Labels right-sided
257                          + $w * $self->{'x_tick_label_length'};
258 17         224        $self->{'gd_obj'}->string($font, $x2, $y2,
259                                           $self->{f_y_tick}->($data->[0][$_*$self->{'skip_y_ticks'}]), $textcolor);
260                 }
261              
262             #update the curr_x_min val
263 2         28     $self->{'curr_x_min'} = $x1 + $self->{'text_space'} + $w* $self->{'x_tick_label_length'}
264                                        + $self->{'tick_len'};
265              
266             #finally draw the left ticks
267 2         288     $x1 = $self->{'curr_x_min'};
268 2         21     $x2 = $self->{'curr_x_min'} - $self->{'tick_len'};
269 2         21     $y1 += $h/2;
270 2         25     for(0..($self->{'num_datapoints'} -1 / $self->{'skip_y_ticks'})) {
271 17         145            $y2 = $y1 - ($delta * $_);
272 17         249            $self->{'gd_obj'}->line($x1,$y2,$x2,$y2,$misccolor);
273 17 50 33     226            if ($self->{'grid_lines'} =~ /^true$/i or $self->{'x_grid_lines'} =~ /^true$/i ) {
274 17         167               $self->{'grid_data'}->{'y'}->[$_] = $y2;
275                        }
276                 }
277               }
278              
279               else {
280             #get the right startposition
281 1         10     $x1 = $self->{'curr_x_min'} ;
282 1         52     $y1 = $self->{'curr_y_max'} -$h/2 ;
283              
284             #get the delta values for positioning
285 1         11     $height = $self->{'curr_y_max'} - $self->{'curr_y_min'} ;
286 1 50       13     $delta = ($height) / ($self->{'num_datapoints'} > 0 ? $self->{'num_datapoints'} : 1);
287 1         10     $y1 -= ($delta/2 );
288               
289 1 50       12     if (!defined($self->{'skip_y_ticks'})) {
290 0         0        $self->{'skip_y_ticks'} =1;
291                 }
292              
293             #draw the labels
294 1         14     for(0.. int (($self->{'num_datapoints'} - 1) / $self->{'skip_y_ticks'})) {
295 5         48        $y2 = $y1 - ($delta) * ($_ * $self->{'skip_y_ticks'});
296 5         63        $x2 = $x1 - $w * length($self->{f_y_tick}->($data->[0][$_*$self->{'skip_y_ticks'}])) #print the Labels right-sided
297                          + $w * $self->{'x_tick_label_length'};
298 5         63        $self->{'gd_obj'}->string($font, $x2, $y2,
299                                           $self->{f_y_tick}->($data->[0][$_*$self->{'skip_y_ticks'}]), $textcolor);
300                 }
301                 
302             #update the curr_x_min val
303 1         14     $self->{'curr_x_min'} = $x1 + $self->{'text_space'} + $w* $self->{'x_tick_label_length'}
304                                        + $self->{'tick_len'};
305               
306             #draw the ticks
307 1         10     $x1 = $self->{'curr_x_min'};
308 1         10     $x2 = $self->{'curr_x_min'} - $self->{'tick_len'};
309 1         9     $y1 += $h/2;
310 1         13     for(0..($self->{'num_datapoints'} -1 / $self->{'skip_y_ticks'})) {
311 5         44            $y2 = $y1 - ($delta * $_);
312 5         64            $self->{'gd_obj'}->line($x1,$y2,$x2,$y2,$misccolor);
313 5 50 33     62            if ($self->{'grid_lines'} =~ /^true$/i or $self->{'x_grid_lines'} =~ /^true$/i ) {
314 5         118               $self->{'grid_data'}->{'y'}->[$_] = $y2;
315                        }
316                 }
317               }
318             #now return
319 4         53   return 1;
320             }
321              
322             #overwrite the find_y_scale function, only to get the right f_x_ticks !!!!!
323             sub _find_y_scale
324             {
325 4     4   41 my $self = shift;
326              
327             # Predeclare vars.
328 4         77 my ($d_min, $d_max); # Dataset min & max.
329 4         38 my ($p_min, $p_max); # Plot min & max.
330 4         37 my ($tickInterval, $tickCount);
331 4         35 my @tickLabels; # List of labels for each tick.
332 4         38 my $maxtickLabelLen = 0; # The length of the longest tick label.
333              
334             # Find the datatset minimum and maximum.
335 4         76 ($d_min, $d_max) = $self->_find_y_range();
336              
337             # Force the inclusion of zero if the user has requested it.
338 4 100       92 if( $self->{'include_zero'} =~ m!^true$!i )
339             {
340 2 50       533 if( ($d_min * $d_max) > 0 ) # If both are non zero and of the same sign.
341             {
342 2 100       54 if( $d_min > 0 ) # If the whole scale is positive.
343             {
344 1         10 $d_min = 0;
345             }
346             else # The scale is entirely negative.
347             {
348 1         12 $d_max = 0;
349             }
350             }
351             }
352              
353 4 50       60         if ( $self->{'integer_ticks_only'} =~ /^\d$/ ) {
354 0 0       0            if ( $self->{'integer_ticks_only'} == 1 ) {
355 0         0               $self->{'integer_ticks_only'} = 'true';
356                        } else {
357 0         0               $self->{'integer_ticks_only'} = 'false';
358                        }
359                     }
360 4 100       52 if( $self->{'integer_ticks_only'} =~ m!^true$!i )
361             {
362             # Allow the dataset range to be overidden by the user.
363             # f_min/max are booleans which indicate that the min & max should not be modified.
364 1         10 my $f_min = defined $self->{'min_val'};
365 1 50       12 $d_min = $self->{'min_val'} if $f_min;
366              
367 1         10 my $f_max = defined $self->{'max_val'};
368 1 50       12 $d_max = $self->{'max_val'} if $f_max;
369              
370             # Assert against the min is larger than the max.
371 1 50       12 if( $d_min > $d_max )
372             {
373 0         0 croak "The the specified 'min_val' & 'max_val' values are reversed (min > max: $d_min>$d_max)";
374             }
375             # The user asked for integer ticks, force the limits to integers.
376             # & work out the range directly.
377 1         16 $p_min = $self->_round2Tick($d_min, 1, -1);
378 1         37 $p_max = $self->_round2Tick($d_max, 1, 1);
379              
380 1         10 my $skip = $self->{skip_int_ticks};
381              
382 1         2228 $tickInterval = $skip;
383 1         17 $tickCount = ($p_max - $p_min ) /$skip +1;
384              
385             # Now sort out an array of tick labels.
386              
387             for( my $labelNum = $p_min; $labelNum<=$p_max; $labelNum+=$tickInterval )
388             {
389 21         164 my $labelText;
390              
391 21 50       200 if( defined $self->{f_x_tick} )
392             {
393             # Is _default_f_tick function used?
394 21 50       291                          if ( $self->{f_x_tick} == \&_default_f_tick) {
395 0         0 $labelText = sprintf("%d", $labelNum);
396                                      }
397             else {
398 21         230 $labelText = $self->{f_x_tick}->($labelNum);
399                                      }
400             }
401              
402             else
403             {
404 0         0 $labelText = sprintf("%d", $labelNum);
405             }
406              
407             #print "labelText = $labelText\n";
408 21         271 push @tickLabels, $labelText;
409 21 100       240 $maxtickLabelLen = length $labelText if $maxtickLabelLen < length $labelText;
410 1         10 }
411              
412             }
413             else
414             {
415             # Allow the dataset range to be overidden by the user.
416             # f_min/max are booleans which indicate that the min & max should not be modified.
417 3         90 my $f_min = defined $self->{'min_val'};
418 3 50       49 $d_min = $self->{'min_val'} if $f_min;
419              
420 3         35 my $f_max = defined $self->{'max_val'};
421 3 50       101 $d_max = $self->{'max_val'} if $f_max;
422              
423             # Assert against the min is larger than the max.
424 3 50       35 if( $d_min > $d_max )
425             {
426 0         0 croak "The the specified 'min_val' & 'max_val' values are reversed (min > max: $d_min>$d_max)";
427             }
428              
429             # Calculate the width of the dataset. (posibly modified by the user)
430 3         32 my $d_width = $d_max - $d_min;
431              
432             # If the width of the range is zero, forcibly widen it
433             # (to avoid division by zero errors elsewhere in the code).
434 3 50       36 if( 0 == $d_width )
435             {
436 0         0 $d_min--;
437 0         0 $d_max++;
438 0         0 $d_width = 2;
439             }
440              
441             # Descale the range by converting the dataset width into
442             # a floating point exponent & mantisa pair.
443 3         65              my( $rangeExponent, $rangeMantisa ) = $self->_sepFP( $d_width );
444 3         970 my $rangeMuliplier = 10 ** $rangeExponent;
445              
446             # Find what tick
447             # to use & how many ticks to plot,
448             # round the plot min & max to suatable round numbers.
449 3         3965 ($tickInterval, $tickCount, $p_min, $p_max)
450             = $self->_calcTickInterval($d_min/$rangeMuliplier, $d_max/$rangeMuliplier,
451             $f_min, $f_max,
452             $self->{'min_y_ticks'}, $self->{'max_y_ticks'});
453             # Restore the tickInterval etc to the correct scale
454 3         29 $_ *= $rangeMuliplier foreach($tickInterval, $p_min, $p_max);
  3         45  
455              
456             #get teh precision for the labels
457 3         36 my $precision = $self->{'precision'};
458              
459             # Now sort out an array of tick labels.
460             for( my $labelNum = $p_min; $labelNum<=$p_max; $labelNum+=$tickInterval )
461             {
462 23         181 my $labelText;
463              
464 23 50       220 if( defined $self->{f_x_tick} )
465             {
466             # Is _default_f_tick function used?
467 23 50       1227                         if ( $self->{f_x_tick} == \&_default_f_tick) {
468 0         0 $labelText = sprintf("%.".$precision."f", $labelNum);
469                                     } else {
470 23         556 $labelText = $self->{f_x_tick}->($labelNum);
471                                     }
472             }
473             else
474             {
475 0         0 $labelText = sprintf("%.".$precision."f", $labelNum);
476             }
477             #print "labelText = $labelText\n";
478 23         219 push @tickLabels, $labelText;
479 23 100       285 $maxtickLabelLen = length $labelText if $maxtickLabelLen < length $labelText;
480 3         27 }
481             }
482              
483             # Store the calculated data.
484 4         45 $self->{'min_val'} = $p_min;
485 4         43 $self->{'max_val'} = $p_max;
486 4         243 $self->{'y_ticks'} = $tickCount;
487 4         45 $self->{'y_tick_labels'} = \@tickLabels;
488 4         41 $self->{'y_tick_label_length'} = $maxtickLabelLen;
489              
490             # and return.
491 4         50 return 1;
492             }
493              
494              
495             ## finally get around to plotting the data
496             sub _draw_data {
497 4     4   39   my $self = shift;
498 4         68   my $data = $self->{'dataref'};
499 4         52   my $misccolor = $self->_color_role_to_index('misc');
500 4         38   my ($x1, $x2, $x3, $y1, $y2, $y3);
501 4         38   my $cut = 0;
502 4         36   my ($width, $height, $delta1, $delta2, $map, $mod, $pink);
503 4         36   my ($i, $j, $color);
504              
505             # init the imagemap data field if they wanted it
506 4 50       51   if ($self->{'imagemap'} =~ /^true$/i) {
507 0         0     $self->{'imagemap_data'} = [];
508               }
509              
510             # find both delta values ($delta1 for stepping between different
511             # datapoint names, $delta2 for setpping between datasets for that
512             # point) and the mapping constant
513 4         38   $width = $self->{'curr_x_max'} - $self->{'curr_x_min'};
514 4         41   $height = $self->{'curr_y_max'} - $self->{'curr_y_min'};
515 4 50       52   $delta1 = $height / ($self->{'num_datapoints'} > 0 ? $self->{'num_datapoints'} : 1);
516 4         42   $map = $width / ($self->{'max_val'} - $self->{'min_val'});
517 4 100       52   if ($self->{'spaced_bars'} =~ /^true$/i) {
518 3         32     $delta2 = $delta1 / ($self->{'num_datasets'} + 2);
519               }
520               else {
521 1         46     $delta2 = $delta1 / $self->{'num_datasets'};
522               }
523              
524             # get the base x-y values
525 4         40   $y1 = $self->{'curr_y_max'} - $delta2;
526 4 100       2329   if ($self->{'min_val'} >= 0) {
    100          
527 2         19     $x1 = $self->{'curr_x_min'} ;
528 2         52     $mod = $self->{'min_val'};
529               }
530               elsif ($self->{'max_val'} <= 0) {
531 1         9     $x1 = $self->{'curr_x_max'};
532 1         10     $mod = $self->{'max_val'};
533               }
534               else {
535 1         22    $x1 = $self->{'curr_x_min'} + abs($map * $self->{'min_val'});
536 1         12    $mod = 0;
537 1         46    $self->{'gd_obj'}->line ($x1, $self->{'curr_y_min'},
538                                         $x1, $self->{'curr_y_max'},
539                                        $misccolor);
540               }
541               
542             # draw the bars
543 4         67   for $i (1..$self->{'num_datasets'}) {
544             # get the color for this dataset
545 13         201     $color = $self->_color_role_to_index('dataset'.($i-1));
546                 
547             # draw every bar for this dataset
548 13         132     for $j (0..$self->{'num_datapoints'}) {
549             # don't try to draw anything if there's no data
550 113 100       1319       if (defined ($data->[$i][$j])) {
551             # find the bounds of the rectangle
552 100 100       1010         if ($self->{'spaced_bars'} =~ /^true$/i) {
553 90         971            $y2 = $y1 - ($j * $delta1) - ($self->{'num_datasets'} * $delta2) + (($i-1) * $delta2);
554             }
555             else {
556 10         100            $y2 = $y1 - ($j * $delta1) - ($self->{'num_datasets'} * $delta2) + (($i) * $delta2);
557             }
558 100         794 $x2 = $x1;
559 100         804 $y3 = $y2 + $delta2;
560              
561             #cut the bars off, if needed
562 100 50       1231         if ($data->[$i][$j] > $self->{'max_val'}) {
    50          
563 0         0            $x3 = $x1 + (($self->{'max_val'} - $mod ) * $map) -1;
564 0         0            $cut = 1;
565                     }
566                     elsif ($data->[$i][$j] < $self->{'min_val'}) {
567 0         0            $x3 = $x1 + (($self->{'min_val'} - $mod ) * $map) +1;
568 0         0            $cut = 1;
569                     }
570                     else {
571 100         972            $x3 = $x1 + (($data->[$i][$j] - $mod) * $map);
572 100         910            $cut = 0;
573                     }
574                     
575             # draw the bar
576             ## y2 and y3 are reversed in some cases because GD's fill
577             ## algorithm is lame
578 100 100       968 if ($data->[$i][$j] < 0) {
579 20         1250           $self->{'gd_obj'}->filledRectangle ($x3, $y2, $x2, $y3, $color);
580 20 50       211           if ($self->{'imagemap'} =~ /^true$/i) {
581 0         0 $self->{'imagemap_data'}->[$i][$j] = [$x3, $y2, $x2, $y3];
582                       }
583              
584 20         1151           $self->{'gd_obj'}->filledRectangle ($x3, $y2, $x2, $y3, $color);
585 20 50       204           if ($self->{'imagemap'} =~ /^true$/i) {
586 0         0               $self->{'imagemap_data'}->[$i][$j] = [$x3, $y2, $x2, $y3];
587                       }
588             }
589             else {
590 80         2305 $self->{'gd_obj'}->filledRectangle ($x2, $y2, $x3, $y3, $color);
591 80 50       1793 if ($self->{'imagemap'} =~ /^true$/i) {
592 0         0 $self->{'imagemap_data'}->[$i][$j] = [$x2, $y2, $x3, $y3];
593             }
594             }
595              
596             # now outline it. outline red if the bar had been cut off
597 100 50       898         unless ($cut){
598 100         3694 $self->{'gd_obj'}->rectangle ($x2, $y3, $x3, $y2, $misccolor);
599                     }
600                     else {
601 0         0           $pink = $self->{'gd_obj'}->colorAllocate(255,0,255);
602 0         0           $self->{'gd_obj'}->rectangle ($x2, $y3, $x3, $y2, $pink);
603                     }
604                     
605                   } else {
606 13 50       194 if ($self->{'imagemap'} =~ /^true$/i) {
607 0         0             $self->{'imagemap_data'}->[$i][$j] = [undef(), undef(), undef(), undef()];
608                       }
609                   }
610                 }
611               }
612                   
613             # and finaly box it off
614 4         228   $self->{'gd_obj'}->rectangle ($self->{'curr_x_min'},
615                $self->{'curr_y_min'},
616             $self->{'curr_x_max'},
617             $self->{'curr_y_max'},
618             $misccolor);
619 4         47   return;
620              
621             }
622              
623             ## be a good module and return 1
624             1;
625