File Coverage

blib/lib/GD/Sparkline.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             #########
2             # Author: rmp
3             # Maintainer: $Author: rmp $
4             # Created: 2008-12-01
5             # Last Modified: $Date$
6             # Id: $Id$
7             # $HeadURL$
8             #
9             package GD::Sparkline;
10 2     2   10 use strict;
  2         4  
  2         73  
11 2     2   11 use warnings;
  2         4  
  2         47  
12 2     2   903 use GD;
  0            
  0            
13             use base qw(Class::Accessor);
14             use Readonly;
15             use Math::Bezier;
16              
17             Readonly::Scalar our $H => 20; # Height
18             Readonly::Scalar our $W => 80; # Width
19             Readonly::Scalar our $T => 'b'; # Chart type
20             Readonly::Scalar our $B => q[FFFFFF]; # Background
21             Readonly::Scalar our $A => q[80D7B7]; # Area colour
22             Readonly::Scalar our $L => q[000000]; # Line colour
23             Readonly::Scalar our $BEZ_REZ => 10;
24              
25             our $VERSION = q[0.05];
26              
27             __PACKAGE__->mk_accessors(__PACKAGE__->fields());
28              
29             sub fields {
30             return qw(p s h w b a l t);
31             }
32              
33             sub new {
34             my ($class, $ref) = @_;
35              
36             if(!$ref) {
37             $ref = {};
38             }
39              
40             bless $ref, $class;
41             return $ref;
42             }
43              
44             sub draw { ## no critic (ProhibitExcessComplexity)
45             my $self = shift;
46             my $raw = $self->p();
47             my $series = $self->s();
48             my $p = [];
49             my ($min, $max);
50              
51             if($raw) {
52             $p = [map { ## no critic (ProhibitComplexMappings)
53             if(!defined $min || $_<$min){
54             $min=$_;
55             }
56              
57             if(!defined $max || $_>$max){
58             $max=$_;
59             }
60              
61             $_;
62             } unpack q[C]x(length $raw), ($raw || q[])];
63              
64             } elsif($series) {
65             $p = [map { ## no critic (ProhibitComplexMappings)
66             if(!defined $min || $_<$min){
67             $min=$_;
68             }
69              
70             if(!defined $max || $_>$max){
71             $max=$_;
72             }
73              
74             $_;
75             } split /,/smx, $series];
76             }
77              
78             my $h = $self->h || $H;
79             my $w = $self->w || $W;
80             my $gd = GD::Image->newTrueColor($w, $h);
81             my $b_str = $self->b;
82              
83             if($b_str eq 'transparent') {
84             $b_str = 'ffffff';
85             }
86              
87             my $bg = $gd->colorAllocate(map { hex $_ } unpack 'A2A2A2', $b_str || $B);
88             my $area = $gd->colorAllocate(map { hex $_ } unpack 'A2A2A2', $self->a || $A);
89             my $line = $gd->colorAllocate(map { hex $_ } unpack 'A2A2A2', $self->l || $L);
90              
91             if($self->b eq 'transparent') {
92             $gd->transparent($bg);
93             }
94              
95             $gd->filledRectangle(0,0, $w,$h, $bg);
96              
97             my $type = $self->t || $T;
98             my $func = "type_$type";
99              
100             if($self->can($func)) {
101             $self->$func($gd, $p,
102             {
103             min => $min,
104             max => $max,
105             },
106             {
107             line => $line,
108             area => $area,
109             h => $h,
110             w => $w,
111             });
112             }
113              
114             return $gd->png();
115             }
116              
117             sub type_b {
118             my ($self, $gd, $p, $data_attrs, $chart_attrs) = @_;
119             my $min = $data_attrs->{min};
120             my $max = $data_attrs->{max};
121             my $line = $chart_attrs->{line};
122             my $area = $chart_attrs->{area};
123             my $h = $chart_attrs->{h} || $H;
124             my $w = $chart_attrs->{w} || $W;
125              
126             my $dy = 0+$max-$min;
127             my $dx = scalar @{$p} - 1;
128             my $scaley = $h/($dy||1);
129             my $scalex = $w/($dx||1);
130             my $pos = 0;
131              
132             my $lastx = 0;
133             my @controls;
134              
135             for my $d (@{$p}) {
136             my $y = $h-($d-$min)*$scaley;
137             my $x = $pos*$scalex;
138              
139             push @controls, ($x, $y);
140             $pos++;
141             }
142              
143             my $bezier = Math::Bezier->new(@controls);
144             my $pointsa = $bezier->curve($w);#10000);#$w/$BEZ_REZ);
145             my $pointsl = [@{$pointsa}];
146              
147             my ($lx, $ly) = splice @{$pointsa}, 0, 2;
148             while(scalar @{$pointsa}) {
149             my ($x, $y) = splice @{$pointsa}, 0, 2;
150              
151             my $poly = GD::Polygon->new;
152             $poly->addPt($lx, $ly);
153             $poly->addPt($lx, $h);
154             $poly->addPt($x, $h);
155             $poly->addPt($x, $y);
156             $gd->filledPolygon($poly, $area);
157             $lx = $x;
158             $ly = $y;
159             }
160              
161             ($lx, $ly) = splice @{$pointsl}, 0, 2;
162             $gd->setAntiAliased($line);
163              
164             while(scalar @{$pointsl}) {
165             my ($x, $y) = splice @{$pointsl}, 0, 2;
166              
167             $gd->line($lx, $ly, $x, $y, gdAntiAliased);
168             $lx = $x;
169             $ly = $y;
170             }
171              
172             return 1;
173             }
174              
175             1;
176             __END__