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__ |