1#========================================================================== 2# Copyright (c) 1995-1998 Martien Verbruggen 3#-------------------------------------------------------------------------- 4# 5# Name: 6# GD::Graph::bars.pm 7# 8# $Id: bars.pm,v 1.26 2007/04/26 03:16:09 ben Exp $ 9# 10#========================================================================== 11 12package GD::Graph::bars; 13 14($GD::Graph::bars::VERSION) = '$Revision: 1.26 $' =~ /\s([\d.]+)/; 15 16use strict; 17 18use GD::Graph::axestype; 19use GD::Graph::utils qw(:all); 20use GD::Graph::colour qw(:colours); 21 22@GD::Graph::bars::ISA = qw(GD::Graph::axestype); 23 24use constant PI => 4 * atan2(1,1); 25 26sub initialise 27{ 28 my $self = shift; 29 $self->SUPER::initialise(); 30 $self->set(correct_width => 1); 31} 32 33sub draw_data 34{ 35 my $self = shift; 36 37 $self->SUPER::draw_data() or return; 38 39 unless ($self->{no_axes}) 40 { 41 # redraw the 'zero' axis 42 if ($self->{rotate_chart}) 43 { 44 $self->{graph}->line( 45 $self->{zeropoint}, $self->{top}, 46 $self->{zeropoint}, $self->{bottom}, 47 $self->{fgci} ); 48 } 49 else 50 { 51 $self->{graph}->line( 52 $self->{left}, $self->{zeropoint}, 53 $self->{right}, $self->{zeropoint}, 54 $self->{fgci} ); 55 } 56 } 57 58 return $self; 59} 60 61sub _top_values 62{ 63 my $self = shift; 64 my @topvalues; 65 66 if ($self->{cumulate}) 67 { 68 my $data = $self->{_data}; 69 for my $i (0 .. $data->num_points - 1) 70 { 71 push @topvalues, $data->get_y_cumulative($data->num_sets, $i); 72 } 73 } 74 75 return \@topvalues; 76} 77 78# 79# Draw the shadow 80# 81sub _draw_shadow 82{ 83 my $self = shift; 84 my ($ds, $i, $value, $topvalues, $l, $t, $r, $b) = @_; 85 my $bsd = $self->{shadow_depth} or return; 86 my $bsci = $self->set_clr(_rgb($self->{shadowclr})); 87 88 if ($self->{cumulate}) 89 { 90 return if $ds > 1; 91 $value = $topvalues->[$i]; 92 if ($self->{rotate_chart}) 93 { 94 $r = ($self->val_to_pixel($i + 1, $value, $ds))[0]; 95 } 96 else 97 { 98 $t = ($self->val_to_pixel($i + 1, $value, $ds))[1]; 99 } 100 } 101 102 # XXX Clean this up 103 if ($value >= 0) 104 { 105 if ($self->{rotate_chart}) 106 { 107 $self->{graph}->filledRectangle( 108 $l, $t + $bsd, $r - $bsd, $b + $bsd, $bsci); 109 } 110 else 111 { 112 $self->{graph}->filledRectangle( 113 $l + $bsd, $t + $bsd, $r + $bsd, $b, $bsci); 114 } 115 } 116 else 117 { 118 if ($self->{rotate_chart}) 119 { 120 $self->{graph}->filledRectangle( 121 $l + $bsd, $t, $r + $bsd, $b, $bsci); 122 } 123 else 124 { 125 $self->{graph}->filledRectangle( 126 $l + $bsd, $b, $r + $bsd, $t + $bsd, $bsci); 127 } 128 } 129} 130 131sub draw_data_set_h 132{ 133 my $self = shift; 134 my $ds = shift; 135 136 my $bar_s = $self->{bar_spacing}/2; 137 138 # Pick a data colour 139 my $dsci = $self->set_clr($self->pick_data_clr($ds)); 140 # contrib "Bremford, Mike" <mike.bremford@gs.com> 141 my $brci = $self->set_clr($self->pick_border_clr($ds)); 142 143 my @values = $self->{_data}->y_values($ds) or 144 return $self->_set_error("Impossible illegal data set: $ds", 145 $self->{_data}->error); 146 147 my $topvalues = $self->_top_values; 148 # 149 # Draw all shadows. 150 for my $i (0 .. $#values) 151 { 152 my $value = $values[$i]; 153 next unless defined $value; 154 155 my $l = $self->_get_bottom($ds, $i); 156 my ($r, $xp) = $self->val_to_pixel($i + 1, $value, $ds); 157 158 # calculate top and bottom of bar 159 my ($t, $b); 160 my $window = $self->{x_step} - $self->{bargroup_spacing}; 161 162 if (ref $self eq 'GD::Graph::mixed' || $self->{overwrite}) 163 { 164 $t = $xp - $window/2 + $bar_s + 1; 165 $b = $xp + $window/2 - $bar_s; 166 } 167 else 168 { 169 $t = $xp 170 - $window/2 171 + ($ds - 1) * $window/$self->{_data}->num_sets 172 + $bar_s + 1; # GRANTM thinks this +1 should be conditional on bargroup_spacing being absent 173 $b = $xp 174 - $window/2 175 + $ds * $window/$self->{_data}->num_sets 176 - $bar_s; 177 } 178 179 $self->_draw_shadow($ds, $i, $value, $topvalues, $l, $t, $r, $b); 180 } 181 182 for my $i (0 .. $#values) 183 { 184 my $value = $values[$i]; 185 next unless defined $value; 186 187 my $l = $self->_get_bottom($ds, $i); 188 $value = $self->{_data}->get_y_cumulative($ds, $i) 189 if ($self->{cumulate}); 190 191 # CONTRIB Jeremy Wadsack 192 # 193 # cycle_clrs option sets the color based on the point, 194 # not the dataset. 195 $dsci = $self->set_clr($self->pick_data_clr($i + 1)) 196 if $self->{cycle_clrs}; 197 $brci = $self->set_clr($self->pick_data_clr($i + 1)) 198 if $self->{cycle_clrs} > 1; 199 200 # get coordinates of right and center of bar 201 my ($r, $xp) = $self->val_to_pixel($i + 1, $value, $ds); 202 203 # calculate top and bottom of bar 204 my ($t, $b); 205 my $window = $self->{x_step} - $self->{bargroup_spacing}; 206 207 if (ref $self eq 'GD::Graph::mixed' || $self->{overwrite}) 208 { 209 $t = $xp - $window/2 + $bar_s + 1; 210 $b = $xp + $window/2 - $bar_s; 211 } 212 else 213 { 214 $t = $xp 215 - $window/2 216 + ($ds - 1) * $window/$self->{_data}->num_sets 217 + $bar_s + 1; # GRANTM thinks this +1 should be conditional on bargroup_spacing being absent 218 $b = $xp 219 - $window/2 220 + $ds * $window/$self->{_data}->num_sets 221 - $bar_s; 222 } 223 224 # draw the bar 225 if ($value < 0) { ($r,$l) = ($l,$r) } 226 227 $self->{graph}->filledRectangle($l, $t, $r, $b, $dsci) 228 if defined $dsci; 229 $self->{graph}->rectangle($l, $t, $r, $b, $brci) 230 if defined $brci && $b - $t > $self->{accent_treshold}; 231 232 $self->{_hotspots}->[$ds]->[$i] = ['rect', $l, $t, $r, $b]; 233 } 234 235 return $ds; 236} 237 238sub draw_data_set_v 239{ 240 my $self = shift; 241 my $ds = shift; 242 243 my $bar_s = $self->{bar_spacing}/2; 244 245 # Pick a data colour 246 my $dsci = $self->set_clr($self->pick_data_clr($ds)); 247 # contrib "Bremford, Mike" <mike.bremford@gs.com> 248 my $brci = $self->set_clr($self->pick_border_clr($ds)); 249 250 my @values = $self->{_data}->y_values($ds) or 251 return $self->_set_error("Impossible illegal data set: $ds", 252 $self->{_data}->error); 253 254 my $topvalues = $self->_top_values; 255 256 my ($bar_sets,$ds_adj) = ( $self->{_data}->num_sets , $ds ); 257 if ( $self->isa( 'GD::Graph::mixed' ) ) { 258 my @types = $self->types; 259 $bar_sets = grep { $_ eq 'bars' } @types; 260 $ds_adj = grep { $_ eq 'bars' } @types[0..$ds-1]; 261 } 262 263 # Draw all shadows. 264 for my $i (0 .. $#values) 265 { 266 my $value = $values[$i]; 267 next unless defined $value; 268 269 my $bottom = $self->_get_bottom($ds, $i); 270 my ($xp, $t) = $self->val_to_pixel($i + 1, $value, $ds); 271 my ($l, $r); 272 my $window = $self->{x_step} - $self->{bargroup_spacing}; 273 274 if ($self->{overwrite}) 275 { 276 $l = $xp - $window/2 + $bar_s + 1; 277 $r = $xp + $window/2 - $bar_s; 278 } 279 else 280 { 281 $l = $xp 282 - $window/2 283 + ($ds_adj - 1) * $window/$bar_sets 284 + $bar_s + 1; # GRANTM thinks this +1 should be conditional on bargroup_spacing being absent 285 $r = $xp 286 - $window/2 287 + $ds_adj * $window/$bar_sets 288 - $bar_s; 289 } 290 291 $self->_draw_shadow($ds, $i, $value, $topvalues, $l, $t, $r, $bottom); 292 } 293 294 # Then all bars. 295 for my $i (0 .. $#values) 296 { 297 my $value = $values[$i]; 298 next unless defined $value; 299 300 my $bottom = $self->_get_bottom($ds, $i); 301 $value = $self->{_data}->get_y_cumulative($ds, $i) 302 if ($self->{cumulate}); 303 304 # CONTRIB Jeremy Wadsack 305 # 306 # cycle_clrs option sets the color based on the point, 307 # not the dataset. 308 $dsci = $self->set_clr($self->pick_data_clr($i + 1)) 309 if $self->{cycle_clrs}; 310 $brci = $self->set_clr($self->pick_data_clr($i + 1)) 311 if $self->{cycle_clrs} > 1; 312 313 # get coordinates of top and center of bar 314 my ($xp, $t) = $self->val_to_pixel($i + 1, $value, $ds); 315 316 # calculate left and right of bar 317 my ($l, $r); 318 my $window = $self->{x_step} - $self->{bargroup_spacing}; 319 320 if ($self->{overwrite}) 321 { 322 $l = $xp - $window/2 + $bar_s + 1; 323 $r = $xp + $window/2 - $bar_s; 324 } 325 else 326 { 327 $l = $xp 328 - $window/2 329 + ($ds_adj - 1) * $window/$bar_sets 330 + $bar_s + 1; # GRANTM thinks this +1 should be conditional on bargroup_spacing being absent 331 $r = $xp 332 - $window/2 333 + $ds_adj * $window/$bar_sets 334 - $bar_s; 335 } 336 337 # draw the bar 338 339 if ($value < 0) { ($bottom,$t) = ($t,$bottom) } 340 $self->{graph}->filledRectangle($l, $t, $r, $bottom, $dsci) 341 if defined $dsci; 342 $self->{graph}->rectangle($l, $t, $r, $bottom, $brci) 343 if defined $brci && $r - $l > $self->{accent_treshold}; 344 $self->{_hotspots}->[$ds]->[$i] = ['rect', $l, $t, $r, $bottom] 345 } 346 347 return $ds; 348} 349 350sub draw_data_set 351{ 352 $_[0]->{rotate_chart} ? goto &draw_data_set_h : goto &draw_data_set_v; 353} 354 355sub draw_values 356{ 357 my $self = shift; 358 359 return $self unless $self->{show_values}; 360 my $has_args = @_; 361 362 my $text_angle = $self->{values_vertical} ? PI/2 : 0; 363 my @numPoints = $self->{_data}->num_points(); 364 my @datasets = $has_args ? @_ : 1 .. $self->{_data}->num_sets; 365 366 my ($l, $r, $b, $t) = ($self->{left}, $self->{right}, $self->{bottom}, $self->{top}); 367 368 for my $dsn ( @datasets ) 369 { # CONTRIB Romeo Juncu 370 my @values = (); 371 if (!$self->get("cumulate")) { 372 @values = $self->{_data}->y_values($dsn) or 373 return $self->_set_error("Impossible illegal data set: $dsn", 374 $self->{_data}->error); 375 } else { 376 my $nPoints = $numPoints[$dsn] || 0; 377 my $vec = $has_args ? \@datasets : undef; 378 @values = map { $self->{_data}->get_y_cumulative($dsn, $_, $vec) } 379 (0..$nPoints - 1) ; 380 } 381 my @display = $self->{show_values}->y_values($dsn) or next; 382 383 for (my $i = 0; $i < @values; $i++) 384 { 385 next unless defined $display[$i]; 386 387 my $value = $display[$i]; 388 if (defined $self->{values_format}) 389 { 390 $value = ref $self->{values_format} eq 'CODE' ? 391 &{$self->{values_format}}($value) : 392 sprintf($self->{values_format}, $value); 393 } 394 395 my ($xp, $yp); 396 if (defined($self->{x_min_value}) && defined($self->{x_max_value})) 397 { 398 ($xp, $yp) = $self->val_to_pixel( 399 $self->{_data}->get_x($i), $values[$i], $dsn); 400 } 401 else 402 { 403 ($xp, $yp) = $self->val_to_pixel($i+1, $values[$i], $dsn); 404 } 405 if ($self->{rotate_chart}) 406 { 407 $xp += $self->{values_space}; 408 unless ($self->{overwrite}) 409 { 410 $yp -= $self->{x_step}/2 - ($dsn - 0.5) 411 * $self->{x_step}/@datasets; 412 } 413 } 414 else 415 { 416 $yp -= $self->{values_space}; 417 unless ($self->{overwrite}) 418 { 419 $xp -= $self->{x_step}/2 - ($dsn - 0.5) 420 * $self->{x_step}/@datasets; 421 } 422 } 423 424 $self->{gdta_values}->set_text($value); 425 if ( $self->{'hide_overlapping_values'} ) { 426 my @bbox = $self->{gdta_values}->bounding_box($xp, $yp, $text_angle); 427 next if grep $_ < $l || $_ > $r, @bbox[0, 2]; 428 next if grep $_ < $t || $_ > $b, @bbox[1, 5]; 429 } 430 $self->{gdta_values}->draw($xp, $yp, $text_angle); 431 } 432 } 433 434 return $self 435} 436 437"Just another true value"; 438