git_pm 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,444 @@
1
+ require 'SVG/Graph/Graph'
2
+
3
+ module SVG
4
+ module Graph
5
+ # === Create presentation quality SVG line graphs easily
6
+ #
7
+ # = Synopsis
8
+ #
9
+ # require 'SVG/Graph/Line'
10
+ #
11
+ # fields = %w(Jan Feb Mar);
12
+ # data_sales_02 = [12, 45, 21]
13
+ # data_sales_03 = [15, 30, 40]
14
+ #
15
+ # graph = SVG::Graph::Line.new({
16
+ # :height => 500,
17
+ # :width => 300,
18
+ # :fields => fields,
19
+ # })
20
+ #
21
+ # graph.add_data({
22
+ # :data => data_sales_02,
23
+ # :title => 'Sales 2002',
24
+ # })
25
+ #
26
+ # graph.add_data({
27
+ # :data => data_sales_03,
28
+ # :title => 'Sales 2003',
29
+ # })
30
+ #
31
+ # print "Content-type: image/svg+xml\r\n\r\n";
32
+ # print graph.burn();
33
+ #
34
+ # = Description
35
+ #
36
+ # This object aims to allow you to easily create high quality
37
+ # SVG line graphs. You can either use the default style sheet
38
+ # or supply your own. Either way there are many options which can
39
+ # be configured to give you control over how the graph is
40
+ # generated - with or without a key, data elements at each point,
41
+ # title, subtitle etc.
42
+ #
43
+ # = Examples
44
+ #
45
+ # http://www.germane-software/repositories/public/SVG/test/single.rb
46
+ #
47
+ # = Notes
48
+ #
49
+ # The default stylesheet handles upto 10 data sets, if you
50
+ # use more you must create your own stylesheet and add the
51
+ # additional settings for the extra data sets. You will know
52
+ # if you go over 10 data sets as they will have no style and
53
+ # be in black.
54
+ #
55
+ # = See also
56
+ #
57
+ # * SVG::Graph::Graph
58
+ # * SVG::Graph::BarHorizontal
59
+ # * SVG::Graph::Bar
60
+ # * SVG::Graph::Pie
61
+ # * SVG::Graph::Plot
62
+ # * SVG::Graph::TimeSeries
63
+ #
64
+ # == Author
65
+ #
66
+ # Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>
67
+ #
68
+ # Copyright 2004 Sean E. Russell
69
+ # This software is available under the Ruby license[LICENSE.txt]
70
+ #
71
+ class Line < SVG::Graph::Graph
72
+ # Show a small circle on the graph where the line
73
+ # goes from one point to the next.
74
+ attr_accessor :show_data_points
75
+ # Accumulates each data set. (i.e. Each point increased by sum of
76
+ # all previous series at same point). Default is 0, set to '1' to show.
77
+ attr_accessor :stacked
78
+ # Fill in the area under the plot if true
79
+ attr_accessor :area_fill
80
+
81
+ # The constructor takes a hash reference, fields (the names for each
82
+ # field on the X axis) MUST be set, all other values are defaulted to
83
+ # those shown above - with the exception of style_sheet which defaults
84
+ # to using the internal style sheet.
85
+ def initialize config
86
+ raise "fields was not supplied or is empty" unless config[:fields] &&
87
+ config[:fields].kind_of?(Array) &&
88
+ config[:fields].length > 0
89
+ super
90
+ end
91
+
92
+ # In addition to the defaults set in Graph::initialize, sets
93
+ # [show_data_points] true
94
+ # [show_data_values] true
95
+ # [stacked] false
96
+ # [area_fill] false
97
+ def set_defaults
98
+ init_with(
99
+ :show_data_points => true,
100
+ :show_data_values => true,
101
+ :stacked => false,
102
+ :area_fill => false
103
+ )
104
+
105
+ self.top_align = self.top_font = self.right_align = self.right_font = 1
106
+ end
107
+
108
+ protected
109
+
110
+ def max_value
111
+ max = 0
112
+
113
+ if (stacked == true) then
114
+ sums = Array.new(@config[:fields].length).fill(0)
115
+
116
+ @data.each do |data|
117
+ sums.each_index do |i|
118
+ sums[i] += data[:data][i].to_f
119
+ end
120
+ end
121
+
122
+ max = sums.max
123
+ else
124
+ max = @data.collect{|x| x[:data].max}.max
125
+ end
126
+
127
+ return max
128
+ end
129
+
130
+ def min_value
131
+ min = 0
132
+
133
+ if (min_scale_value.nil? == false) then
134
+ min = min_scale_value
135
+ elsif (stacked == true) then
136
+ min = @data[-1][:data].min
137
+ else
138
+ min = @data.collect{|x| x[:data].min}.min
139
+ end
140
+
141
+ return min
142
+ end
143
+
144
+ def get_x_labels
145
+ @config[:fields]
146
+ end
147
+
148
+ def calculate_left_margin
149
+ super
150
+ label_left = @config[:fields][0].length / 2 * font_size * 0.6
151
+ @border_left = label_left if label_left > @border_left
152
+ end
153
+
154
+ def get_y_labels
155
+ maxvalue = max_value
156
+ minvalue = min_value
157
+ range = maxvalue - minvalue
158
+ top_pad = range == 0 ? 10 : range / 20.0
159
+ scale_range = (maxvalue + top_pad) - minvalue
160
+
161
+ scale_division = scale_divisions || (scale_range / 10.0)
162
+
163
+ if scale_integers
164
+ scale_division = scale_division < 1 ? 1 : scale_division.round
165
+ end
166
+
167
+ rv = []
168
+ maxvalue = maxvalue%scale_division == 0 ?
169
+ maxvalue : maxvalue + scale_division
170
+ minvalue.step( maxvalue, scale_division ) {|v| rv << v}
171
+ return rv
172
+ end
173
+
174
+ def calc_coords(field, value, width = field_width, height = field_height)
175
+ coords = {:x => 0, :y => 0}
176
+ coords[:x] = width * field
177
+ coords[:y] = @graph_height - value * height
178
+
179
+ return coords
180
+ end
181
+
182
+ def draw_data
183
+ minvalue = min_value
184
+ fieldheight = (@graph_height.to_f - font_size*2*top_font) /
185
+ (get_y_labels.max - get_y_labels.min)
186
+ fieldwidth = field_width
187
+ line = @data.length
188
+
189
+ prev_sum = Array.new(@config[:fields].length).fill(0)
190
+ cum_sum = Array.new(@config[:fields].length).fill(-minvalue)
191
+
192
+ for data in @data.reverse
193
+ lpath = ""
194
+ apath = ""
195
+
196
+ if not stacked then cum_sum.fill(-minvalue) end
197
+
198
+ data[:data].each_index do |i|
199
+ cum_sum[i] += data[:data][i]
200
+
201
+ c = calc_coords(i, cum_sum[i], fieldwidth, fieldheight)
202
+
203
+ lpath << "#{c[:x]} #{c[:y]} "
204
+ end
205
+
206
+ if area_fill
207
+ if stacked then
208
+ (prev_sum.length - 1).downto 0 do |i|
209
+ c = calc_coords(i, prev_sum[i], fieldwidth, fieldheight)
210
+
211
+ apath << "#{c[:x]} #{c[:y]} "
212
+ end
213
+
214
+ c = calc_coords(0, prev_sum[0], fieldwidth, fieldheight)
215
+ else
216
+ apath = "V#@graph_height"
217
+ c = calc_coords(0, 0, fieldwidth, fieldheight)
218
+ end
219
+
220
+ @graph.add_element("path", {
221
+ "d" => "M#{c[:x]} #{c[:y]} L" + lpath + apath + "Z",
222
+ "class" => "fill#{line}"
223
+ })
224
+ end
225
+
226
+ @graph.add_element("path", {
227
+ "d" => "M0 #@graph_height L" + lpath,
228
+ "class" => "line#{line}"
229
+ })
230
+
231
+ if show_data_points || show_data_values
232
+ cum_sum.each_index do |i|
233
+ if show_data_points
234
+ @graph.add_element( "circle", {
235
+ "cx" => (fieldwidth * i).to_s,
236
+ "cy" => (@graph_height - cum_sum[i] * fieldheight).to_s,
237
+ "r" => "2.5",
238
+ "class" => "dataPoint#{line}"
239
+ })
240
+ end
241
+ make_datapoint_text(
242
+ fieldwidth * i,
243
+ @graph_height - cum_sum[i] * fieldheight - 6,
244
+ cum_sum[i] + minvalue
245
+ )
246
+ end
247
+ end
248
+
249
+ prev_sum = cum_sum.dup
250
+ line -= 1
251
+ end
252
+ end
253
+
254
+
255
+ def get_css
256
+ return <<EOL
257
+ /* default line styles */
258
+ .line1{
259
+ fill: none;
260
+ stroke: #ff0000;
261
+ stroke-width: 1px;
262
+ }
263
+ .line2{
264
+ fill: none;
265
+ stroke: #0000ff;
266
+ stroke-width: 1px;
267
+ }
268
+ .line3{
269
+ fill: none;
270
+ stroke: #00ff00;
271
+ stroke-width: 1px;
272
+ }
273
+ .line4{
274
+ fill: none;
275
+ stroke: #ffcc00;
276
+ stroke-width: 1px;
277
+ }
278
+ .line5{
279
+ fill: none;
280
+ stroke: #00ccff;
281
+ stroke-width: 1px;
282
+ }
283
+ .line6{
284
+ fill: none;
285
+ stroke: #ff00ff;
286
+ stroke-width: 1px;
287
+ }
288
+ .line7{
289
+ fill: none;
290
+ stroke: #00ffff;
291
+ stroke-width: 1px;
292
+ }
293
+ .line8{
294
+ fill: none;
295
+ stroke: #ffff00;
296
+ stroke-width: 1px;
297
+ }
298
+ .line9{
299
+ fill: none;
300
+ stroke: #ccc6666;
301
+ stroke-width: 1px;
302
+ }
303
+ .line10{
304
+ fill: none;
305
+ stroke: #663399;
306
+ stroke-width: 1px;
307
+ }
308
+ .line11{
309
+ fill: none;
310
+ stroke: #339900;
311
+ stroke-width: 1px;
312
+ }
313
+ .line12{
314
+ fill: none;
315
+ stroke: #9966FF;
316
+ stroke-width: 1px;
317
+ }
318
+ /* default fill styles */
319
+ .fill1{
320
+ fill: #cc0000;
321
+ fill-opacity: 0.2;
322
+ stroke: none;
323
+ }
324
+ .fill2{
325
+ fill: #0000cc;
326
+ fill-opacity: 0.2;
327
+ stroke: none;
328
+ }
329
+ .fill3{
330
+ fill: #00cc00;
331
+ fill-opacity: 0.2;
332
+ stroke: none;
333
+ }
334
+ .fill4{
335
+ fill: #ffcc00;
336
+ fill-opacity: 0.2;
337
+ stroke: none;
338
+ }
339
+ .fill5{
340
+ fill: #00ccff;
341
+ fill-opacity: 0.2;
342
+ stroke: none;
343
+ }
344
+ .fill6{
345
+ fill: #ff00ff;
346
+ fill-opacity: 0.2;
347
+ stroke: none;
348
+ }
349
+ .fill7{
350
+ fill: #00ffff;
351
+ fill-opacity: 0.2;
352
+ stroke: none;
353
+ }
354
+ .fill8{
355
+ fill: #ffff00;
356
+ fill-opacity: 0.2;
357
+ stroke: none;
358
+ }
359
+ .fill9{
360
+ fill: #cc6666;
361
+ fill-opacity: 0.2;
362
+ stroke: none;
363
+ }
364
+ .fill10{
365
+ fill: #663399;
366
+ fill-opacity: 0.2;
367
+ stroke: none;
368
+ }
369
+ .fill11{
370
+ fill: #339900;
371
+ fill-opacity: 0.2;
372
+ stroke: none;
373
+ }
374
+ .fill12{
375
+ fill: #9966FF;
376
+ fill-opacity: 0.2;
377
+ stroke: none;
378
+ }
379
+ /* default line styles */
380
+ .key1,.dataPoint1{
381
+ fill: #ff0000;
382
+ stroke: none;
383
+ stroke-width: 1px;
384
+ }
385
+ .key2,.dataPoint2{
386
+ fill: #0000ff;
387
+ stroke: none;
388
+ stroke-width: 1px;
389
+ }
390
+ .key3,.dataPoint3{
391
+ fill: #00ff00;
392
+ stroke: none;
393
+ stroke-width: 1px;
394
+ }
395
+ .key4,.dataPoint4{
396
+ fill: #ffcc00;
397
+ stroke: none;
398
+ stroke-width: 1px;
399
+ }
400
+ .key5,.dataPoint5{
401
+ fill: #00ccff;
402
+ stroke: none;
403
+ stroke-width: 1px;
404
+ }
405
+ .key6,.dataPoint6{
406
+ fill: #ff00ff;
407
+ stroke: none;
408
+ stroke-width: 1px;
409
+ }
410
+ .key7,.dataPoint7{
411
+ fill: #00ffff;
412
+ stroke: none;
413
+ stroke-width: 1px;
414
+ }
415
+ .key8,.dataPoint8{
416
+ fill: #ffff00;
417
+ stroke: none;
418
+ stroke-width: 1px;
419
+ }
420
+ .key9,.dataPoint9{
421
+ fill: #cc6666;
422
+ stroke: none;
423
+ stroke-width: 1px;
424
+ }
425
+ .key10,.dataPoint10{
426
+ fill: #663399;
427
+ stroke: none;
428
+ stroke-width: 1px;
429
+ }
430
+ .key11,.dataPoint11{
431
+ fill: #339900;
432
+ stroke: none;
433
+ stroke-width: 1px;
434
+ }
435
+ .key12,.dataPoint12{
436
+ fill: #9966FF;
437
+ stroke: none;
438
+ stroke-width: 1px;
439
+ }
440
+ EOL
441
+ end
442
+ end
443
+ end
444
+ end
@@ -0,0 +1,347 @@
1
+ require 'SVG/Graph/Graph'
2
+
3
+ module SVG
4
+ module Graph
5
+ # === Create presentation quality SVG pie graphs easily
6
+ #
7
+ # == Synopsis
8
+ #
9
+ # require 'SVG/Graph/Pie'
10
+ #
11
+ # fields = %w(Jan Feb Mar)
12
+ # data_sales_02 = [12, 45, 21]
13
+ #
14
+ # graph = SVG::Graph::Pie.new({
15
+ # :height => 500,
16
+ # :width => 300,
17
+ # :fields => fields,
18
+ # })
19
+ #
20
+ # graph.add_data({
21
+ # :data => data_sales_02,
22
+ # :title => 'Sales 2002',
23
+ # })
24
+ #
25
+ # print "Content-type: image/svg+xml\r\n\r\n"
26
+ # print graph.burn();
27
+ #
28
+ # == Description
29
+ #
30
+ # This object aims to allow you to easily create high quality
31
+ # SVG pie graphs. You can either use the default style sheet
32
+ # or supply your own. Either way there are many options which can
33
+ # be configured to give you control over how the graph is
34
+ # generated - with or without a key, display percent on pie chart,
35
+ # title, subtitle etc.
36
+ #
37
+ # = Examples
38
+ #
39
+ # http://www.germane-software/repositories/public/SVG/test/single.rb
40
+ #
41
+ # == See also
42
+ #
43
+ # * SVG::Graph::Graph
44
+ # * SVG::Graph::BarHorizontal
45
+ # * SVG::Graph::Bar
46
+ # * SVG::Graph::Line
47
+ # * SVG::Graph::Plot
48
+ # * SVG::Graph::TimeSeries
49
+ #
50
+ # == Author
51
+ #
52
+ # Sean E. Russell <serATgermaneHYPHENsoftwareDOTcom>
53
+ #
54
+ # Copyright 2004 Sean E. Russell
55
+ # This software is available under the Ruby license[LICENSE.txt]
56
+ #
57
+ class Pie < Graph
58
+ # Defaults are those set by Graph::initialize, and
59
+ # [show_shadow] true
60
+ # [shadow_offset] 10
61
+ # [show_data_labels] false
62
+ # [show_actual_values] false
63
+ # [show_percent] true
64
+ # [show_key_data_labels] true
65
+ # [show_key_actual_values] true
66
+ # [show_key_percent] false
67
+ # [expanded] false
68
+ # [expand_greatest] false
69
+ # [expand_gap] 10
70
+ # [show_x_labels] false
71
+ # [show_y_labels] false
72
+ # [datapoint_font_size] 12
73
+ def set_defaults
74
+ init_with(
75
+ :show_shadow => true,
76
+ :shadow_offset => 10,
77
+
78
+ :show_data_labels => false,
79
+ :show_actual_values => false,
80
+ :show_percent => true,
81
+
82
+ :show_key_data_labels => true,
83
+ :show_key_actual_values => true,
84
+ :show_key_percent => false,
85
+
86
+ :expanded => false,
87
+ :expand_greatest => false,
88
+ :expand_gap => 10,
89
+
90
+ :show_x_labels => false,
91
+ :show_y_labels => false,
92
+ :datapoint_font_size => 12
93
+ )
94
+ @data = []
95
+ end
96
+
97
+ # Adds a data set to the graph.
98
+ #
99
+ # graph.add_data( { :data => [1,2,3,4] } )
100
+ #
101
+ # Note that the :title is not necessary. If multiple
102
+ # data sets are added to the graph, the pie chart will
103
+ # display the +sums+ of the data. EG:
104
+ #
105
+ # graph.add_data( { :data => [1,2,3,4] } )
106
+ # graph.add_data( { :data => [2,3,5,9] } )
107
+ #
108
+ # is the same as:
109
+ #
110
+ # graph.add_data( { :data => [3,5,8,13] } )
111
+ def add_data arg
112
+ arg[:data].each_index {|idx|
113
+ @data[idx] = 0 unless @data[idx]
114
+ @data[idx] += arg[:data][idx]
115
+ }
116
+ end
117
+
118
+ # If true, displays a drop shadow for the chart
119
+ attr_accessor :show_shadow
120
+ # Sets the offset of the shadow from the pie chart
121
+ attr_accessor :shadow_offset
122
+ # If true, display the data labels on the chart
123
+ attr_accessor :show_data_labels
124
+ # If true, display the actual field values in the data labels
125
+ attr_accessor :show_actual_values
126
+ # If true, display the percentage value of each pie wedge in the data
127
+ # labels
128
+ attr_accessor :show_percent
129
+ # If true, display the labels in the key
130
+ attr_accessor :show_key_data_labels
131
+ # If true, display the actual value of the field in the key
132
+ attr_accessor :show_key_actual_values
133
+ # If true, display the percentage value of the wedges in the key
134
+ attr_accessor :show_key_percent
135
+ # If true, "explode" the pie (put space between the wedges)
136
+ attr_accessor :expanded
137
+ # If true, expand the largest pie wedge
138
+ attr_accessor :expand_greatest
139
+ # The amount of space between expanded wedges
140
+ attr_accessor :expand_gap
141
+ # The font size of the data point labels
142
+ attr_accessor :datapoint_font_size
143
+
144
+
145
+ protected
146
+
147
+ def add_defs defs
148
+ gradient = defs.add_element( "filter", {
149
+ "id"=>"dropshadow",
150
+ "width" => "1.2",
151
+ "height" => "1.2",
152
+ } )
153
+ gradient.add_element( "feGaussianBlur", {
154
+ "stdDeviation" => "4",
155
+ "result" => "blur"
156
+ })
157
+ end
158
+
159
+ # We don't need the graph
160
+ def draw_graph
161
+ end
162
+
163
+ def get_y_labels
164
+ [""]
165
+ end
166
+
167
+ def get_x_labels
168
+ [""]
169
+ end
170
+
171
+ def keys
172
+ total = 0
173
+ max_value = 0
174
+ @data.each {|x| total += x }
175
+ percent_scale = 100.0 / total
176
+ count = -1
177
+ a = @config[:fields].collect{ |x|
178
+ count += 1
179
+ v = @data[count]
180
+ perc = show_key_percent ? " "+(v * percent_scale).round.to_s+"%" : ""
181
+ x + " [" + v.to_s + "]" + perc
182
+ }
183
+ end
184
+
185
+ RADIANS = Math::PI/180
186
+
187
+ def draw_data
188
+ @graph = @root.add_element( "g" )
189
+ background = @graph.add_element("g")
190
+ midground = @graph.add_element("g")
191
+
192
+ diameter = @graph_height > @graph_width ? @graph_width : @graph_height
193
+ diameter -= expand_gap if expanded or expand_greatest
194
+ diameter -= datapoint_font_size if show_data_labels
195
+ diameter -= 10 if show_shadow
196
+ radius = diameter / 2.0
197
+
198
+ xoff = (width - diameter) / 2
199
+ yoff = (height - @border_bottom - diameter)
200
+ yoff -= 10 if show_shadow
201
+ @graph.attributes['transform'] = "translate( #{xoff} #{yoff} )"
202
+
203
+ wedge_text_pad = 5
204
+ wedge_text_pad = 20 if show_percent and show_data_labels
205
+
206
+ total = 0
207
+ max_value = 0
208
+ @data.each {|x|
209
+ max_value = max_value < x ? x : max_value
210
+ total += x
211
+ }
212
+ percent_scale = 100.0 / total
213
+
214
+ prev_percent = 0
215
+ rad_mult = 3.6 * RADIANS
216
+ @config[:fields].each_index { |count|
217
+ value = @data[count]
218
+ percent = percent_scale * value
219
+
220
+ radians = prev_percent * rad_mult
221
+ x_start = radius+(Math.sin(radians) * radius)
222
+ y_start = radius-(Math.cos(radians) * radius)
223
+ radians = (prev_percent+percent) * rad_mult
224
+ x_end = radius+(Math.sin(radians) * radius)
225
+ x_end -= 0.00001 if @data.length == 1
226
+ y_end = radius-(Math.cos(radians) * radius)
227
+ path = "M#{radius},#{radius} L#{x_start},#{y_start} "+
228
+ "A#{radius},#{radius} "+
229
+ "0, #{percent >= 50 ? '1' : '0'},1, "+
230
+ "#{x_end} #{y_end} Z"
231
+
232
+
233
+ wedge = @foreground.add_element( "path", {
234
+ "d" => path,
235
+ "class" => "fill#{count+1}"
236
+ })
237
+
238
+ translate = nil
239
+ tx = 0
240
+ ty = 0
241
+ half_percent = prev_percent + percent / 2
242
+ radians = half_percent * rad_mult
243
+
244
+ if show_shadow
245
+ shadow = background.add_element( "path", {
246
+ "d" => path,
247
+ "filter" => "url(#dropshadow)",
248
+ "style" => "fill: #ccc; stroke: none;"
249
+ })
250
+ clear = midground.add_element( "path", {
251
+ "d" => path,
252
+ "style" => "fill: #fff; stroke: none;"
253
+ })
254
+ end
255
+
256
+ if expanded or (expand_greatest && value == max_value)
257
+ tx = (Math.sin(radians) * expand_gap)
258
+ ty = -(Math.cos(radians) * expand_gap)
259
+ translate = "translate( #{tx} #{ty} )"
260
+ wedge.attributes["transform"] = translate
261
+ clear.attributes["transform"] = translate if clear
262
+ end
263
+
264
+ if show_shadow
265
+ shadow.attributes["transform"] =
266
+ "translate( #{tx+shadow_offset} #{ty+shadow_offset} )"
267
+ end
268
+
269
+ if show_data_labels and value != 0
270
+ label = ""
271
+ label += @config[:fields][count] if show_key_data_labels
272
+ label += " ["+value.to_s+"]" if show_actual_values
273
+ label += " "+percent.round.to_s+"%" if show_percent
274
+
275
+ msr = Math.sin(radians)
276
+ mcr = Math.cos(radians)
277
+ tx = radius + (msr * radius)
278
+ ty = radius -(mcr * radius)
279
+
280
+ if expanded or (expand_greatest && value == max_value)
281
+ tx += (msr * expand_gap)
282
+ ty -= (mcr * expand_gap)
283
+ end
284
+ @foreground.add_element( "text", {
285
+ "x" => tx.to_s,
286
+ "y" => ty.to_s,
287
+ "class" => "dataPointLabel",
288
+ "style" => "stroke: #fff; stroke-width: 2;"
289
+ }).text = label.to_s
290
+ @foreground.add_element( "text", {
291
+ "x" => tx.to_s,
292
+ "y" => ty.to_s,
293
+ "class" => "dataPointLabel",
294
+ }).text = label.to_s
295
+ end
296
+
297
+ prev_percent += percent
298
+ }
299
+ end
300
+
301
+
302
+ def round val, to
303
+ up = 10**to.to_f
304
+ (val * up).to_i / up
305
+ end
306
+
307
+
308
+ def get_css(keys = 255)
309
+ return <<EOL
310
+ .dataPointLabel{
311
+ fill: #000000;
312
+ text-anchor:middle;
313
+ font-size: #{datapoint_font_size}px;
314
+ font-family: "Arial", sans-serif;
315
+ font-weight: normal;
316
+ }
317
+
318
+ /* key - MUST match fill styles */
319
+ #{
320
+ str = ""
321
+
322
+ sp = (keys ** (1 / 3.0)).round
323
+ i = 0
324
+ sp.times do |r|
325
+ sp.times do |g|
326
+ sp.times do |b|
327
+ str += %@\n.key#{i},.fill#{i} {
328
+ fill: rgb(#{rand(255) % 255}, #{rand(255) % 255}, #{rand(255) % 255});
329
+ fill-opacity: 0.7;
330
+ stroke: none;
331
+ stroke-width: 1px;
332
+ }
333
+ @
334
+
335
+ i += 1
336
+ end
337
+ end
338
+ end
339
+
340
+ str
341
+ }
342
+
343
+ EOL
344
+ end
345
+ end
346
+ end
347
+ end