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