File Coverage

blib/lib/Chart/Direction.pm
Criterion Covered Total %
statement 377 527 71.5
branch 117 202 57.9
condition 16 45 35.6
subroutine 15 17 88.2
pod n/a
total 525 791 66.4


line stmt bran cond sub pod time code
1             #====================================================================
2             # Chart::Direction
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: Direction.pm,v $ $Revision: 1.2 $ $Date: 2003/02/14 13:30:42 $
13             # $Author: dassing $
14             # $Log: Direction.pm,v $
15             # Revision 1.2 2003/02/14 13:30:42 dassing
16             # Circumvent division of zeros
17             #
18             #====================================================================
19              
20             package Chart::Direction;
21              
22 4     4   142 use Chart::Base 2.3;
  4         110  
  4         72  
23 4     4   84 use GD;
  4         39  
  4         74  
24 4     4   79 use Carp;
  4         35  
  4         66  
25 4     4   96 use strict;
  4         38  
  4         60  
26 4     4   180 use POSIX;
  4         42  
  4         92  
27              
28             @Chart::Direction::ISA = qw(Chart::Base);
29             $Chart::Direction::VERSION = '2.3';
30              
31             #>>>>>>>>>>>>>>>>>>>>>>>>>>#
32             # public methods go here #
33             #<<<<<<<<<<<<<<<<<<<<<<<<<<#
34              
35              
36              
37             #>>>>>>>>>>>>>>>>>>>>>>>>>>>#
38             # private methods go here #
39             #<<<<<<<<<<<<<<<<<<<<<<<<<<<#
40              
41             #we don't need a legend for this type.
42             #sub _draw_legend {
43              
44             # return 1;
45             #}
46              
47             # we use the find_y_scale methode to det the labels of the circles and the amount of them
48             sub _find_y_scale
49             {
50 4     4   41 my $self = shift;
51              
52             # Predeclare vars.
53 4         36 my ($d_min, $d_max); # Dataset min & max.
54 4         36 my ($p_min, $p_max); # Plot min & max.
55 4         72 my ($tickInterval, $tickCount, $skip);
56 4         36 my @tickLabels; # List of labels for each tick.
57 4         38 my $maxtickLabelLen = 0; # The length of the longest tick label.
58              
59             # Find the datatset minimum and maximum.
60 4         63 ($d_min, $d_max) = $self->_find_y_range();
61              
62             # Force the inclusion of zero if the user has requested it.
63 4 100       58 if( $self->{'include_zero'} =~ m!^true$!i )
64             {
65 2 50       24 if( ($d_min * $d_max) > 0 ) # If both are non zero and of the same sign.
66             {
67 2 50       35 if( $d_min > 0 ) # If the whole scale is positive.
68             {
69 2         42 $d_min = 0;
70             }
71             else # The scale is entirely negative.
72             {
73 0         0 $d_max = 0;
74             }
75             }
76             }
77              
78             # Allow the dataset range to be overidden by the user.
79             # f_min/max are booleans which indicate that the min & max should not be modified.
80 4         44 my $f_min = defined $self->{'min_val'};
81 4 50       47 $d_min = $self->{'min_val'} if $f_min;
82              
83 4         41 my $f_max = defined $self->{'max_val'};
84 4 50       45 $d_max = $self->{'max_val'} if $f_max;
85              
86             # Assert against the min is larger than the max.
87 4 50       46 if( $d_min > $d_max )
88             {
89 0         0 croak "The the specified 'min_val' & 'max_val' values are reversed (min > max: $d_min>$d_max)";
90             }
91              
92             # Calculate the width of the dataset. (posibly modified by the user)
93 4         1815 my $d_width = $d_max - $d_min;
94              
95             # If the width of the range is zero, forcibly widen it
96             # (to avoid division by zero errors elsewhere in the code).
97 4 50       100 if( 0 == $d_width )
98             {
99 0         0 $d_min--;
100 0         0 $d_max++;
101 0         0 $d_width = 2;
102             }
103              
104             # Descale the range by converting the dataset width into
105             # a floating point exponent & mantisa pair.
106 4         103              my( $rangeExponent, $rangeMantisa ) = $self->_sepFP( $d_width );
107 4         137 my $rangeMuliplier = 10 ** $rangeExponent;
108              
109             # Find what tick
110             # to use & how many ticks to plot,
111             # round the plot min & max to suatable round numbers.
112 4         75 ($tickInterval, $tickCount, $p_min, $p_max)
113             = $self->_calcTickInterval($d_min/$rangeMuliplier, $d_max/$rangeMuliplier,
114             $f_min, $f_max,
115             $self->{'min_circles'}+1, $self->{'max_circles'}+1);
116             # Restore the tickInterval etc to the correct scale
117 4         40 $_ *= $rangeMuliplier foreach($tickInterval, $p_min, $p_max);
  4         49  
118              
119             #get teh precision for the labels
120 4         44 my $precision = $self->{'precision'};
121              
122             # Now sort out an array of tick labels.
123             for( my $labelNum = $p_min; $labelNum<=$p_max; $labelNum+=$tickInterval )
124             {
125 28         809 my $labelText;
126              
127 28 50       371 if( defined $self->{f_y_tick} )
128             {
129             # Is _default_f_tick function used?
130 28 50       568                         if ( $self->{f_y_tick} == \&Chart::Base::_default_f_tick ) {
131 28         320 $labelText = sprintf("%.".$precision."f", $labelNum);
132 0         0                         } else { print \&_default_f_tick;
133 0         0 $labelText = $self->{f_y_tick}->($labelNum);
134                                     }
135             }
136             else
137             {
138 0         0 $labelText = sprintf("%.".$precision."f", $labelNum);
139             }
140 28         266 push @tickLabels, $labelText;
141 28 100       893 $maxtickLabelLen = length $labelText if $maxtickLabelLen < length $labelText;
142 4         38 }
143              
144             # Store the calculated data.
145 4         47 $self->{'min_val'} = $p_min;
146 4         93 $self->{'max_val'} = $p_max;
147 4         40 $self->{'y_ticks'} = $tickCount;
148 4         57 $self->{'y_tick_labels'} = \@tickLabels;
149 4         42 $self->{'y_tick_label_length'} = $maxtickLabelLen;
150              
151             # and return.
152 4         54 return 1;
153             }
154              
155             # Calculates the tick in normalised units.
156             sub _calcTickInterval
157 4     4   40 {       my $self = shift;
158             my(
159 4         66 $min, $max, # The dataset min & max.
160             $minF, $maxF, # Indicates if those min/max are fixed.
161             $minTicks, $maxTicks, # The minimum & maximum number of ticks.
162             ) = @_;
163              
164             # Verify the supplied 'min_y_ticks' & 'max_y_ticks' are sensible.
165 4 50       48 if( $minTicks < 2 )
166             {
167 0         0 print STDERR "Chart::Base : Incorrect value for 'min_circles', too small.\n";
168 0         0 $minTicks = 2;
169             }
170              
171 4 50       48 if( $maxTicks < 5*$minTicks )
172             {
173 0         0 print STDERR "Chart::Base : Incorrect value for 'max_circles', too small.\n";
174 0         0 $maxTicks = 5*$minTicks;
175             }
176              
177 4         43 my $width = $max - $min;
178 4         36 my @divisorList;
179              
180             for( my $baseMul = 1; ; $baseMul *= 10 )
181             {
182 4         43 TRY: foreach my $tryMul (1, 2, 5)
183             {
184             # Calc a fresh, smaller tick interval.
185 4         36 my $divisor = $baseMul * $tryMul;
186              
187             # Count the number of ticks.
188 4         80 my ($tickCount, $pMin, $pMax) = $self->_countTicks($min, $max, 1/$divisor);
189              
190             # Look a the number of ticks.
191 4 50       90 if( $maxTicks < $tickCount )
    50          
192             {
193             # If it is to high, Backtrack.
194 0         0 $divisor = pop @divisorList;
195             # just for security:
196 0 0 0     0                                 if ( !defined($divisor) || $divisor == 0 ) { $divisor = 1; }
  0         0  
197 0         0 ($tickCount, $pMin, $pMax) = $self->_countTicks($min, $max, 1/$divisor);
198 0         0 print "\nChart::Base : Caution: Tick limit of $maxTicks exceeded. Backing of to an interval of ".1/$divisor." which plots $tickCount ticks\n";
199 0         0 return(1/$divisor, $tickCount, $pMin, $pMax);
200             }
201             elsif( $minTicks > $tickCount )
202             {
203             # If it is to low, try again.
204 0         0 next TRY;
205             }
206             else
207             {
208             # Store the divisor for possible later backtracking.
209 4         40 push @divisorList, $divisor;
210              
211             # if the min or max is fixed, check they will fit in the interval.
212 4 50 33     65 next TRY if( $minF && ( int ($min*$divisor) != ($min*$divisor) ) );
213 4 50 33     80 next TRY if( $maxF && ( int ($max*$divisor) != ($max*$divisor) ) );
214              
215             # If everything passes the tests, return.
216 4         58 return(1/$divisor, $tickCount, $pMin, $pMax)
217             }
218             }
219 4         37 }
220 0         0 die "can't happen!";
221             }
222              
223             #this is where we draw the circles and the axes
224             sub _draw_y_ticks {
225 4     4   72   my $self = shift;
226 4         44   my $data = $self->{'dataref'};
227 4         53   my $misccolor = $self->_color_role_to_index('misc');
228 4         60   my $textcolor = $self->_color_role_to_index('text');
229 4         89   my $background = $self->_color_role_to_index('background');
230 4         39   my @labels = @{$self->{'y_tick_labels'}};
  4         101  
231 4         41   my ($width, $height, $centerX, $centerY, $diameter);
232 4         42   my ($pi, $font, $fontW, $fontH, $labelX, $labelY, $label_offset);
233 4         38   my ($dia_delta, $dia, $x, $y, @label_degrees, $arc, $angle_interval);
234              
235             # set up initial constant values
236 4         41   $pi = 3.14159265358979323846;
237 4         561   $font = $self->{'legend_font'};
238 4         75   $fontW = $self->{'legend_font'}->width;
239 4         56   $fontH = $self->{'legend_font'}->height;
240 4         40   $angle_interval = $self->{'angle_interval'};
241              
242 4 50       63   if ($self->{'grey_background'} =~ /^true$/i) {
243 0         0       $background = $self->_color_role_to_index('grey_background');
244               }
245             # init the imagemap data field if they wanted it
246 4 50       52   if ($self->{'imagemap'} =~ /^true$/i) {
247 0         0     $self->{'imagemap_data'} = [];
248               }
249              
250             # find width and height
251 4         46   $width = $self->{'curr_x_max'} - $self->{'curr_x_min'};
252 4         42   $height = $self->{'curr_y_max'} - $self->{'curr_y_min'};
253              
254             # find center point, from which the pie will be drawn around
255 4         52   $centerX = int($width/2 + $self->{'curr_x_min'});
256 4         48   $centerY = int($height/2 + $self->{'curr_y_min'});
257              
258             # always draw a circle, which means the diameter will be the smaller
259             # of the width and height. let enougth space for the label.
260 4 50       51   if ($width < $height) {
261 0         0    $diameter = $width -110;
262               }
263               else {
264 4         39     $diameter = $height -80 ;
265               }
266              
267             #the difference between the diameter of two following circles;
268 4         268   $dia_delta = ceil($diameter / ($self->{'y_ticks'}-1));
269              
270             #store the calculated data
271 4         742   $self->{'centerX'} = $centerX;
272 4         102   $self->{'centerY'} = $centerY;
273 4         44   $self->{'diameter'} = $diameter;
274              
275             #draw the axes and its labels
276             # set up an array of labels for the axes
277 4 50 33     168   if ($angle_interval == 0) {
    50 33        
    50 66        
    100 33        
    50 66        
    100 33        
    50 0        
    0          
278 0         0      @label_degrees = ( );
279               }
280               elsif ($angle_interval <= 5 && $angle_interval > 0) {
281 0         0      @label_degrees = qw(180 175 170 165 160 155 150 145 140 135 130 125 120 115
282             110 105 100 95 90 85 80 75 70 65 60 55 50 45 40 35 30 25 20 15 10 5 0 355 350
283             345 340 335 330 325 320 315 310 305 300 295 290 285 280 275 270 265 260 255
284             250 245 240 235 230 225 220 215 210 205 200 195 190 185);
285 0         0      $angle_interval = 5;
286               }
287               elsif ($angle_interval <= 10 && $angle_interval > 5) {
288 0         0      @label_degrees = qw(180 170 160 150 140 130 120 110 100 90 80 70 60 50 40
289             30 20 10 0 350 340 330 320 310 300 290 280 270 260 250 240 230 220 210 200 190);
290 0         0      $angle_interval = 10;
291               }
292               elsif ($angle_interval <= 15 && $angle_interval > 10) {
293 1         20      @label_degrees = qw(180 165 150 135 120 105 90 75 60 45 30 15 0 345 330 315 300
294             285 270 255 240 225 210 195);
295 1         9      $angle_interval = 15;
296               }
297               elsif ($angle_interval <=20 && $angle_interval > 15) {
298 0         0      @label_degrees = qw(180 160 140 120 100 80 60 40 20 0 340 320 300 280 260 240
299             220 200);
300 0         0      $angle_interval = 20;
301               }
302               elsif ($angle_interval <= 30 && $angle_interval > 20) {
303 1         310      @label_degrees = qw(180 150 120 90 60 30 0 330 300 270 240 210);
304 1         10      $angle_interval = 30;
305               }
306               elsif ($angle_interval <= 45 && $angle_interval > 30) {
307 2         29      @label_degrees = qw(180 135 90 45 0 315 270 225);
308 2         21      $angle_interval = 45;
309               }
310               elsif ($angle_interval <= 90 && $angle_interval > 45) {
311 0         0      @label_degrees = qw(180 90 0 270);
312 0         0      $angle_interval = 90;
313               }
314               else {
315 0         0      carp "The angle_interval must be between 0 and 90!\nCorrected value: 30";
316 0         0      @label_degrees = qw(180 150 120 90 60 30 0 330 300 270 240 210);
317 0         0      $angle_interval = 30;
318               }
319 4         38   $arc = 0;
320 4         404   foreach (@label_degrees) {
321             #calculated the coordinates of the end point of the line
322 52         670       $x = sin ($arc)*($diameter/2+10) + $centerX;
323 52         498       $y = cos ($arc)*($diameter/2+10) + $centerY;
324             #some ugly correcture
325 52 100       557       if ($_ == '270') { $y++;}
  4         40  
326             #draw the line
327 52         1102       $self->{'gd_obj'}->line($centerX, $centerY, $x, $y, $misccolor);
328             #calculate the string point
329 52         473       $x = sin ($arc)*($diameter/2+30) + $centerX-8;
330 52         465       $y = cos ($arc)*($diameter/2+28) + $centerY-6;
331             #draw the labels
332 52         939       $self->{'gd_obj'}->string($font, $x, $y, $_.'°', $textcolor);
333 52         483       $arc += (($angle_interval)/360) *2*$pi;
334               }
335                   
336             #draw the circles
337 4         44   $dia = 0;
338 4         40   foreach (@labels) {
339 28         3500       $self->{'gd_obj'}->arc($centerX,$centerY,
340                                 $dia, $dia,
341                                 0, 360,
342                                 $misccolor);
343 28         292       $dia += $dia_delta;
344               }
345               
346 4         285   $self->{'gd_obj'}->filledRectangle($centerX-length($labels[0])/2*$fontW-2,
347                                                  $centerY+2,
348                                                  $centerX+2+$diameter/2,
349                                                  $centerY+$fontH+2,
350                                                  $background);
351             #draw the labels of the circles
352 4         38   $dia = 0;
353 4         59   foreach (@labels) {
354 28         449        $self->{'gd_obj'}->string($font, $centerX+$dia/2-length($_)/2*$fontW,
355                                              $centerY+2, $_, $textcolor);
356 28         241        $dia += $dia_delta;
357               }
358                    
359               
360              
361 4         73   return;
362             }
363              
364             #We don't need x ticks, it's all done in _draw_y_ticks
365             sub _draw_x_ticks {
366 4     4   43   my $self = shift;
367              
368 4         48   return;
369             }
370              
371              
372             ## finally get around to plotting the data
373             sub _draw_data {
374 4     4   45   my $self = shift;
375 4         76   my $data = $self->{'dataref'};
376 4         55   my $misccolor = $self->_color_role_to_index('misc');
377 4         53   my $textcolor = $self->_color_role_to_index('text');
378 4         51   my $background = $self->_color_role_to_index('background');
379 4         39   my ($width, $height, $centerX, $centerY, $diameter);
380 4         37   my ($mod, $map, $i, $j, $brush, $color, $x, $y, $winkel, $first_x, $first_y );
381 4         36   my ($arrow_x, $arrow_y, $m);
382 4         37   $color = 1;
383              
384 4         39   my $pi = 3.14159265358979323846;
385 4         35   my $len = 10;
386 4         50   my $alpha = 1;
387 4         37   my $last_x = undef;
388 4         34   my $last_y = undef;
389 4         35   my $diff;
390 4         38   my $n=0;
391               
392               
393               
394               
395               
396 4 100       56   if ($self->{'pairs'} =~ /^true$/i) {
397 2         21      my $a = $self->{'num_datasets'}/2;
398 2         24      my $b = ceil($a);
399 2         19      my $c = $b-$a;
400                 
401 2 50       26      if ($c == 0) {
402 0         0      croak "Wrong number of datasets for 'pairs'";
403                  }
404                 }
405               
406              
407             # init the imagemap data field if they wanted it
408 4 50       52   if ($self->{'imagemap'} =~ /^true$/i) {
409 0         0     $self->{'imagemap_data'} = [];
410               }
411              
412             # find width and height
413 4         41   $width = $self->{'curr_x_max'} - $self->{'curr_x_min'};
414 4         42   $height = $self->{'curr_y_max'} - $self->{'curr_y_min'};
415               
416             # get the base values
417 4         55   $mod = $self->{'min_val'};
418 4         40   $centerX = $self->{'centerX'};
419 4         38   $centerY = $self->{'centerY'};
420 4         54   $diameter = $self->{'diameter'};
421 4         39   $diff = $self->{'max_val'} - $self->{'min_val'};
422 4 50       47   $diff = 1 if $diff < 1;
423 4         40   $map = $diameter/2/$diff;
424               
425               
426 4         50   $brush = $self->_prepare_brush ($color, 'point');
427 4         322   $self->{'gd_obj'}->setBrush ($brush);
428              
429              
430             # draw every line for this dataset
431               
432 4 100       65   if ($self->{'pairs'} =~ /^false$/i) {
433               
434 2         23     for $j (1..$self->{'num_datasets'}) {
435 6         97        $color = $self->_color_role_to_index('dataset'.($j-1));
436                  
437 6         67       for $i (0..$self->{'num_datapoints'}-1) {
438                        
439             # don't try to draw anything if there's no data
440 54 50 33     904       if (defined ($data->[$j][$i]) && $data->[$j][$i] <= $self->{'max_val'}
      33        
441                       && $data->[$j][$i] >= $self->{'min_val'}) {
442            
443             #calculate the point
444 54         558 $winkel = (180 - ($data->[0][$i] % 360)) /360 * 2* $pi;
445              
446 54         785         $x = ceil($centerX + sin ($winkel) * ($data->[$j][$i] - $mod) * $map);
447 54         581         $y = ceil($centerY + cos ($winkel) * ($data->[$j][$i] - $mod) * $map);
448              
449             # set the x and y values back
450 54 100       654 if ($i ==0) {
451 6         58 $first_x = $x;
452 6         93 $first_y = $y;
453 6         49 $last_x = $x;
454 6         53             $last_y = $y;
455             }
456            
457            
458 54 50       646         if ($self->{'point'} =~ /^true$/i) {
459 54         524           $brush = $self->_prepare_brush ($color, 'point');
460 54         990           $self->{'gd_obj'}->setBrush ($brush);
461             #draw the point
462 54         641           $self->{'gd_obj'}->line($x+1, $y, $x, $y, gdBrushed);
463                     }
464 54 100       598         if ($self->{'line'} =~ /^true$/i) {
465 27         252           $brush = $self->_prepare_brush ($color, 'line');
466 27         464           $self->{'gd_obj'}->setBrush ($brush);
467             #draw the line
468 27 50       281           if (defined $last_x) {
469 27         396 $self->{'gd_obj'}->line($x, $y, $last_x, $last_y, gdBrushed);
470                       }
471                       else {}
472            
473                     }
474            
475            
476 54 50       623         if ($self->{'arrow'} =~ /^true$/i) {
477 0         0           $brush = $self->_prepare_brush ($color, 'line');
478 0         0           $self->{'gd_obj'}->setBrush ($brush);
479             #draw the arrow
480 0 0       0           if ($data->[$j][$i] > $self->{'min_val'}) {
481 0         0             $self->{'gd_obj'}->line($x, $y, $centerX, $centerY, gdBrushed);
482              
483 0         0             $arrow_x = $x - cos($winkel-$alpha )*$len;
484 0         0             $arrow_y = $y + sin($winkel-$alpha)*$len;
485 0         0             $self->{'gd_obj'}->line($x, $y, $arrow_x, $arrow_y, gdBrushed);
486              
487 0         0             $arrow_x = $x + sin($pi/2-$winkel-$alpha )*$len;
488 0         0             $arrow_y = $y - cos($pi/2-$winkel-$alpha)*$len;
489 0         0             $self->{'gd_obj'}->line($x, $y, $arrow_x, $arrow_y, gdBrushed);
490              
491                         
492                       }
493                     }
494              
495 54         443         $last_x = $x;
496 54         1062         $last_y = $y;
497                     
498                     
499             # store the imagemap data if they asked for it
500 54 50       672 if ($self->{'imagemap'} =~ /^true$/i) {
501 0         0 $self->{'imagemap_data'}->[$j][$i] = [$x, $y ];
502               }
503                   } else {
504 0 0       0 if ($self->{'imagemap'} =~ /^true$/i) {
505 0         0 $self->{'imagemap_data'}->[$j][$i] = [ undef(), undef() ];
506              
507                     }
508                   }
509                 }
510                 
511             # draw the last line to the first point
512 6 100       208     if ($self->{'line'} =~ /^true$/i) {
513 3         31       $self->{'gd_obj'}->line($x, $y, $first_x, $first_y, gdBrushed);
514                   }
515                   
516                 }
517               }
518               
519              
520 4 100       77   if ($self->{'pairs'} =~ /^true$/i) {
521                  
522                  for ($j = 1; $j <= $self->{'num_datasets'}; $j+=2) {
523 7 100       69        if ($j ==1) {
524 2         1350          $color = $self->_color_role_to_index('dataset'.($j-1));
525             }
526                    else {
527 5         122          $color = $self->_color_role_to_index('dataset'.($j/2-0.5));
528                     }
529 7         81        for $i (0..$self->{'num_datapoints'}-1) {
530                       
531             # don't try to draw anything if there's no data
532 42 50 33     851       if (defined ($data->[$j][$i]) && $data->[$j][$i] <= $self->{'max_val'}
      33        
533                       && $data->[$j][$i] >= $self->{'min_val'}) {
534            
535             # calculate the point
536 42         418 $winkel = (180 - ($data->[$n][$i] % 360)) /360 * 2* $pi;
537              
538 42         476         $x = ceil($centerX + sin ($winkel) * ($data->[$j][$i] - $mod) * $map);
539 42         477         $y = ceil($centerY + cos ($winkel) * ($data->[$j][$i] - $mod) * $map);
540              
541             # set the x and y values back
542 42 100       390 if ($i ==0) {
543 7         60 $first_x = $x;
544 7         56 $first_y = $y;
545 7         58 $last_x = $x;
546 7         59             $last_y = $y;
547             }
548            
549            
550 42 50       430         if ($self->{'point'} =~ /^true$/i) {
551 0         0           $brush = $self->_prepare_brush ($color, 'point');
552 0         0           $self->{'gd_obj'}->setBrush ($brush);
553             #draw the point
554 0         0           $self->{'gd_obj'}->line($x+1, $y, $x, $y, gdBrushed);
555                     }
556 42 50       413         if ($self->{'line'} =~ /^true$/i) {
557 0         0           $brush = $self->_prepare_brush ($color, 'line');
558 0         0           $self->{'gd_obj'}->setBrush ($brush);
559             #draw the line
560 0 0       0           if (defined $last_x) {
561 0         0 $self->{'gd_obj'}->line($x, $y, $last_x, $last_y, gdBrushed);
562                       }
563                       else {}
564            
565                      }
566            
567            
568 42 50       460         if ($self->{'arrow'} =~ /^true$/i) {
569 42         442           $brush = $self->_prepare_brush ($color, 'line');
570 42         927           $self->{'gd_obj'}->setBrush ($brush);
571             #draw the arrow
572 42 50       523           if ($data->[$j][$i] > $self->{'min_val'}) {
573 42         457             $self->{'gd_obj'}->line($x, $y, $centerX, $centerY, gdBrushed);
574              
575 42         462             $arrow_x = $x - cos($winkel-$alpha )*$len;
576 42         428             $arrow_y = $y + sin($winkel-$alpha)*$len;
577 42         1827             $self->{'gd_obj'}->line($x, $y, $arrow_x, $arrow_y, gdBrushed);
578              
579 42         718             $arrow_x = $x + sin($pi/2-$winkel-$alpha )*$len;
580 42         1494             $arrow_y = $y - cos($pi/2-$winkel-$alpha)*$len;
581 42         483             $self->{'gd_obj'}->line($x, $y, $arrow_x, $arrow_y, gdBrushed);
582              
583                         
584                       }
585                     }
586              
587 42         367         $last_x = $x;
588 42         338         $last_y = $y;
589                     
590                     
591             # store the imagemap data if they asked for it
592 42 50       615 if ($self->{'imagemap'} =~ /^true$/i) {
593 0         0 $self->{'imagemap_data'}->[$j][$i] = [$x, $y ];
594               }
595                   } else {
596 0 0       0 if ($self->{'imagemap'} =~ /^true$/i) {
597 0         0 $self->{'imagemap_data'}->[$j][$i] = [ undef(), undef() ];
598              
599                     }
600                   }
601                 }
602                 
603             # draw the last line to the first point
604 7 50       124     if ($self->{'line'} =~ /^true$/i) {
605 0         0       $self->{'gd_obj'}->line($x, $y, $first_x, $first_y, gdBrushed);
606                   }
607 7         86       $n+=2;
608 2         19      }
609                  
610                }
611              
612             # now outline it
613 4         294    $self->{'gd_obj'}->rectangle ($self->{'curr_x_min'} ,
614                                              $self->{'curr_y_min'},
615                                              $self->{'curr_x_max'},
616                                              $self->{'curr_y_max'},
617                                              $misccolor);
618              
619 4         64   return;
620              
621             }
622              
623              
624             ## set the gdBrush object to trick GD into drawing fat lines
625             sub _prepare_brush {
626 133     133   1265   my $self = shift;
627 133         1185   my $color = shift;
628 133         3273   my $type = shift;
629 133         1298   my ($radius, @rgb, $brush, $white, $newcolor);
630                
631 133         1959   @rgb = $self->{'gd_obj'}->rgb($color);
632              
633             # get the appropriate brush size
634 133 100       1436   if ($type eq 'line') {
    50          
635 69         609     $radius = $self->{'brush_size'}/2;
636               }
637               elsif ($type eq 'point') {
638 64         672     $radius = $self->{'pt_size'}/2;
639               }
640              
641             # create the new image
642 133         1929   $brush = GD::Image->new ($radius*2, $radius*2);
643              
644             # get the colors, make the background transparent
645 133         7011   $white = $brush->colorAllocate (255,255,255);
646 133         2901   $newcolor = $brush->colorAllocate (@rgb);
647 133         1431   $brush->transparent ($white);
648              
649             # draw the circle
650 133         6176   $brush->arc ($radius-1, $radius-1, $radius, $radius, 0, 360, $newcolor);
651              
652             # fill it if we're using lines
653 133         2135   $brush->fill ($radius-1, $radius-1, $newcolor);
654               
655             #}
656              
657             # set the new image as the main object's brush
658 133         3868   return $brush;
659             }
660              
661              
662              
663             sub _draw_legend {
664 4     4   89   my $self = shift;
665 4         36   my ($length);
666              
667             # check to see if legend type is none..
668 4 100       60   if ($self->{'legend'} =~ /^none$/) {
669 2         39     return 1;
670               }
671             # check to see if they have as many labels as datasets,
672             # warn them if not
673 2 50 66     20   if (($#{$self->{'legend_labels'}} >= 0) &&
  2         33  
  1         15  
674                    ((scalar(@{$self->{'legend_labels'}})) != $self->{'num_datasets'})) {
675 0         0     carp "The number of legend labels and datasets doesn\'t match";
676               }
677              
678             # init a field to store the length of the longest legend label
679 2 50       25   unless ($self->{'max_legend_label'}) {
680 2         22     $self->{'max_legend_label'} = 0;
681               }
682              
683             # fill in the legend labels, find the longest one
684               
685 2 50       27   if ($self->{'pairs'} =~ /^false$/i) {
686 2         22   for (1..$self->{'num_datasets'}) {
687 6 100       82     unless ($self->{'legend_labels'}[$_-1]) {
688 3         35       $self->{'legend_labels'}[$_-1] = "Dataset $_";
689                 }
690 6         56     $length = length($self->{'legend_labels'}[$_-1]);
691 6 100       68     if ($length > $self->{'max_legend_label'}) {
692 2         21       $self->{'max_legend_label'} = $length;
693                 }
694               }
695               }
696               
697 2 50       25   if ($self->{'pairs'} =~ /^true$/i) {
698               
699 0         0    for (1..ceil($self->{'num_datasets'}/2)) {
700 0 0       0     unless ($self->{'legend_labels'}[$_-1]) {
701 0         0       $self->{'legend_labels'}[$_-1] = "Dataset $_";
702                 }
703 0         0     $length = length($self->{'legend_labels'}[$_-1]);
704 0 0       0     if ($length > $self->{'max_legend_label'}) {
705 0         0       $self->{'max_legend_label'} = $length;
706                 }
707               }
708              }
709                   
710             # different legend types
711 2 100       31   if ($self->{'legend'} eq 'bottom') {
    50          
    50          
    50          
712 1         12     $self->_draw_bottom_legend;
713               }
714               elsif ($self->{'legend'} eq 'right') {
715 0         0     $self->_draw_right_legend;
716               }
717               elsif ($self->{'legend'} eq 'left') {
718 0         0     $self->_draw_left_legend;
719               }
720               elsif ($self->{'legend'} eq 'top') {
721 1         12     $self->_draw_top_legend;
722               } else {
723 0         0     carp "I can't put a legend there (at ".$self->{'legend'}.")\n";
724               }
725              
726             # and return
727 2         29   return 1;
728             }
729              
730             ## put the legend on the bottom of the chart
731             sub _draw_bottom_legend {
732 1     1   9   my $self = shift;
733 1         9   my @labels = @{$self->{'legend_labels'}};
  1         13  
734 1         10   my ($x1, $y1, $x2, $x3, $y2, $empty_width, $max_label_width, $cols, $rows, $color, $brush);
735 1         10   my ($col_width, $row_height, $r, $c, $index, $x, $y, $w, $h, $axes_space);
736 1         10   my $font = $self->{'legend_font'};
737              
738              
739             # make sure we're using a real font
740 1 50       13   unless ((ref ($font)) eq 'GD::Font') {
741 0         0     croak "The subtitle font you specified isn\'t a GD Font object";
742               }
743              
744             # get the size of the font
745 1         17   ($h, $w) = ($font->height, $font->width);
746              
747             # find the base x values
748 1         17   $axes_space = ($self->{'y_tick_label_length'} * $self->{'tick_label_font'}->width)
749             + $self->{'tick_len'} + (3 * $self->{'text_space'});
750 1         11   $x1 = $self->{'curr_x_min'} + $self->{'graph_border'};
751 1         10   $x2 = $self->{'curr_x_max'} - $self->{'graph_border'};
752              
753 1 50       29   if ($self->{'y_axes'} =~ /^right$/i) {
    50          
754 0         0      $x2 -= $axes_space;
755               }
756               elsif ($self->{'y_axes'} =~ /^both$/i) {
757 0         0      $x2 -= $axes_space;
758             # $x1 += $axes_space;
759               }
760               else {
761             # $x1 += $axes_space;
762                  
763               }
764              
765              
766 1 50       12   if ($self->{'y_label'}) {
767 0         0     $x1 += $self->{'label_font'}->height + 2 * $self->{'text_space'};
768               }
769 1 50       12   if ($self->{'y_label2'}) {
770 0         0     $x2 -= $self->{'label_font'}->height + 2 * $self->{'text_space'};
771               }
772              
773             # figure out how wide the columns need to be, and how many we
774             # can fit in the space available
775 1         10   $empty_width = ($x2 - $x1) - (2 * $self->{'legend_space'});
776 1         11   $max_label_width = $self->{'max_legend_label'} * $w
777                 + (4 * $self->{'text_space'}) + $self->{'legend_example_size'};
778 1         10   $cols = int ($empty_width / $max_label_width);
779 1 50       13   unless ($cols) {
780 0         0     $cols = 1;
781               }
782 1         9   $col_width = $empty_width / $cols;
783              
784             # figure out how many rows we need, remember how tall they are
785 1         10   $rows = int ($self->{'num_datasets'} / $cols);
786 1 50       13   unless (($self->{'num_datasets'} % $cols) == 0) {
787 1         9     $rows++;
788               }
789 1 50       11   unless ($rows) {
790 0         0     $rows = 1;
791               }
792 1         9   $row_height = $h + $self->{'text_space'};
793              
794             # box the legend off
795 1         11   $y1 = $self->{'curr_y_max'} - $self->{'text_space'}
796                       - ($rows * $row_height) - (2 * $self->{'legend_space'});
797 1         10   $y2 = $self->{'curr_y_max'};
798              
799               
800 1         13   $self->{'gd_obj'}->rectangle($x1, $y1, $x2, $y2,
801                                            $self->_color_role_to_index('misc'));
802              
803 1         11   $x1 += $self->{'legend_space'} + $self->{'text_space'};
804 1         10   $x2 -= $self->{'legend_space'};
805 1         10   $y1 += $self->{'legend_space'} + $self->{'text_space'};
806 1         33   $y2 -= $self->{'legend_space'} + $self->{'text_space'};
807              
808             # draw in the actual legend
809 1         11   for $r (0..$rows-1) {
810 1         10     for $c (0..$cols-1) {
811 8         72       $index = ($r * $cols) + $c; # find the index in the label array
812 8 100       83       if ($labels[$index]) {
813             # get the color
814 3         43         $color = $self->_color_role_to_index('dataset'.$index);
815              
816             # get the x-y coordinate for the start of the example line
817 3         32 $x = $x1 + ($col_width * $c);
818 3         30         $y = $y1 + ($row_height * $r) + $h/2;
819            
820             # now draw the example line
821 3         74         $self->{'gd_obj'}->line($x, $y,
822                                             $x + $self->{'legend_example_size'}, $y,
823                                             $color);
824              
825             # reset the brush for points
826 3         46         $brush = $self->_prepare_brush($color, 'point',
827             $self->{'pointStyle' . $index});
828 3         115         $self->{'gd_obj'}->setBrush($brush);
829             # draw the point
830 3         53         $x3 = int($x + $self->{'legend_example_size'}/2);
831 3         46         $self->{'gd_obj'}->line($x3, $y, $x3, $y, gdBrushed);
832              
833             # adjust the x-y coordinates for the start of the label
834 3         34 $x += $self->{'legend_example_size'} + (2 * $self->{'text_space'});
835 3         28         $y = $y1 + ($row_height * $r);
836              
837             # now draw the label
838 3         75 $self->{'gd_obj'}->string($font, $x, $y, $labels[$index], $color);
839                   }
840                 }
841               }
842              
843             # mark off the space used
844 1         13   $self->{'curr_y_max'} -= ($rows * $row_height) + $self->{'text_space'}
845             + (2 * $self->{'legend_space'});
846              
847             # now return
848 1         17   return 1;
849             }
850              
851             ## put the legend on top of the chart
852             sub _draw_top_legend {
853 1     1   10   my $self = shift;
854 1         9   my @labels = @{$self->{'legend_labels'}};
  1         12  
855 1         10   my ($x1, $y1, $x2, $x3, $y2, $empty_width, $max_label_width, $cols, $rows, $color, $brush);
856 1         11   my ($col_width, $row_height, $r, $c, $index, $x, $y, $w, $h, $axes_space);
857 1         10   my $font = $self->{'legend_font'};
858              
859             # make sure we're using a real font
860 1 50       29   unless ((ref ($font)) eq 'GD::Font') {
861 0         0     croak "The subtitle font you specified isn\'t a GD Font object";
862               }
863              
864             # get the size of the font
865 1         16   ($h, $w) = ($font->height, $font->width);
866              
867             # find the base x values
868 1         17   $axes_space = ($self->{'y_tick_label_length'} * $self->{'tick_label_font'}->width)
869             + $self->{'tick_len'} + (3 * $self->{'text_space'});
870 1         9   $x1 = $self->{'curr_x_min'} + $self->{'graph_border'};
871 1         10   $x2 = $self->{'curr_x_max'} - $self->{'graph_border'};
872              
873 1 50       14   if ($self->{'y_axes'} =~ /^right$/i) {
    50          
874 0         0      $x2 -= $axes_space;
875               }
876               elsif ($self->{'y_axes'} =~ /^both$/i) {
877 0         0      $x2 -= $axes_space;
878             # $x1 += $axes_space;
879               }
880               else {
881             # $x1 += $axes_space;
882               }
883              
884             # figure out how wide the columns can be, and how many will fit
885 1         10   $empty_width = ($x2 - $x1) - (2 * $self->{'legend_space'});
886 1         11   $max_label_width = (4 * $self->{'text_space'})
887                 + ($self->{'max_legend_label'} * $w)
888                 + $self->{'legend_example_size'};
889 1         12   $cols = int ($empty_width / $max_label_width);
890 1 50       10   unless ($cols) {
891 0         0     $cols = 1;
892               }
893 1         10   $col_width = $empty_width / $cols;
894              
895             # figure out how many rows we need and remember how tall they are
896 1         10   $rows = int ($self->{'num_datasets'} / $cols);
897 1 50       13   unless (($self->{'num_datasets'} % $cols) == 0) {
898 1         10     $rows++;
899               }
900 1 50       10   unless ($rows) {
901 0         0     $rows = 1;
902               }
903 1         10   $row_height = $h + $self->{'text_space'};
904              
905             # box the legend off
906 1         11   $y1 = $self->{'curr_y_min'};
907 1         10   $y2 = $self->{'curr_y_min'} + $self->{'text_space'}
908                       + ($rows * $row_height) + (2 * $self->{'legend_space'});
909 1         14   $self->{'gd_obj'}->rectangle($x1, $y1, $x2, $y2,
910                                            $self->_color_role_to_index('misc'));
911              
912             # leave some space inside the legend
913 1         10   $x1 += $self->{'legend_space'} + $self->{'text_space'};
914 1         10   $x2 -= $self->{'legend_space'};
915 1         10   $y1 += $self->{'legend_space'} + $self->{'text_space'};
916 1         11   $y2 -= $self->{'legend_space'} + $self->{'text_space'};
917              
918             # draw in the actual legend
919 1         11   for $r (0..$rows-1) {
920 1         10     for $c (0..$cols-1) {
921 4         67       $index = ($r * $cols) + $c; # find the index in the label array
922 4 100       52       if ($labels[$index]) {
923             # get the color
924 3         38         $color = $self->_color_role_to_index('dataset'.$index);
925                     
926             # find the x-y coords
927 3         30 $x = $x1 + ($col_width * $c);
928 3         28         $y = $y1 + ($row_height * $r) + $h/2;
929              
930             # draw the line first
931 3         90         $self->{'gd_obj'}->line($x, $y,
932                                             $x + $self->{'legend_example_size'}, $y,
933                                             $color);
934              
935             # reset the brush for points
936 3         45         $brush = $self->_prepare_brush($color, 'point',
937             $self->{'pointStyle' . $index});
938 3         96         $self->{'gd_obj'}->setBrush($brush);
939             # draw the point
940 3         30         $x3 = int($x + $self->{'legend_example_size'}/2);
941 3         39         $self->{'gd_obj'}->line($x3, $y, $x3, $y, gdBrushed);
942              
943             # now the label
944 3         115 $x += $self->{'legend_example_size'} + (2 * $self->{'text_space'});
945 3         92 $y -= $h/2;
946 3         678 $self->{'gd_obj'}->string($font, $x, $y, $labels[$index], $color);
947                   }
948                 }
949               }
950                   
951             # mark off the space used
952 1         18   $self->{'curr_y_min'} += ($rows * $row_height) + $self->{'text_space'}
953             + 2 * $self->{'legend_space'};
954              
955             # now return
956 1         24   return 1;
957             }
958              
959              
960              
961             sub _draw_left_legend {
962 0     0   0   my $self = shift;
963 0         0   my @labels = @{$self->{'legend_labels'}};
  0         0  
964 0         0   my ($x1, $x2, $x3, $y1, $y2, $width, $color, $misccolor, $w, $h, $brush);
965 0         0   my $font = $self->{'legend_font'};
966              
967             # make sure we're using a real font
968 0 0       0   unless ((ref ($font)) eq 'GD::Font') {
969 0         0     croak "The subtitle font you specified isn\'t a GD Font object";
970               }
971              
972             # get the size of the font
973 0         0   ($h, $w) = ($font->height, $font->width);
974              
975             # get the miscellaneous color
976 0         0   $misccolor = $self->_color_role_to_index('misc');
977              
978             # find out how wide the largest label is
979 0         0   $width = (2 * $self->{'text_space'})
980                 + ($self->{'max_legend_label'} * $w)
981                 + $self->{'legend_example_size'}
982                 + (2 * $self->{'legend_space'});
983              
984             # get some base x-y coordinates
985 0         0   $x1 = $self->{'curr_x_min'};
986 0         0   $x2 = $self->{'curr_x_min'} + $width;
987 0         0   $y1 = $self->{'curr_y_min'} + $self->{'graph_border'} ;
988               
989 0 0       0   if ($self->{'pairs'} =~ /^true$/i) {
990 0         0   $y2 = $self->{'curr_y_min'} + $self->{'graph_border'} + $self->{'text_space'}
991                       + (($self->{'num_datasets'}/2) * ($h + $self->{'text_space'}))
992             + (4 * $self->{'legend_space'});
993                  }
994               else {
995 0         0   $y2 = $self->{'curr_y_min'} + $self->{'graph_border'} + $self->{'text_space'}
996                     + ($self->{'num_datasets'} * ($h + $self->{'text_space'}))
997             + (2 * $self->{'legend_space'});
998             }
999            
1000             # box the legend off
1001 0         0   $self->{'gd_obj'}->rectangle ($x1, $y1, $x2, $y2, $misccolor);
1002              
1003             # leave that nice space inside the legend box
1004 0         0   $x1 += $self->{'legend_space'};
1005 0         0   $y1 += $self->{'legend_space'} + $self->{'text_space'};
1006              
1007             # now draw the actual legend
1008 0         0   for (0..$#labels) {
1009             # get the color
1010 0         0     my $c = $self->{'num_datasets'}-$_-1;
1011 0         0     $color = $self->_color_role_to_index('dataset'.$_);
1012                 
1013             # find the x-y coords
1014 0         0     $x2 = $x1;
1015 0         0     $x3 = $x2 + $self->{'legend_example_size'};
1016 0         0     $y2 = $y1 + ($_ * ($self->{'text_space'} + $h)) + $h/2;
1017              
1018             # do the line first
1019 0         0     $self->{'gd_obj'}->line ($x2, $y2, $x3, $y2, $color);
1020              
1021             # reset the brush for points
1022 0         0     $brush = $self->_prepare_brush($color, 'point',
1023             $self->{'pointStyle' . $_});
1024 0         0     $self->{'gd_obj'}->setBrush($brush);
1025             # draw the point
1026 0         0     $self->{'gd_obj'}->line(int(($x3+$x2)/2), $y2,
1027             int(($x3+$x2)/2), $y2, gdBrushed);
1028                 
1029             # now the label
1030 0         0     $x2 = $x3 + (2 * $self->{'text_space'});
1031 0         0     $y2 -= $h/2;
1032                 
1033             # order of the datasets in the legend
1034 0         0     $self->{'gd_obj'}->string ($font, $x2, $y2, $labels[$_], $color);
1035                }
1036              
1037             # mark off the used space
1038 0         0   $self->{'curr_x_min'} += $width;
1039              
1040             # and return
1041 0         0   return 1;
1042             }
1043              
1044              
1045              
1046             sub _draw_right_legend {
1047 0     0   0   my $self = shift;
1048 0         0   my @labels = @{$self->{'legend_labels'}};
  0         0  
1049 0         0   my ($x1, $x2, $x3, $y1, $y2, $width, $color, $misccolor, $w, $h, $brush);
1050 0         0   my $font = $self->{'legend_font'};
1051              
1052             # make sure we're using a real font
1053 0 0       0   unless ((ref ($font)) eq 'GD::Font') {
1054 0         0     croak "The subtitle font you specified isn\'t a GD Font object";
1055               }
1056              
1057             # get the size of the font
1058 0         0   ($h, $w) = ($font->height, $font->width);
1059              
1060             # get the miscellaneous color
1061 0         0   $misccolor = $self->_color_role_to_index('misc');
1062              
1063             # find out how wide the largest label is
1064 0         0   $width = (2 * $self->{'text_space'})
1065                 + ($self->{'max_legend_label'} * $w)
1066                 + $self->{'legend_example_size'}
1067                 + (2 * $self->{'legend_space'});
1068              
1069             # get some starting x-y values
1070 0         0   $x1 = $self->{'curr_x_max'} - $width;
1071 0         0   $x2 = $self->{'curr_x_max'};
1072 0         0   $y1 = $self->{'curr_y_min'} + $self->{'graph_border'} ;
1073               
1074 0 0       0   if ($self->{'pairs'} =~ /^true$/i) {
1075 0         0   $y2 = $self->{'curr_y_min'} + $self->{'graph_border'} + $self->{'text_space'}
1076                       + (($self->{'num_datasets'}/2) * ($h + $self->{'text_space'}))
1077             + (4 * $self->{'legend_space'});
1078             }
1079               else {
1080 0         0   $y2 = $self->{'curr_y_min'} + $self->{'graph_border'} + $self->{'text_space'}
1081                       + ($self->{'num_datasets'} * ($h + $self->{'text_space'}))
1082             + (2 * $self->{'legend_space'});
1083             }
1084             # box the legend off
1085 0         0   $self->{'gd_obj'}->rectangle ($x1, $y1, $x2, $y2, $misccolor);
1086              
1087             # leave that nice space inside the legend box
1088 0         0   $x1 += $self->{'legend_space'};
1089 0         0   $y1 += $self->{'legend_space'} + $self->{'text_space'};
1090              
1091             # now draw the actual legend
1092 0         0   for (0..$#labels) {
1093             # get the color
1094 0         0     $color = $self->_color_role_to_index('dataset'.$_);
1095                
1096             # find the x-y coords
1097 0         0     $x2 = $x1;
1098 0         0     $x3 = $x2 + $self->{'legend_example_size'};
1099 0         0     $y2 = $y1 + ($_ * ($self->{'text_space'} + $h)) + $h/2;
1100              
1101             # do the line first
1102 0         0     $self->{'gd_obj'}->line ($x2, $y2, $x3, $y2, $color);
1103              
1104             # reset the brush for points
1105 0         0     $brush = $self->_prepare_brush($color, 'point',
1106             $self->{'pointStyle' . $_});
1107 0         0     $self->{'gd_obj'}->setBrush($brush);
1108             # draw the point
1109 0         0     $self->{'gd_obj'}->line(int(($x3+$x2)/2), $y2,
1110             int(($x3+$x2)/2), $y2, gdBrushed);
1111              
1112             # now the label
1113 0         0     $x2 = $x3 + (2 * $self->{'text_space'});
1114 0         0     $y2 -= $h/2;
1115                 
1116 0         0     $self->{'gd_obj'}->string ($font, $x2, $y2, $labels[$_], $color);
1117                
1118               }
1119              
1120             # mark off the used space
1121 0         0   $self->{'curr_x_max'} -= $width;
1122              
1123             # and return
1124 0         0   return 1;
1125             }
1126              
1127              
1128             sub _find_y_range {
1129 4     4   51   my $self = shift;
1130 4         53   my $data = $self->{'dataref'};
1131               
1132 4         39   my $max = undef;
1133 4         61   my $min = undef;
1134 4         37   my $k=1;
1135 4         38   my $dataset = 1;
1136 4         37   my $datum;
1137               
1138               
1139               
1140 4 100       56   if ($self->{'pairs'} =~ /^false$/i) {
1141 2         27   for $dataset ( @$data[1..$#$data] ) {
1142             # print "dataset @$dataset\n";
1143 6         53     for $datum ( @$dataset ) {
1144 54 50       583       if ( defined $datum ) {
1145             # Prettier, but probably slower:
1146             # $max = $datum unless defined $max && $max >= $datum;
1147             # $min = $datum unless defined $min && $min <= $datum;
1148 54 100       476         if ( defined $max ) {
1149 52 100       525           if ( $datum > $max ) { $max = $datum }
  9 100       78  
1150 2         20           elsif ( $datum < $min ) { $min = $datum }
1151                     }
1152 2         20         else { $min = $max = $datum }
1153                   }
1154                 }
1155               }
1156              }
1157              
1158 4 100       63  if ($self->{'pairs'} =~ /^true$/i) {
1159             # only every second dataset must be checked
1160 2         22  for $dataset ( @$data[$k] ) {
1161 2         19       for $datum ( @$dataset ) {
1162 12 50       135       if ( defined $datum ) {
1163             ## Prettier, but probably slower:
1164             # $max = $datum unless defined $max && $max >= $datum;
1165             # $min = $datum unless defined $min && $min <= $datum;
1166 12 100       110           if ( defined $max ) {
1167 10 100       118             if ( $datum > $max ) { $max = $datum }
  4 100       36  
1168 2         21           elsif ( $datum < $min ) { $min = $datum }
1169                       }
1170 2         19          else { $min = $max = $datum }
1171                    }
1172                 }
1173 2         23     $k+=2;
1174               }
1175              }
1176              
1177 4         49  ($min, $max);
1178             }
1179              
1180             ## be a good module and return 1
1181             1;
1182