File Coverage

blib/lib/Chart/Clicker/Renderer/Line.pm
Criterion Covered Total %
statement 21 108 19.4
branch 0 32 0.0
condition 0 15 0.0
subroutine 7 9 77.7
pod 2 2 100.0
total 30 166 18.0


line stmt bran cond sub pod time code
1             package Chart::Clicker::Renderer::Line;
2             $Chart::Clicker::Renderer::Line::VERSION = '2.90';
3 9     9   60068 use Moose;
  9         371516  
  9         73  
4              
5             # ABSTRACT: Line renderer
6              
7             extends 'Chart::Clicker::Renderer';
8              
9 9     9   52682 use Geometry::Primitive::Point;
  9         198093  
  9         389  
10 9     9   720 use Graphics::Primitive::Brush;
  9         100538  
  9         313  
11 9     9   5671 use Graphics::Primitive::Operation::Stroke;
  9         316542  
  9         482  
12 9     9   4575 use Geometry::Primitive::Circle;
  9         149406  
  9         384  
13              
14             #number of defined points we must have around another point
15             #to render a line instead of a scatter
16             #
17 9     9   66 use constant MIN_DEFINED_SURROUNDING_POINTS => 5;
  9         14  
  9         8917  
18              
19              
20             has 'brush' => (
21             is => 'rw',
22             isa => 'Graphics::Primitive::Brush',
23             default => sub { Graphics::Primitive::Brush->new(width => 2) }
24             );
25              
26              
27             has 'shape' => (
28             is => 'rw',
29             isa => 'Geometry::Primitive::Shape',
30             );
31              
32              
33             has 'shape_brush' => (
34             is => 'rw',
35             isa => 'Graphics::Primitive::Brush',
36             );
37             # TODO Readd shapes
38              
39             sub finalize {
40 0     0 1   my ($self) = @_;
41              
42 0           my $width = $self->width;
43 0           my $height = $self->height;
44              
45 0           my $clicker = $self->clicker;
46              
47 0           my $dses = $clicker->get_datasets_for_context($self->context);
48 0           my %accum;
49 0           foreach my $ds (@{ $dses }) {
  0            
50 0           foreach my $series (@{ $ds->series }) {
  0            
51              
52             # TODO if undef...
53 0           my $ctx = $clicker->get_context($ds->context);
54 0           my $domain = $ctx->domain_axis;
55 0           my $range = $ctx->range_axis;
56              
57 0           my $color = $clicker->color_allocator->next;
58              
59 0           my @vals = @{ $series->values };
  0            
60 0           my @keys = @{ $series->keys };
  0            
61              
62 0           my $kcount = $series->key_count - 1;
63              
64 0           my $skip = 0;
65 0           my $previous_x = -1;
66 0           my $previous_y = -1;
67 0           my $min_y_delta_on_same_x = $height / 100;
68              
69 0           for(0..$kcount) {
70              
71 0           my $key = $keys[$_];
72              
73 0           my $x = $domain->mark($width, $key);
74 0 0         next unless defined($x);
75 0 0         $skip = 1 unless defined $vals[$_];
76 0           my $ymark = $range->mark($height, $vals[$_]);
77 0 0         next unless defined($ymark);
78              
79 0 0         if($self->additive) {
80 0 0         if(exists($accum{$key})) {
81 0           $accum{$key} += $ymark;
82 0           $ymark = $accum{$key};
83             } else {
84 0           $accum{$key} = $ymark;
85             }
86             }
87              
88 0           my $y = $height - $ymark;
89 0 0 0       if( $_ == 0 || $skip ) {
90 0           my $lineop = Graphics::Primitive::Operation::Stroke->new(
91             brush => $self->brush->clone
92             );
93 0           $lineop->brush->color($color);
94 0           $self->do($lineop);
95 0           $self->move_to($x, $y);
96 0           my $start_new_line = 1;
97 0           foreach my $i ($_..($_ + MIN_DEFINED_SURROUNDING_POINTS)) {
98 0 0 0       if ($i > 0 && $i < @vals && !defined($vals[$i])) {
      0        
99 0           $start_new_line = 0;
100             }
101             }
102 0 0         if ($start_new_line){
103 0           $skip = 0;
104             }
105             else {
106 0           my $shape = Geometry::Primitive::Circle->new(radius => 3);
107 0           $shape->origin(Geometry::Primitive::Point->new(x => $x, y => $y));
108 0           $self->path->add_primitive($shape);
109 0           my $fill = Graphics::Primitive::Operation::Fill->new(
110             paint => Graphics::Primitive::Paint::Solid->new(
111             color => $color
112             )
113             );
114 0           $self->do($fill);
115             }
116             }
117             else {
118             # when in fast mode, we plot only if we moved by more than
119             # 1 of a pixel on the X axis or we moved by more than 1%
120             # of the size of the Y axis.
121 0 0 0       if( $clicker->plot_mode ne 'fast' ||
      0        
122             $x - $previous_x > 1 ||
123             abs($y - $previous_y) > $min_y_delta_on_same_x
124             )
125             {
126 0           $self->line_to($x, $y);
127 0           $previous_x = $x;
128 0           $previous_y = $y;
129             }
130             }
131              
132             }
133 0           my $op = Graphics::Primitive::Operation::Stroke->new;
134 0           $op->brush($self->brush->clone);
135 0           $op->brush->color($color);
136 0           $self->do($op);
137              
138 0 0         if(defined($self->shape)) {
139 0           for(0..$kcount) {
140 0           my $key = $keys[$_];
141 0           my $x = $domain->mark($width, $key);
142 0 0         next unless defined($x);
143 0           my $ymark = $range->mark($height, $vals[$_]);
144 0 0         next unless defined($ymark);
145              
146 0 0         if($self->additive) {
147 0 0         if(exists($accum{$key})) {
148 0           $ymark = $accum{$key};
149             } else {
150 0           $accum{$key} = $ymark;
151             }
152             }
153              
154 0           my $y = $height - $ymark;
155              
156 0           $self->move_to($x, $y);
157 0           $self->draw_point($x, $y, $series, $vals[$_]);
158             }
159              
160             # Fill the shape
161 0           my $op2 = Graphics::Primitive::Operation::Fill->new(
162             paint => Graphics::Primitive::Paint::Solid->new(
163             color => $color
164             )
165             );
166 0 0         if(defined($self->shape_brush)) {
167 0           $op2->preserve(1);
168             }
169 0           $self->do($op2);
170              
171             # Optionally stroke the shape
172 0 0         if(defined($self->shape_brush)) {
173 0           my $op3 = Graphics::Primitive::Operation::Stroke->new;
174 0           $op3->brush($self->shape_brush->clone);
175 0           $self->do($op3);
176             }
177             }
178             }
179             }
180              
181 0           return 1;
182             }
183              
184              
185             sub draw_point {
186 0     0 1   my ($self, $x, $y, $series, $count) = @_;
187              
188 0           my $shape = $self->shape->clone;
189 0           $shape->origin(Geometry::Primitive::Point->new(x => $x, y => $y));
190 0           $self->path->add_primitive($shape);
191             }
192              
193             __PACKAGE__->meta->make_immutable;
194              
195 9     9   63 no Moose;
  9         17  
  9         70  
196              
197             1;
198              
199             __END__
200              
201             =pod
202              
203             =head1 NAME
204              
205             Chart::Clicker::Renderer::Line - Line renderer
206              
207             =head1 VERSION
208              
209             version 2.90
210              
211             =head1 SYNOPSIS
212              
213             my $lr = Chart::Clicker::Renderer::Line->new(
214             brush => Graphics::Primitive::Brush->new({
215             #...
216             })
217             );
218              
219             =head1 DESCRIPTION
220              
221             Chart::Clicker::Renderer::Line renders a dataset as lines.
222              
223             =for HTML <p><img src="http://gphat.github.com/chart-clicker/static/images/examples/line.png" width="500" height="250" alt="Line Chart" /></p>
224              
225             =for HTML <p><img src="http://gphat.github.com/chart-clicker/static/images/examples/line-shapes.png" width="500" height="250" alt="Line + Shape Chart" /></p>
226              
227             =for HTML <p><img src="http://gphat.github.com/chart-clicker/static/images/examples/line-shapes-brushed.png" width="500" height="250" alt="Line + Shape (Brushed) Chart" /></p>
228              
229             =head1 ATTRIBUTES
230              
231             =head2 additive
232              
233             If true, the lines are drawn "stacked", each key accumulates based on those
234             drawn below it.
235              
236             =head2 brush
237              
238             Set/Get a L<brush|Graphics::Primitive::Brush> to be used for the lines.
239              
240             =head2 shape
241              
242             Set a L<shape|Geometry::Primitive::Shape> object to draw at each of the data points. Adding a shape results
243             in:
244              
245             =head2 shape_brush
246              
247             Set/Get the L<brush|Graphics::Primitive::Brush> to be used on the shapes at
248             each point. If no shape_brush is provided, then the shapes will be filled.
249             The brush allows you to draw a "halo" around each shape. This sometimes help
250             to separate the points from the lines and make them more distinct.
251              
252             =head1 METHODS
253              
254             =head2 draw_point
255              
256             Called for each point encountered on the line.
257              
258             =head1 AUTHOR
259              
260             Cory G Watson <gphat@cpan.org>
261              
262             =head1 COPYRIGHT AND LICENSE
263              
264             This software is copyright (c) 2016 by Cory G Watson.
265              
266             This is free software; you can redistribute it and/or modify it under
267             the same terms as the Perl 5 programming language system itself.
268              
269             =cut