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