File Coverage

blib/lib/Chart/Clicker/Renderer/Bar.pm
Criterion Covered Total %
statement 18 18 100.0
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 24 24 100.0


line stmt bran cond sub pod time code
1             package Chart::Clicker::Renderer::Bar;
2             $Chart::Clicker::Renderer::Bar::VERSION = '2.88';
3 2     2   27139 use Moose;
  2         519136  
  2         21  
4              
5             extends 'Chart::Clicker::Renderer';
6              
7             # ABSTRACT: Bar renderer
8              
9 2     2   16542 use Graphics::Primitive::Brush;
  2         335431  
  2         84  
10              
11 2     2   949 use Graphics::Primitive::Operation::Fill;
  2         44731  
  2         67  
12 2     2   895 use Graphics::Primitive::Operation::Stroke;
  2         28586  
  2         63  
13 2     2   974 use Graphics::Primitive::Paint::Solid;
  2         32649  
  2         2167  
14              
15              
16             has 'bar_padding' => (
17             is => 'rw',
18             isa => 'Int',
19             default => 0
20             );
21              
22              
23             has 'bar_width' => (
24             is => 'rw',
25             isa => 'Num',
26             predicate => 'has_bar_width'
27             );
28              
29              
30             has 'brush' => (
31             is => 'rw',
32             isa => 'Graphics::Primitive::Brush',
33             default => sub { Graphics::Primitive::Brush->new }
34             );
35              
36              
37             has 'opacity' => (
38             is => 'rw',
39             isa => 'Num',
40             default => 1
41             );
42              
43             override('prepare', sub {
44             my $self = shift;
45              
46             super;
47              
48             my $datasets = $self->clicker->get_datasets_for_context($self->context);
49              
50             $self->{KEYCOUNT} = 0;
51             foreach my $ds (@{ $datasets }) {
52             $self->{SCOUNT} += $ds->count;
53             if($ds->max_key_count > $self->{KEYCOUNT}) {
54             $self->{KEYCOUNT} = $ds->max_key_count;
55             }
56             }
57              
58             return 1;
59             });
60              
61             override('finalize', sub {
62             my ($self) = @_;
63              
64             my $clicker = $self->clicker;
65              
66             my $height = $self->height;
67             my $width = $self->width;
68              
69             my $dses = $clicker->get_datasets_for_context($self->context);
70              
71             my $padding = $self->bar_padding + $self->brush->width * 2;
72              
73             my $offset = 1;
74             foreach my $ds (@{ $dses }) {
75             foreach my $series (@{ $ds->series }) {
76             # TODO if undef...
77             my $ctx = $clicker->get_context($ds->context);
78             my $domain = $ctx->domain_axis;
79             my $range = $ctx->range_axis;
80              
81             my $owidth = $width - ($width * $domain->fudge_amount);
82             my $bwidth;
83             if($self->has_bar_width) {
84             $bwidth = $self->bar_width;
85             } else {
86             $bwidth = int(($owidth / $self->{KEYCOUNT})) - $padding;
87             }
88             my $hbwidth = int($bwidth / 2);
89              
90             # Fudge amounts mess up the calculation of bar widths, so
91             # we compensate for them here.
92             my $cbwidth = $bwidth / $self->{SCOUNT};
93             my $chbwidth = int($cbwidth / 2);
94              
95             my $color = $clicker->color_allocator->next;
96              
97             my $base = $range->baseline;
98             my $basey;
99             if(defined($base)) {
100             $basey = $height - $range->mark($height, $base);
101             } else {
102             $basey = $height;
103             $base = $range->range->lower;
104             }
105              
106             my @vals = @{ $series->values };
107             my @keys = @{ $series->keys };
108              
109             my $sksent = $series->key_count;
110             for(0..($sksent - 1)) {
111              
112             # Skip drawing anything if the value is equal to the baseline
113             next if $vals[$_] == $range->baseline;
114              
115             my $x = $domain->mark($width, $keys[$_]);
116             my $y = $range->mark($height, $vals[$_]);
117              
118             if($vals[$_] >= $base) {
119             if($self->{SCOUNT} == 1) {
120             $self->move_to($x + $chbwidth, $basey);
121             $self->rectangle(
122             -int($cbwidth), -int($y - ($height - $basey))
123             );
124             } else {
125             $self->move_to(
126             $x - $hbwidth + ($offset * $cbwidth), $basey
127             );
128             $self->rectangle(
129             -int($cbwidth) + $self->brush->width, -int($y - ($height - $basey))
130             );
131             }
132             } else {
133             if($self->{SCOUNT} == 1) {
134             $self->move_to($x + $chbwidth, $basey);
135             $self->rectangle(
136             -int($cbwidth), -int($y - ($height - $basey))
137             );
138             } else {
139             $self->move_to(
140             $x - $hbwidth + ($offset * $cbwidth), $basey
141             );
142             $self->rectangle(
143             -int($cbwidth) + $self->brush->width, int($height - $basey - $y)
144             );
145             }
146             }
147             }
148              
149             my $fillop = Graphics::Primitive::Operation::Fill->new(
150             paint => Graphics::Primitive::Paint::Solid->new
151             );
152              
153             my $brwidth = $self->brush->width;
154              
155             if($self->opacity < 1) {
156             my $fillcolor = $color->clone;
157             $fillcolor->alpha($self->opacity);
158             $fillop->paint->color($fillcolor);
159             # Since we're going to stroke this, we want to preserve it.
160             $fillop->preserve(1) if $brwidth;
161             } else {
162             $fillop->paint->color($color);
163             }
164              
165             $self->do($fillop);
166              
167             if(($self->opacity < 1) && ($brwidth > 0)) {
168             my $strokeop = Graphics::Primitive::Operation::Stroke->new;
169             $strokeop->brush($self->brush->clone);
170             unless(defined($self->brush->color)) {
171             $strokeop->brush->color($color);
172             }
173             $self->do($strokeop);
174             }
175              
176             $offset++;
177             }
178             }
179              
180             return 1;
181             });
182              
183             __PACKAGE__->meta->make_immutable;
184              
185 2     2   16 no Moose;
  2         5  
  2         14  
186              
187             1;
188              
189             __END__
190              
191             =pod
192              
193             =head1 NAME
194              
195             Chart::Clicker::Renderer::Bar - Bar renderer
196              
197             =head1 VERSION
198              
199             version 2.88
200              
201             =head1 SYNOPSIS
202              
203             my $br = Chart::Clicker::Renderer::Bar->new;
204              
205             =head1 DESCRIPTION
206              
207             Chart::Clicker::Renderer::Bar renders a dataset as bars.
208              
209             =head1 NEGATIVE BARS
210              
211             If you'd like to render both "negative and positive" bars, look at
212             L<Chart::Clicker::Axis>'s C<baseline> attribute. Setting it will result in
213             something like this:
214              
215             =for HTML <p><img src="http://gphat.github.com/chart-clicker/static/images/examples/bar-baseline.png" width="500" height="250" alt="Base (Baseline) Chart" /></p>
216              
217             =for HTML <p><img src="http://gphat.github.com/chart-clicker/static/images/examples/bar.png" width="500" height="250" alt="Bar Chart" /></p>
218              
219             =head2 bar_padding
220              
221             How much padding to put around a bar. A padding of 4 will result in 2 pixels
222             on each side.
223              
224             =head1 ATTRIBUTES
225              
226             =head2 bar_width
227              
228             Allows you to override the calculation that determines the optimal width for
229             bars. Be careful using this as it can making things look terrible. Note that
230             this number is divided evenly between all the values in a series when charting
231             multiple series.
232              
233             =head2 brush
234              
235             Set/Get the L<brush|Graphics::Primitive::Brush> to use around each bar.
236              
237             =head2 opacity
238              
239             Set/Get the alpha value to make each bar more or less opaque.
240              
241             =head1 AUTHOR
242              
243             Cory G Watson <gphat@cpan.org>
244              
245             =head1 COPYRIGHT AND LICENSE
246              
247             This software is copyright (c) 2014 by Cold Hard Code, LLC.
248              
249             This is free software; you can redistribute it and/or modify it under
250             the same terms as the Perl 5 programming language system itself.
251              
252             =cut