line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Chart::Plot::Annotated;
|
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
262725
|
use 5.006;
|
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
50
|
|
4
|
1
|
|
|
1
|
|
6
|
use strict;
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
40
|
|
5
|
1
|
|
|
1
|
|
6
|
use warnings;
|
|
1
|
|
|
|
|
15
|
|
|
1
|
|
|
|
|
37
|
|
6
|
1
|
|
|
1
|
|
6
|
use Carp;
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
87
|
|
7
|
|
|
|
|
|
|
our $VERSION = '0.01';
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
##################################################################
|
10
|
|
|
|
|
|
|
# define this class:
|
11
|
1
|
|
|
1
|
|
5
|
use base 'Chart::Plot';
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
1820
|
|
12
|
|
|
|
|
|
|
use Class::MethodMaker
|
13
|
|
|
|
|
|
|
# the extra data we'll put into the Chart::Plot object
|
14
|
0
|
|
|
|
|
|
object_list => [ Chart::Plot::Annotated::_DataPt => '_AnnoData' ],
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
# formatting internals
|
17
|
|
|
|
|
|
|
get_set => [ qw [ _anno_xOffset _anno_yOffset _anno_font _anno_color ] ],
|
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
# error-reporting
|
20
|
0
|
|
|
0
|
|
|
get_set => [ '_problem' ];
|
|
0
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
##################################################################
|
22
|
|
|
|
|
|
|
# define an auxiliary class:
|
23
|
|
|
|
|
|
|
use Class::Struct Chart::Plot::Annotated::_DataPt =>
|
24
|
|
|
|
|
|
|
[ X => '$', Y => '$', anno => '$' ];
|
25
|
|
|
|
|
|
|
##################################################################
|
26
|
|
|
|
|
|
|
# new public method:
|
27
|
|
|
|
|
|
|
sub setAnnoData {
|
28
|
|
|
|
|
|
|
my $self = shift;
|
29
|
|
|
|
|
|
|
my @annos = @{shift @_};
|
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
my $rc = $self->setData(@_);
|
32
|
|
|
|
|
|
|
if (not $rc) {
|
33
|
|
|
|
|
|
|
return $rc;
|
34
|
|
|
|
|
|
|
}
|
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
# store the datapoints for later
|
37
|
|
|
|
|
|
|
if (ref $_[0] eq 'ARRAY' and ref $_[1] eq 'ARRAY') {
|
38
|
|
|
|
|
|
|
# x datapts and y datapts separated
|
39
|
|
|
|
|
|
|
my (@x) = @{shift @_};
|
40
|
|
|
|
|
|
|
my (@y) = @{shift @_};
|
41
|
|
|
|
|
|
|
unless (@annos == @x) {
|
42
|
|
|
|
|
|
|
$self->_problem("different numbers of annotations and x-values");
|
43
|
|
|
|
|
|
|
return 0;
|
44
|
|
|
|
|
|
|
}
|
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
unless (@annos == @y) {
|
47
|
|
|
|
|
|
|
$self->_problem("different numbers of annotations and y-values");
|
48
|
|
|
|
|
|
|
return 0;
|
49
|
|
|
|
|
|
|
}
|
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
while (@annos) {
|
52
|
|
|
|
|
|
|
my $datum =
|
53
|
|
|
|
|
|
|
Chart::Plot::Annotated::_DataPt->new( X => shift @x,
|
54
|
|
|
|
|
|
|
Y => shift @y,
|
55
|
|
|
|
|
|
|
anno => shift @annos
|
56
|
|
|
|
|
|
|
);
|
57
|
|
|
|
|
|
|
$self->push__AnnoData($datum);
|
58
|
|
|
|
|
|
|
}
|
59
|
|
|
|
|
|
|
}
|
60
|
|
|
|
|
|
|
else {
|
61
|
|
|
|
|
|
|
# assume X and Y datapoints presented as one array
|
62
|
|
|
|
|
|
|
my @xy = @{shift @_};
|
63
|
|
|
|
|
|
|
unless (@xy == 2*@annos) {
|
64
|
|
|
|
|
|
|
$self->_problem("annos not synced with (x,y) values " .
|
65
|
|
|
|
|
|
|
"-- different numbers of elements");
|
66
|
|
|
|
|
|
|
return 0;
|
67
|
|
|
|
|
|
|
}
|
68
|
|
|
|
|
|
|
while (@annos) {
|
69
|
|
|
|
|
|
|
my $datum;
|
70
|
|
|
|
|
|
|
my $anno = shift @annos;
|
71
|
|
|
|
|
|
|
if (not defined $anno) {
|
72
|
|
|
|
|
|
|
$anno = '';
|
73
|
|
|
|
|
|
|
}
|
74
|
|
|
|
|
|
|
$datum =
|
75
|
|
|
|
|
|
|
Chart::Plot::Annotated::_DataPt->new( X => shift @xy,
|
76
|
|
|
|
|
|
|
Y => shift @xy,
|
77
|
|
|
|
|
|
|
anno => $anno );
|
78
|
|
|
|
|
|
|
$self->push__AnnoData($datum);
|
79
|
|
|
|
|
|
|
}
|
80
|
|
|
|
|
|
|
}
|
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
if (defined $_[0] and ref $_[0] eq 'ARRAY') {
|
83
|
|
|
|
|
|
|
$self->_problem("too many arrayrefs to setAnnoData");
|
84
|
|
|
|
|
|
|
return 0;
|
85
|
|
|
|
|
|
|
}
|
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
# arrive here? no problems.
|
88
|
|
|
|
|
|
|
return 1;
|
89
|
|
|
|
|
|
|
}
|
90
|
|
|
|
|
|
|
##################################################################
|
91
|
|
|
|
|
|
|
# override base class to handle extra layer's new possible errors
|
92
|
|
|
|
|
|
|
sub error {
|
93
|
|
|
|
|
|
|
my $self = shift;
|
94
|
|
|
|
|
|
|
if (defined $self->_problem) {
|
95
|
|
|
|
|
|
|
return $self->_problem();
|
96
|
|
|
|
|
|
|
}
|
97
|
|
|
|
|
|
|
# else call base class
|
98
|
|
|
|
|
|
|
return $self->SUPER::error();
|
99
|
|
|
|
|
|
|
}
|
100
|
|
|
|
|
|
|
##################################################################
|
101
|
|
|
|
|
|
|
# override base class to handle extra layer's extra options
|
102
|
|
|
|
|
|
|
sub setGraphOptions {
|
103
|
|
|
|
|
|
|
my $self = shift;
|
104
|
|
|
|
|
|
|
my %args = @_;
|
105
|
|
|
|
|
|
|
if (defined $args{anno_color}) {
|
106
|
|
|
|
|
|
|
if (ref $args{anno_color} ne 'ARRAY') {
|
107
|
|
|
|
|
|
|
$self->_problem("anno_color arg to setGraphOptions()" .
|
108
|
|
|
|
|
|
|
" needs arrayref value");
|
109
|
|
|
|
|
|
|
return 0;
|
110
|
|
|
|
|
|
|
}
|
111
|
|
|
|
|
|
|
$self->_setAnnoColor(@{$args{anno_color}})
|
112
|
|
|
|
|
|
|
or return 0; # problem?
|
113
|
|
|
|
|
|
|
delete $args{anno_color};
|
114
|
|
|
|
|
|
|
}
|
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
if (defined $args{anno_xOffset}) {
|
117
|
|
|
|
|
|
|
$self->_anno_xOffset($args{anno_xOffset});
|
118
|
|
|
|
|
|
|
delete $args{anno_xOffset};
|
119
|
|
|
|
|
|
|
}
|
120
|
|
|
|
|
|
|
if (defined $args{anno_yOffset}) {
|
121
|
|
|
|
|
|
|
$self->_anno_yOffset($args{anno_yOffset});
|
122
|
|
|
|
|
|
|
delete $args{anno_yOffset};
|
123
|
|
|
|
|
|
|
}
|
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
# send remaining args to base class, if there are any left
|
126
|
|
|
|
|
|
|
if (%args) {
|
127
|
|
|
|
|
|
|
return $self->SUPER::setGraphOptions(%args);
|
128
|
|
|
|
|
|
|
}
|
129
|
|
|
|
|
|
|
else {
|
130
|
|
|
|
|
|
|
# everything went fine!
|
131
|
|
|
|
|
|
|
return 1;
|
132
|
|
|
|
|
|
|
}
|
133
|
|
|
|
|
|
|
}
|
134
|
|
|
|
|
|
|
##################################################################
|
135
|
|
|
|
|
|
|
use GD; # use this to directly annotate the resulting plot with the
|
136
|
|
|
|
|
|
|
# annotations.
|
137
|
|
|
|
|
|
|
##################################################################
|
138
|
|
|
|
|
|
|
# override base class to handle extra layer's extra markup on the
|
139
|
|
|
|
|
|
|
# image object
|
140
|
|
|
|
|
|
|
sub draw {
|
141
|
|
|
|
|
|
|
my $self = shift;
|
142
|
|
|
|
|
|
|
my $gdObj = $self->SUPER::getGDobject();
|
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
if (not defined $self->_anno_color) {
|
145
|
|
|
|
|
|
|
$self->_setAnnoColor(0,0,0); # black
|
146
|
|
|
|
|
|
|
}
|
147
|
|
|
|
|
|
|
if (not defined $self->_anno_font) {
|
148
|
|
|
|
|
|
|
$self->_anno_font( gdTinyFont );
|
149
|
|
|
|
|
|
|
}
|
150
|
|
|
|
|
|
|
if (not defined $self->_anno_xOffset) {
|
151
|
|
|
|
|
|
|
$self->_anno_xOffset( 0 );
|
152
|
|
|
|
|
|
|
}
|
153
|
|
|
|
|
|
|
if (not defined $self->_anno_yOffset) {
|
154
|
|
|
|
|
|
|
$self->_anno_yOffset( 0 );
|
155
|
|
|
|
|
|
|
}
|
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
# set all the annotations
|
158
|
|
|
|
|
|
|
while ($self->count__AnnoData) {
|
159
|
|
|
|
|
|
|
my $datum = $self->shift__AnnoData;
|
160
|
|
|
|
|
|
|
if (defined $datum->anno and length $datum->anno) {
|
161
|
|
|
|
|
|
|
$self->_setAnno($gdObj, $datum);
|
162
|
|
|
|
|
|
|
}
|
163
|
|
|
|
|
|
|
# otherwise skip empty strings
|
164
|
|
|
|
|
|
|
}
|
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
# done with extra markup, now call base class draw; returning
|
167
|
|
|
|
|
|
|
# whatever it does
|
168
|
|
|
|
|
|
|
return $self->SUPER::draw();
|
169
|
|
|
|
|
|
|
}
|
170
|
|
|
|
|
|
|
##################################################################
|
171
|
|
|
|
|
|
|
# private methods
|
172
|
|
|
|
|
|
|
##################################################################
|
173
|
|
|
|
|
|
|
sub _setAnnoColor {
|
174
|
|
|
|
|
|
|
# sets the annotation color to the appropriate color-triple. Used
|
175
|
|
|
|
|
|
|
# for handling configuration data
|
176
|
|
|
|
|
|
|
my $self = shift;
|
177
|
|
|
|
|
|
|
my ($r, $g, $b) = @_;
|
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
if (@_ < 3) {
|
180
|
|
|
|
|
|
|
# fatal
|
181
|
|
|
|
|
|
|
$self->_problem( "need 3 args to annotation color-setting" );
|
182
|
|
|
|
|
|
|
return 0;
|
183
|
|
|
|
|
|
|
}
|
184
|
|
|
|
|
|
|
if (@_ > 3) {
|
185
|
|
|
|
|
|
|
# non-fatal, though silly
|
186
|
|
|
|
|
|
|
carp "_setAnnoColor args beyond (R,G,B) ignored";
|
187
|
|
|
|
|
|
|
}
|
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
my $gdObj = $self->SUPER::getGDobject();
|
190
|
|
|
|
|
|
|
my $color = $gdObj->colorAllocate($r, $g, $b);
|
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
$self->_anno_color($color);
|
193
|
|
|
|
|
|
|
return 1;
|
194
|
|
|
|
|
|
|
}
|
195
|
|
|
|
|
|
|
##################################################################
|
196
|
|
|
|
|
|
|
sub _setAnno {
|
197
|
|
|
|
|
|
|
# writes an annotation onto the base class Chart::Plot, given a
|
198
|
|
|
|
|
|
|
# pointer to the GD Object underneath.
|
199
|
|
|
|
|
|
|
my $self = shift;
|
200
|
|
|
|
|
|
|
my $gdObj = shift;
|
201
|
|
|
|
|
|
|
my $datum = shift;
|
202
|
|
|
|
|
|
|
my ($xp, $yp) = $self->SUPER::data2pxl($datum->X, $datum->Y);
|
203
|
|
|
|
|
|
|
$gdObj->string($self->_anno_font,
|
204
|
|
|
|
|
|
|
($xp + $self->_anno_xOffset),
|
205
|
|
|
|
|
|
|
($yp + $self->_anno_yOffset),
|
206
|
|
|
|
|
|
|
$datum->anno,
|
207
|
|
|
|
|
|
|
$self->_anno_color);
|
208
|
|
|
|
|
|
|
}
|
209
|
|
|
|
|
|
|
##################################################################
|
210
|
|
|
|
|
|
|
1;
|
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
__END__
|