1package Chart::Clicker::Renderer::StackedBar; 2$Chart::Clicker::Renderer::StackedBar::VERSION = '2.90'; 3use Moose; 4 5extends 'Chart::Clicker::Renderer'; 6 7# ABSTRACT: Stacked Bar renderer 8 9use Graphics::Primitive::Brush; 10use Graphics::Primitive::Paint::Solid; 11use Graphics::Primitive::Operation::Fill; 12use Graphics::Primitive::Operation::Stroke; 13 14 15has '+additive' => ( default => 1 ); 16 17 18has 'bar_padding' => ( 19 is => 'rw', 20 isa => 'Int', 21 default => 0 22); 23 24 25has 'bar_width' => ( 26 is => 'rw', 27 isa => 'Num', 28 predicate => 'has_bar_width' 29); 30 31 32has 'brush' => ( 33 is => 'rw', 34 isa => 'Graphics::Primitive::Brush', 35 default => sub { Graphics::Primitive::Brush->new } 36); 37 38 39has 'opacity' => ( 40 is => 'rw', 41 isa => 'Num', 42 default => 0 43); 44 45override('prepare', sub { 46 my ($self) = @_; 47 48 super; 49 50 my $dses = $self->clicker->get_datasets_for_context($self->context); 51 52 foreach my $ds (@{ $dses }) { 53 if(!defined($self->{KEYCOUNT})) { 54 $self->{KEYCOUNT} = $ds->max_key_count; 55 } 56 $self->{SCOUNT} += $ds->count; 57 } 58 59 return 1; 60}); 61 62override('finalize', sub { 63 my ($self) = @_; 64 65 my $clicker = $self->clicker; 66 67 my $height = $self->height; 68 my $width = $self->width; 69 70 my $dses = $clicker->get_datasets_for_context($self->context); 71 my $ctx = $clicker->get_context($dses->[0]->context); 72 my $domain = $ctx->domain_axis; 73 my $range = $ctx->range_axis; 74 75 my $padding = $self->bar_padding; 76 77 my $strokewidth = $self->brush->width; 78 $padding += $strokewidth; 79 80 my $bwidth; 81 if($self->has_bar_width) { 82 $bwidth = $self->bar_width; 83 } else { 84 $bwidth = int(($width - ($width * $domain->fudge_amount) 85 - ($padding / 2 * $self->{KEYCOUNT})) / ($self->{KEYCOUNT})); 86 } 87 88 my $hbwidth = $bwidth / 2; 89 90 # Fetch all the colors we'll need. Since we build each vertical bar from 91 # top to bottom, we'll need to change colors vertically. 92 for (my $i = 0; $i < $self->{SCOUNT}; $i++) { 93 push(@{ $self->{COLORS} }, $clicker->color_allocator->next); 94 } 95 96 my @keys = $dses->[0]->get_all_series_keys; 97 98 # Iterate over each key... 99 for (my $i = 0; $i < scalar(@keys); $i++) { 100 101 # Mark the x, since it's the same for each Y value 102 my $x = $domain->mark($width, $keys[$i]); 103 my $accum = 0; 104 105 # Get all the values from every dataset's series for each key 106 my @values; 107 foreach my $ds (@{ $dses }) { 108 push(@values, @{ $ds->get_series_values_for_key($keys[$i]) }); 109 } 110 111 my $val = 0; 112 for my $j (0 .. $#values) { 113 my $sval = $values[$j]; 114 115 # Skip this if there is no value for the specified key position 116 next if !defined($sval); 117 118 # Skip it if it's equal to our baseline, as there's no reason to 119 # draw anything if so 120 next if $sval == $range->baseline; 121 $val += $sval; 122 123 my $y = $range->mark($height, $val); 124 next unless defined($y); 125 126 $self->move_to($x - $hbwidth, $height - $y + $self->brush->width * 2); 127 $self->rectangle($bwidth, $y - $accum - 1); 128 # Accumulate the Y value, as it dictates how much we bump up the 129 # next bar. 130 $accum += $y - $accum; 131 132 my $color = $self->{COLORS}->[$j]; 133 134 my $fillop = Graphics::Primitive::Operation::Fill->new( 135 paint => Graphics::Primitive::Paint::Solid->new 136 ); 137 138 if($self->opacity) { 139 my $fillcolor = $color->clone; 140 $fillcolor->alpha($self->opacity); 141 $fillop->paint->color($fillcolor); 142 # Since we're going to stroke this, we want to preserve it. 143 $fillop->preserve(1); 144 } else { 145 $fillop->paint->color($color); 146 } 147 148 $self->do($fillop); 149 150 if($self->opacity) { 151 my $strokeop = Graphics::Primitive::Operation::Stroke->new; 152 $strokeop->brush->color($color); 153 $self->do($strokeop); 154 } 155 } 156 } 157 158 return 1; 159}); 160 161__PACKAGE__->meta->make_immutable; 162 163no Moose; 164 1651; 166 167__END__ 168 169=pod 170 171=head1 NAME 172 173Chart::Clicker::Renderer::StackedBar - Stacked Bar renderer 174 175=head1 VERSION 176 177version 2.90 178 179=head1 SYNOPSIS 180 181 my $br = Chart::Clicker::Renderer::Bar->new; 182 183=head1 DESCRIPTION 184 185Chart::Clicker::Renderer::StackedBar renders a dataset as stacked bars. 186 187=for HTML <p><img src="http://gphat.github.com/chart-clicker/static/images/examples/stacked-bar.png" width="500" height="250" alt="Stacked Bar Chart" /></p> 188 189=head1 ATTRIBUTES 190 191=head2 bar_padding 192 193How much padding to put around a bar. A padding of 4 will result in 2 pixels 194on each side. 195 196=head2 bar_width 197 198Allows you to override the calculation that determines the optimal width for 199bars. Be careful using this as it can making things look terrible. 200 201=head2 brush 202 203A L<brush|Graphics::Primitive::Brush> to stroke on each bar. 204 205=head2 opacity 206 207If true this value will be used when setting the opacity of the bar's fill. 208 209=head1 AUTHOR 210 211Cory G Watson <gphat@cpan.org> 212 213=head1 COPYRIGHT AND LICENSE 214 215This software is copyright (c) 2016 by Cory G Watson. 216 217This is free software; you can redistribute it and/or modify it under 218the same terms as the Perl 5 programming language system itself. 219 220=cut 221