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