| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package SVGGraph; |
|
2
|
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
11409
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
37
|
|
|
4
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
33
|
|
|
5
|
1
|
|
|
1
|
|
1045
|
use utf8; |
|
|
1
|
|
|
|
|
14
|
|
|
|
1
|
|
|
|
|
6
|
|
|
6
|
|
|
|
|
|
|
our $VERSION = '0.07'; |
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
sub new() |
|
9
|
|
|
|
|
|
|
{ |
|
10
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
11
|
0
|
|
|
|
|
|
return bless {}, $self; |
|
12
|
|
|
|
|
|
|
} |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
sub CreateGraph() |
|
15
|
|
|
|
|
|
|
{ |
|
16
|
|
|
|
|
|
|
### First element of @_ is a reference to the element that called this subroutine |
|
17
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
18
|
|
|
|
|
|
|
### Second is a reference to a hash with options |
|
19
|
0
|
|
|
|
|
|
my $options = shift; |
|
20
|
|
|
|
|
|
|
### The options passed in the anonymous hash are optional so create a default value first |
|
21
|
0
|
|
|
|
|
|
my $horiUnitDistance = 20; |
|
22
|
0
|
0
|
|
|
|
|
if ($$options{'horiunitdistance'}) |
|
23
|
|
|
|
|
|
|
{ |
|
24
|
0
|
|
|
|
|
|
$horiUnitDistance = $$options{'horiunitdistance'}; |
|
25
|
|
|
|
|
|
|
} |
|
26
|
0
|
|
|
|
|
|
my $graphType = 'spline'; |
|
27
|
0
|
0
|
|
|
|
|
if ($$options{'graphtype'}) |
|
28
|
|
|
|
|
|
|
{ |
|
29
|
0
|
|
|
|
|
|
$graphType = $$options{'graphtype'}; |
|
30
|
|
|
|
|
|
|
} |
|
31
|
|
|
|
|
|
|
### The rest are references to arrays with references to arrays with x and y values |
|
32
|
0
|
|
|
|
|
|
my @xyArrayRefs = @_; |
|
33
|
|
|
|
|
|
|
### Check if the color ($xyArrayRefs[$i]->[3]) is provided. If not, choose black |
|
34
|
0
|
|
|
|
|
|
for (my $i = 0; $i < @xyArrayRefs; $i++) |
|
35
|
|
|
|
|
|
|
{ |
|
36
|
0
|
0
|
|
|
|
|
unless ($xyArrayRefs[$i]->[3]) |
|
37
|
|
|
|
|
|
|
{ |
|
38
|
0
|
|
|
|
|
|
$xyArrayRefs[$i]->[3] = '#000000'; |
|
39
|
|
|
|
|
|
|
} |
|
40
|
|
|
|
|
|
|
} |
|
41
|
|
|
|
|
|
|
### Declare the $minX as the lowest value of x in the arrays, same for $minY, $maxX and $maxY |
|
42
|
0
|
|
|
|
|
|
my $minX = $xyArrayRefs[0]->[0]->[0]; ### Equivalent to ${${$xyArrayRefs[0]}[0]}[0]; |
|
43
|
0
|
|
|
|
|
|
my $minY = $xyArrayRefs[0]->[1]->[0]; |
|
44
|
0
|
|
|
|
|
|
my $maxX = $minX; |
|
45
|
0
|
|
|
|
|
|
my $maxY = $minY; |
|
46
|
|
|
|
|
|
|
### Then really search for the lowest and highest value of x and y |
|
47
|
0
|
|
|
|
|
|
for (my $i = 0; $i < @xyArrayRefs; $i++) |
|
48
|
|
|
|
|
|
|
{ |
|
49
|
0
|
|
|
|
|
|
for (my $j = 0; $j < @{$xyArrayRefs[$i]->[0]}; $j++) |
|
|
0
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
{ |
|
51
|
0
|
0
|
|
|
|
|
if ($xyArrayRefs[$i]->[0]->[$j] > $maxX) |
|
52
|
|
|
|
|
|
|
{ |
|
53
|
0
|
|
|
|
|
|
$maxX = $xyArrayRefs[$i]->[0]->[$j]; |
|
54
|
|
|
|
|
|
|
} |
|
55
|
0
|
0
|
|
|
|
|
if ($xyArrayRefs[$i]->[0]->[$j] < $minX) |
|
56
|
|
|
|
|
|
|
{ |
|
57
|
0
|
|
|
|
|
|
$minX = $xyArrayRefs[$i]->[0]->[$j]; |
|
58
|
|
|
|
|
|
|
} |
|
59
|
0
|
0
|
|
|
|
|
if ($xyArrayRefs[$i]->[1]->[$j] > $maxY) |
|
60
|
|
|
|
|
|
|
{ |
|
61
|
0
|
|
|
|
|
|
$maxY = $xyArrayRefs[$i]->[1]->[$j]; |
|
62
|
|
|
|
|
|
|
} |
|
63
|
0
|
0
|
|
|
|
|
if ($xyArrayRefs[$i]->[1]->[$j] < $minY) |
|
64
|
|
|
|
|
|
|
{ |
|
65
|
0
|
|
|
|
|
|
$minY = $xyArrayRefs[$i]->[1]->[$j]; |
|
66
|
|
|
|
|
|
|
} |
|
67
|
|
|
|
|
|
|
} |
|
68
|
|
|
|
|
|
|
} |
|
69
|
|
|
|
|
|
|
### If max equals min, change them artificially |
|
70
|
0
|
0
|
|
|
|
|
if ($maxX == $minX) |
|
71
|
|
|
|
|
|
|
{ |
|
72
|
0
|
|
|
|
|
|
$maxX += 1; |
|
73
|
|
|
|
|
|
|
} |
|
74
|
0
|
0
|
|
|
|
|
if ($maxY == $minY) |
|
75
|
|
|
|
|
|
|
{ |
|
76
|
0
|
|
|
|
|
|
$maxY += 1; |
|
77
|
|
|
|
|
|
|
} |
|
78
|
|
|
|
|
|
|
### Calculate all dimensions neccessary to create the Graph |
|
79
|
|
|
|
|
|
|
### Height of the total svg image in pixels: |
|
80
|
0
|
|
|
|
|
|
my $imageHeight = 400; |
|
81
|
0
|
0
|
|
|
|
|
if ($$options{'imageheight'}) |
|
82
|
|
|
|
|
|
|
{ |
|
83
|
0
|
|
|
|
|
|
$imageHeight = $$options{'imageheight'}; |
|
84
|
|
|
|
|
|
|
} |
|
85
|
|
|
|
|
|
|
### Width of the verticabar or dots in the graph |
|
86
|
0
|
|
|
|
|
|
my $barWidth = 3; |
|
87
|
0
|
0
|
|
|
|
|
if ($$options{'barwidth'}) |
|
88
|
|
|
|
|
|
|
{ |
|
89
|
0
|
|
|
|
|
|
$barWidth = $$options{'barwidth'}; |
|
90
|
|
|
|
|
|
|
} |
|
91
|
|
|
|
|
|
|
### Distance between the sides of the gris and the sides of the image: |
|
92
|
0
|
|
|
|
|
|
my $cornerDistance = 50; |
|
93
|
|
|
|
|
|
|
### Since svg counts from the top left corner of the image, we translate all coordinates vertically in pixels: |
|
94
|
0
|
|
|
|
|
|
my $vertTranslate = $imageHeight - $cornerDistance; |
|
95
|
|
|
|
|
|
|
### The width of the grid in pixels: |
|
96
|
0
|
|
|
|
|
|
my $gridWidth = $horiUnitDistance * ($maxX - $minX); |
|
97
|
|
|
|
|
|
|
### The height of the grid in pixels: |
|
98
|
0
|
|
|
|
|
|
my $gridHeight = $imageHeight - 2 * $cornerDistance; |
|
99
|
|
|
|
|
|
|
### The width of the whole svg image: |
|
100
|
0
|
|
|
|
|
|
my $imageWidth = $gridWidth + (4 * $cornerDistance); |
|
101
|
|
|
|
|
|
|
### The horizontal space between vertical gridlines in pixels: |
|
102
|
0
|
|
|
|
|
|
my $xGridDistance = 20; |
|
103
|
|
|
|
|
|
|
### The vertical space between horizontal gridlines in pixels: |
|
104
|
0
|
|
|
|
|
|
my $yGridDistance = 30; |
|
105
|
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
### Now initiate the svg graph by declaring some general stuff. |
|
107
|
0
|
|
|
|
|
|
my $svg .= <<" EOF"; |
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
EOF |
|
113
|
0
|
0
|
|
|
|
|
if ($graphType eq 'spline') |
|
114
|
|
|
|
|
|
|
{ |
|
115
|
0
|
|
|
|
|
|
for (my $i = 0; $i < @xyArrayRefs; $i++) |
|
116
|
|
|
|
|
|
|
{ |
|
117
|
0
|
|
|
|
|
|
$svg .= $self->CreateDot(0, 0, $barWidth, $xyArrayRefs[$i]->[3], $i); |
|
118
|
|
|
|
|
|
|
} |
|
119
|
|
|
|
|
|
|
} |
|
120
|
0
|
|
|
|
|
|
$svg .= <<" EOF"; |
|
121
|
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
EOF |
|
143
|
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
### make x- and y axes |
|
145
|
0
|
|
|
|
|
|
$svg .= "\n"; |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
### print numbers on y axis and horizontal gridlines |
|
148
|
|
|
|
|
|
|
### First calculate the width between the gridlines in y-units, not in pixels |
|
149
|
0
|
|
|
|
|
|
my $deltaYUnits = $self->NaturalRound ($yGridDistance * ($maxY - $minY) / $gridHeight); |
|
150
|
|
|
|
|
|
|
### Adjust $minX and $maxX so the gridlines and numbers startand end in a whole and nice number. |
|
151
|
0
|
|
|
|
|
|
$minY = int ($minY / $deltaYUnits - 0.999999999999) * $deltaYUnits; |
|
152
|
0
|
|
|
|
|
|
$maxY = int ($maxY / $deltaYUnits + 0.999999999999) * $deltaYUnits; |
|
153
|
|
|
|
|
|
|
### Calculate the number of pixels each units stands for. |
|
154
|
0
|
|
|
|
|
|
my $yPixelsPerUnit = ($gridHeight / ($maxY - $minY)); |
|
155
|
0
|
|
|
|
|
|
my $deltaYPixels = $deltaYUnits * $yPixelsPerUnit; |
|
156
|
|
|
|
|
|
|
### Calculate the amount of gridlines and therefore the amount of numbers on the y-axis |
|
157
|
0
|
|
|
|
|
|
my $yNumberOfNumbers = int ($gridHeight / $deltaYPixels) + 1; |
|
158
|
|
|
|
|
|
|
### Draw the numbers and the gridlines |
|
159
|
0
|
|
|
|
|
|
for (my $i = 0; $i < $yNumberOfNumbers; $i++) |
|
160
|
|
|
|
|
|
|
{ |
|
161
|
0
|
|
|
|
|
|
my $YValue = sprintf ("%1.2f", (-1 * $i * $deltaYPixels)) + 0; |
|
162
|
|
|
|
|
|
|
### numbers |
|
163
|
0
|
|
|
|
|
|
$svg .= "" . ($minY + $i * $deltaYUnits) . "\n"; |
|
164
|
|
|
|
|
|
|
### gridline |
|
165
|
0
|
0
|
|
|
|
|
if ($i != 0) |
|
166
|
|
|
|
|
|
|
{ |
|
167
|
0
|
|
|
|
|
|
$svg .= "\n"; |
|
168
|
|
|
|
|
|
|
} |
|
169
|
|
|
|
|
|
|
} |
|
170
|
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
### print numbers on x axis and vertical gridlines |
|
172
|
0
|
|
|
|
|
|
my $deltaXUnits = $self->NaturalRound ($xGridDistance * ($maxX - $minX) / $gridWidth); |
|
173
|
0
|
|
|
|
|
|
my $xPixelsPerUnit = ($gridWidth / ($maxX - $minX)); |
|
174
|
0
|
|
|
|
|
|
my $deltaXPixels = $deltaXUnits * $xPixelsPerUnit; |
|
175
|
0
|
|
|
|
|
|
my $xNumberOfNumbers = int ($gridWidth / $deltaXPixels) + 1; |
|
176
|
0
|
|
|
|
|
|
for (my $i = 0; $i < $xNumberOfNumbers; $i++) |
|
177
|
|
|
|
|
|
|
{ |
|
178
|
0
|
|
|
|
|
|
my $XValue = sprintf ("%1.2f", ($i * $deltaXPixels)) + 0; |
|
179
|
|
|
|
|
|
|
### numbers |
|
180
|
0
|
|
|
|
|
|
$svg .= "" . ($minX + $i * $deltaXUnits) . "\n"; |
|
181
|
|
|
|
|
|
|
### gridline |
|
182
|
0
|
0
|
|
|
|
|
if ($i != 0) |
|
183
|
|
|
|
|
|
|
{ |
|
184
|
0
|
|
|
|
|
|
$svg .= "\n"; |
|
185
|
|
|
|
|
|
|
} |
|
186
|
|
|
|
|
|
|
} |
|
187
|
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
### print measurepoints (dots) (data) (coordinates) |
|
189
|
|
|
|
|
|
|
### Spline |
|
190
|
0
|
0
|
|
|
|
|
if ($graphType eq 'spline') |
|
|
|
0
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
{ |
|
192
|
0
|
|
|
|
|
|
for (my $i = 0; $i < @xyArrayRefs; $i++) |
|
193
|
|
|
|
|
|
|
{ |
|
194
|
0
|
|
|
|
|
|
my $dots; |
|
195
|
0
|
|
|
|
|
|
for (my $dotNumber = 0; $dotNumber < @{$xyArrayRefs[$i]->[0]}; $dotNumber++) |
|
|
0
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
{ |
|
197
|
0
|
|
|
|
|
|
my $dotX = $horiUnitDistance * ($xyArrayRefs[$i]->[0]->[$dotNumber] - $minX); |
|
198
|
0
|
|
|
|
|
|
my $dotY = sprintf ("%1.2f", -1 * $yPixelsPerUnit * ($xyArrayRefs[$i]->[1]->[$dotNumber] - $minY)) + 0; |
|
199
|
0
|
|
|
|
|
|
$dots .= "\n"; |
|
200
|
0
|
0
|
|
|
|
|
if ($dotNumber == 0) |
|
201
|
|
|
|
|
|
|
{ |
|
202
|
0
|
|
|
|
|
|
$svg .= "
|
|
203
|
|
|
|
|
|
|
} |
|
204
|
|
|
|
|
|
|
else |
|
205
|
|
|
|
|
|
|
{ |
|
206
|
0
|
|
|
|
|
|
$svg .= " L$dotX $dotY"; |
|
207
|
|
|
|
|
|
|
} |
|
208
|
|
|
|
|
|
|
} |
|
209
|
0
|
|
|
|
|
|
$svg .= "\" style=\"fill: none; stroke: " . $xyArrayRefs[$i]->[3] . "; stroke-width:2\"/>\n$dots"; |
|
210
|
|
|
|
|
|
|
} |
|
211
|
|
|
|
|
|
|
} |
|
212
|
|
|
|
|
|
|
### Vertical Bars |
|
213
|
|
|
|
|
|
|
elsif ($graphType eq 'verticalbars') |
|
214
|
|
|
|
|
|
|
{ |
|
215
|
0
|
|
|
|
|
|
for (my $dotNumber = 0; $dotNumber < @{$xyArrayRefs[0]->[0]}; $dotNumber++) |
|
|
0
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
{ |
|
217
|
|
|
|
|
|
|
### The longest bars must be drawn first, so that the shorter bars are drwan on top of the longer. |
|
218
|
|
|
|
|
|
|
### So we sort $i (the number of the graph) to the length of the bar for each point. |
|
219
|
0
|
|
|
|
|
|
foreach my $i (sort {$xyArrayRefs[$b]->[1]->[$dotNumber] <=> $xyArrayRefs[$a]->[1]->[$dotNumber]} (0 .. $#xyArrayRefs)) |
|
|
0
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
{ |
|
221
|
0
|
|
|
|
|
|
my $lineX = $horiUnitDistance * ($xyArrayRefs[$i]->[0]->[$dotNumber] - $minX); |
|
222
|
0
|
|
|
|
|
|
my $lineY1 = 0; |
|
223
|
0
|
0
|
0
|
|
|
|
if (($minY < 0) && ($maxY > 0)) |
|
|
|
0
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
{ |
|
225
|
0
|
|
|
|
|
|
$lineY1 = $yPixelsPerUnit * $minY; |
|
226
|
|
|
|
|
|
|
} |
|
227
|
|
|
|
|
|
|
elsif ($maxY < 0) |
|
228
|
|
|
|
|
|
|
{ |
|
229
|
0
|
|
|
|
|
|
$lineY1 = -1 * 1; |
|
230
|
|
|
|
|
|
|
} |
|
231
|
0
|
|
|
|
|
|
my $lineY2 = sprintf ("%1.2f", -1 * $yPixelsPerUnit * ($xyArrayRefs[$i]->[1]->[$dotNumber] - $minY)) + 0; |
|
232
|
0
|
|
|
|
|
|
$svg .= "[3] . ";stroke-width:$barWidth;\"/>\n"; |
|
233
|
|
|
|
|
|
|
} |
|
234
|
|
|
|
|
|
|
} |
|
235
|
|
|
|
|
|
|
} |
|
236
|
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
### print Title, Labels and Legend |
|
238
|
|
|
|
|
|
|
### Title |
|
239
|
0
|
0
|
|
|
|
|
if ($$options{'title'}) |
|
240
|
|
|
|
|
|
|
{ |
|
241
|
0
|
|
|
|
|
|
my $titleStyle = 'font-size:24;'; |
|
242
|
0
|
0
|
|
|
|
|
if ($$options{'titlestyle'}) |
|
243
|
|
|
|
|
|
|
{ |
|
244
|
0
|
|
|
|
|
|
$titleStyle = $self->XMLEscape($$options{'titlestyle'}); |
|
245
|
|
|
|
|
|
|
} |
|
246
|
0
|
|
|
|
|
|
$svg .= "" . $self->XMLEscape($$options{'title'}) . "\n"; |
|
247
|
|
|
|
|
|
|
} |
|
248
|
|
|
|
|
|
|
### x-axis label |
|
249
|
0
|
0
|
|
|
|
|
if ($$options{'xlabel'}) |
|
250
|
|
|
|
|
|
|
{ |
|
251
|
0
|
|
|
|
|
|
my $xLabelStyle = 'font-size:16;'; |
|
252
|
0
|
0
|
|
|
|
|
if ($$options{'xlabelstyle'}) |
|
253
|
|
|
|
|
|
|
{ |
|
254
|
0
|
|
|
|
|
|
$xLabelStyle = $self->XMLEscape($$options{'xlabelstyle'}); |
|
255
|
|
|
|
|
|
|
} |
|
256
|
0
|
|
|
|
|
|
$svg .= "" . $self->XMLEscape($$options{'xlabel'}) . "\n"; |
|
257
|
|
|
|
|
|
|
} |
|
258
|
|
|
|
|
|
|
### y-axis label |
|
259
|
0
|
0
|
|
|
|
|
if ($$options{'ylabel'}) |
|
260
|
|
|
|
|
|
|
{ |
|
261
|
0
|
|
|
|
|
|
my $yLabelStyle = 'font-size:16;'; |
|
262
|
0
|
0
|
|
|
|
|
if ($$options{'ylabelstyle'}) |
|
263
|
|
|
|
|
|
|
{ |
|
264
|
0
|
|
|
|
|
|
$yLabelStyle = $self->XMLEscape($$options{'ylabelstyle'}); |
|
265
|
|
|
|
|
|
|
} |
|
266
|
0
|
|
|
|
|
|
$svg .= "" . $self->XMLEscape($$options{'ylabel'}) . "\n"; |
|
267
|
|
|
|
|
|
|
} |
|
268
|
|
|
|
|
|
|
### Legend |
|
269
|
0
|
|
|
|
|
|
my $legendOffset = ($cornerDistance + $gridWidth + 10) . ", $cornerDistance"; |
|
270
|
0
|
0
|
|
|
|
|
if ($$options{'legendoffset'}) |
|
271
|
|
|
|
|
|
|
{ |
|
272
|
0
|
|
|
|
|
|
$legendOffset = $self->XMLEscape($$options{'legendoffset'}); |
|
273
|
|
|
|
|
|
|
} |
|
274
|
0
|
|
|
|
|
|
$svg .= "\n\n"; |
|
275
|
0
|
|
|
|
|
|
for (my $i = 0; $i < @xyArrayRefs; $i++) |
|
276
|
|
|
|
|
|
|
{ |
|
277
|
0
|
0
|
|
|
|
|
if ($xyArrayRefs[$i]->[2]) |
|
278
|
|
|
|
|
|
|
{ |
|
279
|
0
|
|
|
|
|
|
my $y = 12 * $i; |
|
280
|
0
|
0
|
|
|
|
|
if ($graphType eq 'spline') |
|
281
|
|
|
|
|
|
|
{ |
|
282
|
|
|
|
|
|
|
### The line |
|
283
|
0
|
|
|
|
|
|
$svg .= "[3] . "\"/>\n"; |
|
284
|
|
|
|
|
|
|
### The dot |
|
285
|
0
|
|
|
|
|
|
$svg .= $self->CreateDot(8, $y, 3, $xyArrayRefs[$i]->[3], $i); |
|
286
|
|
|
|
|
|
|
} |
|
287
|
|
|
|
|
|
|
### The text |
|
288
|
0
|
|
|
|
|
|
$svg .= "[3] . "\">" . $xyArrayRefs[$i]->[2] . "\n"; |
|
289
|
|
|
|
|
|
|
} |
|
290
|
|
|
|
|
|
|
} |
|
291
|
0
|
|
|
|
|
|
$svg .= "\n\n"; |
|
292
|
0
|
|
|
|
|
|
return $svg; |
|
293
|
|
|
|
|
|
|
} |
|
294
|
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
### CreateDot is a subroutine that creates the svg code for different |
|
296
|
|
|
|
|
|
|
### kinds of dots used in the spline graph type: circles, squares, triangles and more. |
|
297
|
|
|
|
|
|
|
sub CreateDot($$$$$) |
|
298
|
|
|
|
|
|
|
{ |
|
299
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
300
|
0
|
|
|
|
|
|
my $x = shift; |
|
301
|
0
|
|
|
|
|
|
my $y = shift; |
|
302
|
0
|
|
|
|
|
|
my $r = shift; |
|
303
|
0
|
|
|
|
|
|
my $color = shift; |
|
304
|
0
|
|
|
|
|
|
$color = $self->DarkenHexRGB($color); |
|
305
|
0
|
|
|
|
|
|
my $dotNumber = shift; |
|
306
|
0
|
|
|
|
|
|
my $d = 2 * $r; |
|
307
|
0
|
|
|
|
|
|
my $negr = -1 * $r; |
|
308
|
0
|
|
|
|
|
|
my $svg; |
|
309
|
|
|
|
|
|
|
### Circle |
|
310
|
0
|
0
|
|
|
|
|
if ($dotNumber == 0) |
|
311
|
|
|
|
|
|
|
{ |
|
312
|
0
|
|
|
|
|
|
$svg = "\n"; |
|
313
|
|
|
|
|
|
|
} |
|
314
|
|
|
|
|
|
|
### Stars |
|
315
|
|
|
|
|
|
|
else |
|
316
|
|
|
|
|
|
|
{ |
|
317
|
0
|
|
|
|
|
|
$svg .= "
|
|
318
|
0
|
|
|
|
|
|
for (my $i = 1; $i <= (2*$dotNumber+2); $i++) |
|
319
|
|
|
|
|
|
|
{ |
|
320
|
0
|
0
|
|
|
|
|
my $radius = ($i % 2) ? $r*1.5 : $r/2; |
|
321
|
0
|
|
|
|
|
|
my $pi = atan2(1,1) * 4; |
|
322
|
0
|
|
|
|
|
|
my $alpha = $i * ($pi / ($dotNumber + 1)); |
|
323
|
0
|
|
|
|
|
|
my $xi = $x + $radius * cos($alpha); |
|
324
|
0
|
|
|
|
|
|
my $yi = $y + $radius * sin($alpha); |
|
325
|
0
|
0
|
|
|
|
|
$svg .= ($i == 1) ? "M" : "L"; |
|
326
|
0
|
|
|
|
|
|
$svg .= sprintf (" %1.3f ", $xi) . (sprintf (" %1.3f ", $yi) + 0); |
|
327
|
|
|
|
|
|
|
} |
|
328
|
0
|
|
|
|
|
|
$svg .= "z\" style=\"fill: $color; stroke: $color;\"/>\n"; |
|
329
|
|
|
|
|
|
|
} |
|
330
|
0
|
|
|
|
|
|
return $svg; |
|
331
|
|
|
|
|
|
|
} |
|
332
|
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
### NaturalRound is a subroutine that rounds a number to 1, 2, 5 or 10 times its order |
|
334
|
|
|
|
|
|
|
### So 110.34 becomes 100 |
|
335
|
|
|
|
|
|
|
### 3.1234 becomes 2 |
|
336
|
|
|
|
|
|
|
### 40 becomes 50 |
|
337
|
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
sub NaturalRound($) |
|
339
|
|
|
|
|
|
|
{ |
|
340
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
341
|
0
|
|
|
|
|
|
my $numberToRound = shift; |
|
342
|
0
|
|
|
|
|
|
my $rounded; |
|
343
|
0
|
|
|
|
|
|
my $order = int (log ($numberToRound) / log (10)); |
|
344
|
0
|
|
|
|
|
|
my $remainder = $numberToRound / 10**$order; |
|
345
|
0
|
0
|
|
|
|
|
if ($remainder < 1.4) |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
{ |
|
347
|
0
|
|
|
|
|
|
$rounded = 10**$order; |
|
348
|
|
|
|
|
|
|
} |
|
349
|
|
|
|
|
|
|
elsif ($remainder < 3.2) |
|
350
|
|
|
|
|
|
|
{ |
|
351
|
0
|
|
|
|
|
|
$rounded = 2 * 10**$order; |
|
352
|
|
|
|
|
|
|
} |
|
353
|
|
|
|
|
|
|
elsif ($remainder < 7.1) |
|
354
|
|
|
|
|
|
|
{ |
|
355
|
0
|
|
|
|
|
|
$rounded = 5 * 10**$order; |
|
356
|
|
|
|
|
|
|
} |
|
357
|
|
|
|
|
|
|
else |
|
358
|
|
|
|
|
|
|
{ |
|
359
|
0
|
|
|
|
|
|
$rounded = 10 * 10**$order; |
|
360
|
|
|
|
|
|
|
} |
|
361
|
|
|
|
|
|
|
} |
|
362
|
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
### DarkenHexRGB is a subroutine that makes a rgb color value darker |
|
364
|
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
sub DarkenHexRGB($) |
|
366
|
|
|
|
|
|
|
{ |
|
367
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
368
|
0
|
|
|
|
|
|
my $hexString = shift; |
|
369
|
0
|
|
|
|
|
|
my $darkHexString; |
|
370
|
0
|
0
|
|
|
|
|
if ($hexString =~ m/^\#/) |
|
371
|
|
|
|
|
|
|
{ |
|
372
|
0
|
|
|
|
|
|
$darkHexString = '#'; |
|
373
|
|
|
|
|
|
|
} |
|
374
|
0
|
0
|
|
|
|
|
if ($hexString =~ m/^\#?[0-9a-f]{6}$/i) |
|
375
|
|
|
|
|
|
|
{ |
|
376
|
0
|
|
|
|
|
|
while ($hexString =~ m/([0-9a-f][0-9a-f])/ig) |
|
377
|
|
|
|
|
|
|
{ |
|
378
|
0
|
|
|
|
|
|
$darkHexString .= sprintf "%02lx", int(hex($1)/2); |
|
379
|
|
|
|
|
|
|
} |
|
380
|
0
|
|
|
|
|
|
return $darkHexString; |
|
381
|
|
|
|
|
|
|
} |
|
382
|
|
|
|
|
|
|
else |
|
383
|
|
|
|
|
|
|
{ |
|
384
|
0
|
|
|
|
|
|
return $hexString; |
|
385
|
|
|
|
|
|
|
} |
|
386
|
|
|
|
|
|
|
} |
|
387
|
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
sub NegateHexadecimalRGB($) |
|
389
|
|
|
|
|
|
|
{ |
|
390
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
391
|
0
|
|
|
|
|
|
my $hexString = shift; |
|
392
|
0
|
|
|
|
|
|
my $negHexString; |
|
393
|
0
|
0
|
|
|
|
|
if ($hexString =~ m/^\#/) |
|
394
|
|
|
|
|
|
|
{ |
|
395
|
0
|
|
|
|
|
|
$negHexString = '#'; |
|
396
|
|
|
|
|
|
|
} |
|
397
|
0
|
|
|
|
|
|
while ($hexString =~ m/([0-9a-f]{2})/ig) |
|
398
|
|
|
|
|
|
|
{ |
|
399
|
0
|
|
|
|
|
|
$negHexString .= sprintf "%02lx", (255 - hex($1)); |
|
400
|
|
|
|
|
|
|
} |
|
401
|
0
|
|
|
|
|
|
return $negHexString; |
|
402
|
|
|
|
|
|
|
} |
|
403
|
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
### XMLEscape is a subroutine that converts special XML characters to their xml encoding character. |
|
405
|
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
sub XMLEscape($) |
|
407
|
|
|
|
|
|
|
{ |
|
408
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
409
|
0
|
|
|
|
|
|
my $string = shift; |
|
410
|
0
|
0
|
|
|
|
|
unless (defined ($string)) |
|
411
|
|
|
|
|
|
|
{ |
|
412
|
0
|
|
|
|
|
|
$string = ''; |
|
413
|
|
|
|
|
|
|
} |
|
414
|
0
|
|
|
|
|
|
$string =~ s/\&/&/g; |
|
415
|
0
|
|
|
|
|
|
$string =~ s/>/>/g; |
|
416
|
0
|
|
|
|
|
|
$string =~ s/</g; |
|
417
|
0
|
|
|
|
|
|
$string =~ s/\"/"/g; |
|
418
|
0
|
|
|
|
|
|
$string =~ s/\'/'/g; |
|
419
|
|
|
|
|
|
|
#$string =~ s/([\x00-\x1f])/sprintf('%02X;', ord($1))/ge; |
|
420
|
0
|
|
|
|
|
|
$string =~ s/([\x{80}-\x{ffff}])/sprintf('%04X;', ord($1))/ge; |
|
|
0
|
|
|
|
|
|
|
|
421
|
0
|
|
|
|
|
|
return $string; |
|
422
|
|
|
|
|
|
|
} |
|
423
|
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
1; |
|
425
|
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
__END__ |