svg-graph19 0.6.2 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,500 +0,0 @@
1
- require 'SVG/Graph/Graph'
2
-
3
- module SVG
4
- module Graph
5
- # === For creating SVG plots of scalar data
6
- #
7
- # = Synopsis
8
- #
9
- # require 'SVG/Graph/Plot'
10
- #
11
- # # Data sets are x,y pairs
12
- # # Note that multiple data sets can differ in length, and that the
13
- # # data in the datasets needn't be in order; they will be ordered
14
- # # by the plot along the X-axis.
15
- # projection = [
16
- # 6, 11, 0, 5, 18, 7, 1, 11, 13, 9, 1, 2, 19, 0, 3, 13,
17
- # 7, 9
18
- # ]
19
- # actual = [
20
- # 0, 18, 8, 15, 9, 4, 18, 14, 10, 2, 11, 6, 14, 12,
21
- # 15, 6, 4, 17, 2, 12
22
- # ]
23
- #
24
- # graph = SVG::Graph::Plot.new({
25
- # :height => 500,
26
- # :width => 300,
27
- # :key => true,
28
- # :scale_x_integers => true,
29
- # :scale_y_integerrs => true,
30
- # })
31
- #
32
- # graph.add_data({
33
- # :data => projection
34
- # :title => 'Projected',
35
- # })
36
- #
37
- # graph.add_data({
38
- # :data => actual,
39
- # :title => 'Actual',
40
- # })
41
- #
42
- # print graph.burn()
43
- #
44
- # = Description
45
- #
46
- # Produces a graph of scalar data.
47
- #
48
- # This object aims to allow you to easily create high quality
49
- # SVG[http://www.w3c.org/tr/svg] scalar plots. You can either use the
50
- # default style sheet or supply your own. Either way there are many options
51
- # which can be configured to give you control over how the graph is
52
- # generated - with or without a key, data elements at each point, title,
53
- # subtitle etc.
54
- #
55
- # = Examples
56
- #
57
- # http://www.germane-software/repositories/public/SVG/test/plot.rb
58
- #
59
- # = Notes
60
- #
61
- # The default stylesheet handles upto 10 data sets, if you
62
- # use more you must create your own stylesheet and add the
63
- # additional settings for the extra data sets. You will know
64
- # if you go over 10 data sets as they will have no style and
65
- # be in black.
66
- #
67
- # Unlike the other types of charts, data sets must contain x,y pairs:
68
- #
69
- # [ 1, 2 ] # A data set with 1 point: (1,2)
70
- # [ 1,2, 5,6] # A data set with 2 points: (1,2) and (5,6)
71
- #
72
- # = See also
73
- #
74
- # * SVG::Graph::Graph
75
- # * SVG::Graph::BarHorizontal
76
- # * SVG::Graph::Bar
77
- # * SVG::Graph::Line
78
- # * SVG::Graph::Pie
79
- # * SVG::Graph::TimeSeries
80
- #
81
- # == Author
82
- #
83
- # Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>
84
- #
85
- # Copyright 2004 Sean E. Russell
86
- # This software is available under the Ruby license[LICENSE.txt]
87
- #
88
- class Plot < Graph
89
-
90
- # In addition to the defaults set by Graph::initialize, sets
91
- # [show_data_values] true
92
- # [show_data_points] true
93
- # [area_fill] false
94
- # [stacked] false
95
- def set_defaults
96
- init_with(
97
- :show_data_values => true,
98
- :show_data_points => true,
99
- :area_fill => false,
100
- :stacked => false
101
- )
102
- self.top_align = self.right_align = self.top_font = self.right_font = 1
103
- end
104
-
105
- # Determines the scaling for the X axis divisions.
106
- #
107
- # graph.scale_x_divisions = 2
108
- #
109
- # would cause the graph to attempt to generate labels stepped by 2; EG:
110
- # 0,2,4,6,8...
111
- attr_accessor :scale_x_divisions
112
- # Determines the scaling for the Y axis divisions.
113
- #
114
- # graph.scale_y_divisions = 0.5
115
- #
116
- # would cause the graph to attempt to generate labels stepped by 0.5; EG:
117
- # 0, 0.5, 1, 1.5, 2, ...
118
- attr_accessor :scale_y_divisions
119
- # Make the X axis labels integers
120
- attr_accessor :scale_x_integers
121
- # Make the Y axis labels integers
122
- attr_accessor :scale_y_integers
123
- # Fill the area under the line
124
- attr_accessor :area_fill
125
- # Show a small circle on the graph where the line
126
- # goes from one point to the next.
127
- attr_accessor :show_data_points
128
- # Set the minimum value of the X axis
129
- attr_accessor :min_x_value
130
- # Set the minimum value of the Y axis
131
- attr_accessor :min_y_value
132
-
133
-
134
- # Adds data to the plot. The data must be in X,Y pairs; EG
135
- # [ 1, 2 ] # A data set with 1 point: (1,2)
136
- # [ 1,2, 5,6] # A data set with 2 points: (1,2) and (5,6)
137
- def add_data data
138
- @data = [] unless @data
139
-
140
- raise "No data provided by #{conf.inspect}" unless data[:data] and
141
- data[:data].kind_of? Array
142
- raise "Data supplied must be x,y pairs! "+
143
- "The data provided contained an odd set of "+
144
- "data points" unless data[:data].length % 2 == 0
145
- return if data[:data].length == 0
146
-
147
- x = []
148
- y = []
149
- data[:data].each_index {|i|
150
- (i%2 == 0 ? x : y) << data[:data][i]
151
- }
152
- sort( x, y )
153
- data[:data] = [x,y]
154
- @data << data
155
- end
156
-
157
-
158
- protected
159
-
160
- def keys
161
- @data.collect{ |x| x[:title] }
162
- end
163
-
164
- def calculate_left_margin
165
- super
166
- label_left = get_x_labels[0].to_s.length / 2 * font_size * 0.6
167
- @border_left = label_left if label_left > @border_left
168
- end
169
-
170
- def calculate_right_margin
171
- super
172
- label_right = get_x_labels[-1].to_s.length / 2 * font_size * 0.6
173
- @border_right = label_right if label_right > @border_right
174
- end
175
-
176
-
177
- X = 0
178
- Y = 1
179
- def x_range
180
- max_value = @data.collect{|x| x[:data][X][-1] }.max
181
- min_value = @data.collect{|x| x[:data][X][0] }.min
182
- min_value = min_value<min_x_value ? min_value : min_x_value if min_x_value
183
-
184
- range = max_value - min_value
185
- right_pad = range == 0 ? 10 : range / 20.0
186
- scale_range = (max_value + right_pad) - min_value
187
-
188
- scale_division = scale_x_divisions || (scale_range / 10.0)
189
-
190
- if scale_x_integers
191
- scale_division = scale_division < 1 ? 1 : scale_division.round
192
- end
193
-
194
- [min_value, max_value, scale_division]
195
- end
196
-
197
- def get_x_values
198
- min_value, max_value, scale_division = x_range
199
- rv = []
200
- min_value.step( max_value, scale_division ) {|v| rv << v}
201
- return rv
202
- end
203
- alias :get_x_labels :get_x_values
204
-
205
- def field_width
206
- values = get_x_values
207
- max = @data.collect{|x| x[:data][X][-1]}.max
208
- dx = (max - values[-1]).to_f / (values[-1] - values[-2])
209
- (@graph_width.to_f - font_size*2*right_font) /
210
- (values.length + dx - right_align)
211
- end
212
-
213
-
214
- def y_range
215
- max_value = @data.collect{|x| x[:data][Y].max }.max
216
- min_value = @data.collect{|x| x[:data][Y].min }.min
217
- min_value = min_value<min_y_value ? min_value : min_y_value if min_y_value
218
-
219
- range = max_value - min_value
220
- top_pad = range == 0 ? 10 : range / 20.0
221
- scale_range = (max_value + top_pad) - min_value
222
-
223
- scale_division = scale_y_divisions || (scale_range / 10.0)
224
-
225
- if scale_y_integers
226
- scale_division = scale_division < 1 ? 1 : scale_division.round
227
- end
228
-
229
- return [min_value, max_value, scale_division]
230
- end
231
-
232
- def get_y_values
233
- min_value, max_value, scale_division = y_range
234
- rv = []
235
- min_value.step( max_value, scale_division ) {|v| rv << v}
236
- return rv
237
- end
238
- alias :get_y_labels :get_y_values
239
-
240
- def field_height
241
- values = get_y_values
242
- max = @data.collect{|x| x[:data][Y].max }.max
243
- if values.length == 1
244
- dx = values[-1]
245
- else
246
- dx = (max - values[-1]).to_f / (values[-1] - values[-2])
247
- end
248
- (@graph_height.to_f - font_size*2*top_font) /
249
- (values.length + dx - top_align)
250
- end
251
-
252
- def draw_data
253
- line = 1
254
-
255
- x_min, x_max, x_div = x_range
256
- y_min, y_max, y_div = y_range
257
- x_step = (@graph_width.to_f - font_size*2) / (x_max-x_min)
258
- y_step = (@graph_height.to_f - font_size*2) / (y_max-y_min)
259
-
260
- for data in @data
261
- x_points = data[:data][X]
262
- y_points = data[:data][Y]
263
-
264
- lpath = "L"
265
- x_start = 0
266
- y_start = 0
267
- x_points.each_index { |idx|
268
- x = (x_points[idx] - x_min) * x_step
269
- y = @graph_height - (y_points[idx] - y_min) * y_step
270
- x_start, y_start = x,y if idx == 0
271
- lpath << "#{x} #{y} "
272
- }
273
-
274
- if area_fill
275
- @graph.add_element( "path", {
276
- "d" => "M#{x_start} #@graph_height #{lpath} V#@graph_height Z",
277
- "class" => "fill#{line}"
278
- })
279
- end
280
-
281
- @graph.add_element( "path", {
282
- "d" => "M#{x_start} #{y_start} #{lpath}",
283
- "class" => "line#{line}"
284
- })
285
-
286
- if show_data_points || show_data_values
287
- x_points.each_index { |idx|
288
- x = (x_points[idx] - x_min) * x_step
289
- y = @graph_height - (y_points[idx] - y_min) * y_step
290
- if show_data_points
291
- @graph.add_element( "circle", {
292
- "cx" => x.to_s,
293
- "cy" => y.to_s,
294
- "r" => "2.5",
295
- "class" => "dataPoint#{line}"
296
- })
297
- add_popup(x, y, format( x_points[idx], y_points[idx] )) if add_popups
298
- end
299
- make_datapoint_text( x, y-6, y_points[idx] ) if show_data_values
300
- }
301
- end
302
- line += 1
303
- end
304
- end
305
-
306
- def format x, y
307
- "(#{(x * 100).to_i / 100}, #{(y * 100).to_i / 100})"
308
- end
309
-
310
- def get_css
311
- return <<EOL
312
- /* default line styles */
313
- .line1{
314
- fill: none;
315
- stroke: #ff0000;
316
- stroke-width: 1px;
317
- }
318
- .line2{
319
- fill: none;
320
- stroke: #0000ff;
321
- stroke-width: 1px;
322
- }
323
- .line3{
324
- fill: none;
325
- stroke: #00ff00;
326
- stroke-width: 1px;
327
- }
328
- .line4{
329
- fill: none;
330
- stroke: #ffcc00;
331
- stroke-width: 1px;
332
- }
333
- .line5{
334
- fill: none;
335
- stroke: #00ccff;
336
- stroke-width: 1px;
337
- }
338
- .line6{
339
- fill: none;
340
- stroke: #ff00ff;
341
- stroke-width: 1px;
342
- }
343
- .line7{
344
- fill: none;
345
- stroke: #00ffff;
346
- stroke-width: 1px;
347
- }
348
- .line8{
349
- fill: none;
350
- stroke: #ffff00;
351
- stroke-width: 1px;
352
- }
353
- .line9{
354
- fill: none;
355
- stroke: #ccc6666;
356
- stroke-width: 1px;
357
- }
358
- .line10{
359
- fill: none;
360
- stroke: #663399;
361
- stroke-width: 1px;
362
- }
363
- .line11{
364
- fill: none;
365
- stroke: #339900;
366
- stroke-width: 1px;
367
- }
368
- .line12{
369
- fill: none;
370
- stroke: #9966FF;
371
- stroke-width: 1px;
372
- }
373
- /* default fill styles */
374
- .fill1{
375
- fill: #cc0000;
376
- fill-opacity: 0.2;
377
- stroke: none;
378
- }
379
- .fill2{
380
- fill: #0000cc;
381
- fill-opacity: 0.2;
382
- stroke: none;
383
- }
384
- .fill3{
385
- fill: #00cc00;
386
- fill-opacity: 0.2;
387
- stroke: none;
388
- }
389
- .fill4{
390
- fill: #ffcc00;
391
- fill-opacity: 0.2;
392
- stroke: none;
393
- }
394
- .fill5{
395
- fill: #00ccff;
396
- fill-opacity: 0.2;
397
- stroke: none;
398
- }
399
- .fill6{
400
- fill: #ff00ff;
401
- fill-opacity: 0.2;
402
- stroke: none;
403
- }
404
- .fill7{
405
- fill: #00ffff;
406
- fill-opacity: 0.2;
407
- stroke: none;
408
- }
409
- .fill8{
410
- fill: #ffff00;
411
- fill-opacity: 0.2;
412
- stroke: none;
413
- }
414
- .fill9{
415
- fill: #cc6666;
416
- fill-opacity: 0.2;
417
- stroke: none;
418
- }
419
- .fill10{
420
- fill: #663399;
421
- fill-opacity: 0.2;
422
- stroke: none;
423
- }
424
- .fill11{
425
- fill: #339900;
426
- fill-opacity: 0.2;
427
- stroke: none;
428
- }
429
- .fill12{
430
- fill: #9966FF;
431
- fill-opacity: 0.2;
432
- stroke: none;
433
- }
434
- /* default line styles */
435
- .key1,.dataPoint1{
436
- fill: #ff0000;
437
- stroke: none;
438
- stroke-width: 1px;
439
- }
440
- .key2,.dataPoint2{
441
- fill: #0000ff;
442
- stroke: none;
443
- stroke-width: 1px;
444
- }
445
- .key3,.dataPoint3{
446
- fill: #00ff00;
447
- stroke: none;
448
- stroke-width: 1px;
449
- }
450
- .key4,.dataPoint4{
451
- fill: #ffcc00;
452
- stroke: none;
453
- stroke-width: 1px;
454
- }
455
- .key5,.dataPoint5{
456
- fill: #00ccff;
457
- stroke: none;
458
- stroke-width: 1px;
459
- }
460
- .key6,.dataPoint6{
461
- fill: #ff00ff;
462
- stroke: none;
463
- stroke-width: 1px;
464
- }
465
- .key7,.dataPoint7{
466
- fill: #00ffff;
467
- stroke: none;
468
- stroke-width: 1px;
469
- }
470
- .key8,.dataPoint8{
471
- fill: #ffff00;
472
- stroke: none;
473
- stroke-width: 1px;
474
- }
475
- .key9,.dataPoint9{
476
- fill: #cc6666;
477
- stroke: none;
478
- stroke-width: 1px;
479
- }
480
- .key10,.dataPoint10{
481
- fill: #663399;
482
- stroke: none;
483
- stroke-width: 1px;
484
- }
485
- .key11,.dataPoint11{
486
- fill: #339900;
487
- stroke: none;
488
- stroke-width: 1px;
489
- }
490
- .key12,.dataPoint12{
491
- fill: #9966FF;
492
- stroke: none;
493
- stroke-width: 1px;
494
- }
495
- EOL
496
- end
497
-
498
- end
499
- end
500
- end