| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Imager::TimelineDiagram; |
|
2
|
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
23475
|
use 5.00503; |
|
|
2
|
|
|
|
|
9
|
|
|
|
2
|
|
|
|
|
90
|
|
|
4
|
2
|
|
|
2
|
|
11
|
use strict; |
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
89
|
|
|
5
|
2
|
|
|
2
|
|
11
|
use vars qw($VERSION); |
|
|
2
|
|
|
|
|
9
|
|
|
|
2
|
|
|
|
|
344
|
|
|
6
|
2
|
|
|
2
|
|
13715
|
use Imager; |
|
|
2
|
|
|
|
|
166546
|
|
|
|
2
|
|
|
|
|
18
|
|
|
7
|
2
|
|
|
2
|
|
2089
|
use Imager::Fill; |
|
|
2
|
|
|
|
|
5708
|
|
|
|
2
|
|
|
|
|
62
|
|
|
8
|
2
|
|
|
2
|
|
16
|
use Imager::Color; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
36
|
|
|
9
|
2
|
|
|
2
|
|
11
|
use Carp; |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
2401
|
|
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
$VERSION = '0.15'; |
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
# create object |
|
14
|
|
|
|
|
|
|
sub new { |
|
15
|
1
|
|
|
1
|
0
|
2805
|
my ($class,@args) = @_; |
|
16
|
1
|
50
|
|
|
|
6
|
if (scalar(@args)%2 != 0) { |
|
17
|
1
|
|
|
|
|
398
|
carp("Invalid arguments. No in name/value pair format."); |
|
18
|
1
|
|
|
|
|
5
|
return(undef); |
|
19
|
|
|
|
|
|
|
} |
|
20
|
|
|
|
|
|
|
|
|
21
|
0
|
|
|
|
|
|
my %hashObject = ( |
|
22
|
|
|
|
|
|
|
imageHeight => 440, |
|
23
|
|
|
|
|
|
|
imageWidth => 440, |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
gridWidth => 401, |
|
26
|
|
|
|
|
|
|
gridHeight => 401, |
|
27
|
|
|
|
|
|
|
gridSpacing => 10, |
|
28
|
|
|
|
|
|
|
gridXOffset => 20, |
|
29
|
|
|
|
|
|
|
gridYOffset => 10, |
|
30
|
|
|
|
|
|
|
gridColor => Imager::Color->new(200,200,200), |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
dataColor => Imager::Color->new(255,100,100), |
|
33
|
|
|
|
|
|
|
dataFormat => '%0.2f', # sprintf() format string |
|
34
|
|
|
|
|
|
|
dataLabelSide => 'right', |
|
35
|
|
|
|
|
|
|
showArrowheads => 1, |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
labelColor => Imager::Color->new(0,0,0), |
|
38
|
|
|
|
|
|
|
labelSize => 12, |
|
39
|
|
|
|
|
|
|
labelFont => Imager::Font->new(file => 'ImUgly.ttf'), |
|
40
|
|
|
|
|
|
|
); |
|
41
|
|
|
|
|
|
|
|
|
42
|
0
|
|
|
|
|
|
my %hash = @args; |
|
43
|
0
|
|
|
|
|
|
for (keys %hash) { |
|
44
|
0
|
|
|
|
|
|
$hashObject{$_} = $hash{$_}; |
|
45
|
|
|
|
|
|
|
} |
|
46
|
|
|
|
|
|
|
|
|
47
|
0
|
0
|
|
|
|
|
if (! defined($hashObject{'labelFont'})) { |
|
48
|
0
|
|
|
|
|
|
carp("Failed to load labelFont specified."); |
|
49
|
0
|
|
|
|
|
|
return(undef); |
|
50
|
|
|
|
|
|
|
} |
|
51
|
|
|
|
|
|
|
|
|
52
|
0
|
|
|
|
|
|
$hashObject{_image} = Imager->new(xsize => $hashObject{'imageWidth'}, |
|
53
|
|
|
|
|
|
|
ysize => $hashObject{'imageHeight'}, |
|
54
|
|
|
|
|
|
|
channels => 4); |
|
55
|
|
|
|
|
|
|
|
|
56
|
0
|
0
|
|
|
|
|
if (! defined($hashObject{'_image'})) { |
|
57
|
0
|
|
|
|
|
|
carp("Failed to create new Imager object : $!"); |
|
58
|
0
|
|
|
|
|
|
return(undef); |
|
59
|
|
|
|
|
|
|
} |
|
60
|
|
|
|
|
|
|
|
|
61
|
0
|
|
0
|
|
|
|
my $self = bless(\%hashObject,$class||__PACKAGE__); |
|
62
|
|
|
|
|
|
|
} |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
# set list of milestones. |
|
65
|
|
|
|
|
|
|
sub set_milestones { |
|
66
|
0
|
|
|
0
|
1
|
|
my ($self,@milestones) = @_; |
|
67
|
0
|
|
|
|
|
|
$self->{_legend} = [@milestones]; |
|
68
|
|
|
|
|
|
|
} |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
# and AoA of : |
|
71
|
|
|
|
|
|
|
# @array = ( |
|
72
|
|
|
|
|
|
|
# ['processFrom','processTo','time'], |
|
73
|
|
|
|
|
|
|
# . |
|
74
|
|
|
|
|
|
|
# . |
|
75
|
|
|
|
|
|
|
# . |
|
76
|
|
|
|
|
|
|
# ) |
|
77
|
|
|
|
|
|
|
# time being units from start of timeline |
|
78
|
|
|
|
|
|
|
sub add_points { |
|
79
|
0
|
|
|
0
|
1
|
|
my ($self,@aoa) = @_; |
|
80
|
0
|
|
|
|
|
|
$self->{_data} = [@aoa]; |
|
81
|
|
|
|
|
|
|
} |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
# write out to disk/stdout |
|
84
|
|
|
|
|
|
|
# but first, this is where the magic happens |
|
85
|
|
|
|
|
|
|
sub write { |
|
86
|
0
|
|
|
0
|
1
|
|
my ($self,$file) = @_; |
|
87
|
0
|
|
|
|
|
|
$self->_draw_grid(); |
|
88
|
0
|
|
|
|
|
|
$self->_draw_data(); |
|
89
|
0
|
|
|
|
|
|
$self->{'_image'}->write(file => $file); |
|
90
|
|
|
|
|
|
|
} |
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
######## internal functions ####### |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
# draw the grid and labels |
|
97
|
|
|
|
|
|
|
sub _draw_grid { |
|
98
|
0
|
|
|
0
|
|
|
my ($self) = @_; |
|
99
|
0
|
|
|
|
|
|
my $image = $self->{_image}; |
|
100
|
|
|
|
|
|
|
|
|
101
|
0
|
|
|
|
|
|
my @v_lines; |
|
102
|
0
|
|
|
|
|
|
my @points = @{ $self->{_legend} }; |
|
|
0
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
# for every $gridSpacing pixes across, draw a vertical line |
|
105
|
0
|
|
|
|
|
|
for (my $i=$self->{'gridXOffset'}; $i <= $self->{'gridWidth'} ;$i += $self->{'gridSpacing'}) { |
|
106
|
0
|
|
|
|
|
|
$image->line(color => $self->{'gridColor'}, x1 => $i, y1 => $self->{'gridYOffset'}, |
|
107
|
|
|
|
|
|
|
x2 => $i, y2 => $self->{'gridYOffset'}+$self->{'gridHeight'}); |
|
108
|
0
|
|
|
|
|
|
push(@v_lines,$i); |
|
109
|
|
|
|
|
|
|
} |
|
110
|
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
# for every $gridSpacing pixes across, draw a horizontal line |
|
112
|
0
|
|
|
|
|
|
for (my $i=$self->{'gridYOffset'}; $i < $self->{'gridYOffset'}+$self->{'gridHeight'} ;$i += $self->{'gridSpacing'}) { |
|
113
|
0
|
|
|
|
|
|
$image->line(color => $self->{'gridColor'}, x1 => $self->{'gridXOffset'}, y1 => $i, |
|
114
|
|
|
|
|
|
|
x2 => $self->{'gridWidth'}, y2 => $i); |
|
115
|
|
|
|
|
|
|
} |
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
# Logic Time: |
|
118
|
|
|
|
|
|
|
# There are scalar(@v_lines) rows in the grid. |
|
119
|
|
|
|
|
|
|
# There are scalar(@points) connection point. |
|
120
|
0
|
|
|
|
|
|
$self->{'px_per_point'} = int( scalar(@v_lines) / (scalar(@points)-1) ) * $self->{'gridSpacing'}; |
|
121
|
0
|
|
|
|
|
|
my $current_px = $self->{'gridXOffset'}; |
|
122
|
0
|
|
|
|
|
|
for (my $pn=0;$pn < scalar(@points);$pn++) { |
|
123
|
0
|
0
|
|
|
|
|
if ($current_px > $v_lines[-1]) { |
|
124
|
0
|
|
|
|
|
|
$current_px = $v_lines[-1]; |
|
125
|
|
|
|
|
|
|
} |
|
126
|
0
|
|
|
|
|
|
$image->box(color => Imager::Color->new(0,0,0), |
|
127
|
|
|
|
|
|
|
xmin => $current_px-1, ymin => $self->{'gridYOffset'}, |
|
128
|
|
|
|
|
|
|
xmax => $current_px+1, ymax => $self->{'gridHeight'}+$self->{'gridYOffset'}, |
|
129
|
|
|
|
|
|
|
filled => 1 |
|
130
|
|
|
|
|
|
|
); |
|
131
|
0
|
|
|
|
|
|
my @bbox = $self->{'labelFont'}->bounding_box(string => $points[$pn]); |
|
132
|
0
|
|
|
|
|
|
$image->string(font => $self->{'labelFont'}, |
|
133
|
|
|
|
|
|
|
text => $points[$pn], |
|
134
|
|
|
|
|
|
|
x => $current_px-(($bbox[2]-$bbox[0])/2), # current line/2 |
|
135
|
|
|
|
|
|
|
y => $self->{'gridYOffset'}+$self->{'gridHeight'}+($bbox[3]), # grid + letter height |
|
136
|
|
|
|
|
|
|
size => $self->{'labelSize'}, |
|
137
|
|
|
|
|
|
|
color => $self->{'labelColor'} |
|
138
|
|
|
|
|
|
|
); |
|
139
|
0
|
|
|
|
|
|
$self->{_label_to_x_offset}{$points[$pn]} = $current_px; |
|
140
|
0
|
|
|
|
|
|
$current_px += $self->{'px_per_point'}; |
|
141
|
|
|
|
|
|
|
} |
|
142
|
|
|
|
|
|
|
|
|
143
|
0
|
|
|
|
|
|
$image->string( |
|
144
|
|
|
|
|
|
|
font => $self->{'labelFont'}, |
|
145
|
|
|
|
|
|
|
size => $self->{'labelSize'}, |
|
146
|
|
|
|
|
|
|
color => $self->{'labelColor'}, |
|
147
|
|
|
|
|
|
|
text => sprintf($self->{dataFormat},0), |
|
148
|
|
|
|
|
|
|
x => $self->{'gridWidth'}, |
|
149
|
|
|
|
|
|
|
y => $self->{'gridYOffset'}, |
|
150
|
|
|
|
|
|
|
); |
|
151
|
0
|
|
0
|
|
|
|
$image->string( |
|
152
|
|
|
|
|
|
|
font => $self->{'labelFont'}, |
|
153
|
|
|
|
|
|
|
size => $self->{'labelSize'}, |
|
154
|
|
|
|
|
|
|
color => $self->{'labelColor'}, |
|
155
|
|
|
|
|
|
|
text => sprintf($self->{dataFormat},($self->{'maxTime'} || $self->{_data}[-1][2])), |
|
156
|
|
|
|
|
|
|
x => $self->{'gridWidth'}, |
|
157
|
|
|
|
|
|
|
y => $self->{'gridHeight'}+$self->{'gridYOffset'}, |
|
158
|
|
|
|
|
|
|
); |
|
159
|
|
|
|
|
|
|
} |
|
160
|
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
sub _draw_data { |
|
162
|
0
|
|
|
0
|
|
|
my ($self) = @_; |
|
163
|
0
|
0
|
|
|
|
|
if (! $self->{'px_per_point'}) { |
|
164
|
0
|
|
|
|
|
|
$self->_draw_grid(); |
|
165
|
|
|
|
|
|
|
} |
|
166
|
0
|
|
|
|
|
|
my $image = $self->{'_image'}; |
|
167
|
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
# ok, more logic : |
|
169
|
|
|
|
|
|
|
# the grid is $self->{'gridHeight'} pixes high |
|
170
|
|
|
|
|
|
|
# the highest scale needed is $self->{'maxTime'} || $self->{_data}[-1][2] |
|
171
|
|
|
|
|
|
|
# there is no negative time, the scale begins at 0 |
|
172
|
|
|
|
|
|
|
# so ... |
|
173
|
|
|
|
|
|
|
# |
|
174
|
|
|
|
|
|
|
# gridHeight/maxTime pixels per second |
|
175
|
|
|
|
|
|
|
|
|
176
|
0
|
|
0
|
|
|
|
my $px_per_sec = ($self->{'gridHeight'}/($self->{'maxTime'} || $self->{_data}[-1][2])); |
|
177
|
0
|
|
|
|
|
|
foreach my $aref (@{ $self->{_data} }) { |
|
|
0
|
|
|
|
|
|
|
|
178
|
0
|
|
|
|
|
|
my $from = $aref->[0]; |
|
179
|
0
|
|
|
|
|
|
my $to = $aref->[1]; |
|
180
|
0
|
|
|
|
|
|
my $time = $aref->[2]; |
|
181
|
|
|
|
|
|
|
|
|
182
|
0
|
|
|
|
|
|
my $fromX = $self->{_label_to_x_offset}{$from}; |
|
183
|
0
|
|
|
|
|
|
my $toX = $self->{_label_to_x_offset}{$to}; |
|
184
|
0
|
|
|
|
|
|
my $timeY = $px_per_sec * $time; |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
#print "[$fromX,$timeY] -> [$toX,$timeY]\n"; |
|
187
|
0
|
|
|
|
|
|
$image->line(color => $self->{'dataColor'}, |
|
188
|
|
|
|
|
|
|
x1 => $fromX , y1 => $timeY, |
|
189
|
|
|
|
|
|
|
x2 => $toX , y2 => $timeY, |
|
190
|
|
|
|
|
|
|
); |
|
191
|
|
|
|
|
|
|
|
|
192
|
0
|
|
|
|
|
|
my $dlX; |
|
193
|
0
|
|
|
|
|
|
my @bbox = $self->{'labelFont'}->bounding_box(string => sprintf($self->{'dataFormat'},$time)); |
|
194
|
0
|
|
|
|
|
|
my $dlY = $timeY; |
|
195
|
0
|
0
|
|
|
|
|
if ($self->{'dataLabelSide'} eq 'left') { |
|
196
|
0
|
0
|
|
|
|
|
$dlX = ( $fromX < $toX ? $fromX : $toX ) - 5 - ($bbox[2]-$bbox[0]); |
|
197
|
|
|
|
|
|
|
} else { |
|
198
|
0
|
0
|
|
|
|
|
$dlX = ( $fromX > $toX ? $fromX : $toX ) + 5; |
|
199
|
|
|
|
|
|
|
} |
|
200
|
0
|
|
|
|
|
|
$image->string(font => $self->{'labelFont'}, |
|
201
|
|
|
|
|
|
|
size => $self->{'labelSize'}, |
|
202
|
|
|
|
|
|
|
color => $self->{'labelColor'}, |
|
203
|
|
|
|
|
|
|
text => sprintf($self->{'dataFormat'},$time), |
|
204
|
|
|
|
|
|
|
x => $dlX, |
|
205
|
|
|
|
|
|
|
y => $dlY, |
|
206
|
|
|
|
|
|
|
); |
|
207
|
|
|
|
|
|
|
|
|
208
|
0
|
0
|
|
|
|
|
if ($self->{'showArrowheads'}) { |
|
209
|
0
|
|
|
|
|
|
my ($ahBkX,$ahBkY1,$ahBkY2); |
|
210
|
0
|
0
|
|
|
|
|
if ($toX > $fromX) { |
|
211
|
0
|
|
|
|
|
|
$ahBkX = $toX-3; |
|
212
|
|
|
|
|
|
|
} else { |
|
213
|
0
|
|
|
|
|
|
$ahBkX = $toX+3; |
|
214
|
|
|
|
|
|
|
} |
|
215
|
0
|
|
|
|
|
|
$ahBkY1 = $timeY-2; |
|
216
|
0
|
|
|
|
|
|
$ahBkY2 = $timeY+2; |
|
217
|
|
|
|
|
|
|
# ploygon's are anti-aliased ... and that core's my Imager :( |
|
218
|
|
|
|
|
|
|
#$image->polygon(x => [$toX,$ahBkX,$ahBkX],y => [$timeY,$ahBkY1,$ahBkY2],color => $self->{'dataColor'}); |
|
219
|
0
|
|
|
|
|
|
$image->polyline(x => [$toX,$ahBkX,$ahBkX,$toX],y => [$timeY,$ahBkY1,$ahBkY2,$timeY],color => $self->{'dataColor'}); |
|
220
|
|
|
|
|
|
|
} |
|
221
|
|
|
|
|
|
|
} |
|
222
|
|
|
|
|
|
|
} |
|
223
|
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
1; |
|
225
|
|
|
|
|
|
|
__END__ |