gruff 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/assets/plastik-yellow.png +0 -0
- data/lib/gruff.rb +1 -0
- data/lib/gruff/base.rb +42 -12
- data/lib/gruff/line.rb +27 -5
- data/lib/gruff/stacked_bar.rb +59 -0
- data/test/line_test.rb +59 -51
- data/test/stacked_bar_test.rb +116 -0
- metadata +5 -2
Binary file
|
data/lib/gruff.rb
CHANGED
data/lib/gruff/base.rb
CHANGED
@@ -7,17 +7,13 @@
|
|
7
7
|
#
|
8
8
|
#
|
9
9
|
|
10
|
-
|
11
|
-
require 'rmagick'
|
12
|
-
rescue LoadError
|
13
|
-
require 'RMagick' # capitalized on Windows
|
14
|
-
end
|
10
|
+
require 'RMagick'
|
15
11
|
|
16
12
|
require 'yaml'
|
17
13
|
|
18
14
|
module Gruff
|
19
15
|
|
20
|
-
VERSION = '0.0.
|
16
|
+
VERSION = '0.0.7'
|
21
17
|
|
22
18
|
class Base
|
23
19
|
|
@@ -170,8 +166,11 @@ module Gruff
|
|
170
166
|
@column_count = (data_points.length > @column_count) ? data_points.length : @column_count
|
171
167
|
|
172
168
|
# Pre-normalize
|
173
|
-
data_points.
|
174
|
-
|
169
|
+
data_points.each_with_index do |data_point, index|
|
170
|
+
next if data_point.nil?
|
171
|
+
|
172
|
+
@maximum_value = larger_than_max?(data_point, index) ? max(data_point, index) : @maximum_value
|
173
|
+
# @maximum_value = (data_point > @maximum_value) ? data_point : @maximum_value
|
175
174
|
if @maximum_value > 0
|
176
175
|
@has_data = true
|
177
176
|
end
|
@@ -211,7 +210,11 @@ protected
|
|
211
210
|
# - legend
|
212
211
|
# - title
|
213
212
|
def setup_drawing
|
214
|
-
|
213
|
+
# Maybe should be done in one of the following functions for more granularity.
|
214
|
+
unless @has_data
|
215
|
+
draw_no_data()
|
216
|
+
return
|
217
|
+
end
|
215
218
|
|
216
219
|
normalize()
|
217
220
|
setup_graph_measurements()
|
@@ -230,7 +233,11 @@ protected
|
|
230
233
|
@data.each do |data_row|
|
231
234
|
norm_data_points = Array.new
|
232
235
|
data_row[1].each do |data_point|
|
233
|
-
|
236
|
+
if data_point.nil?
|
237
|
+
norm_data_points << nil
|
238
|
+
else
|
239
|
+
norm_data_points << (data_point.to_f/@maximum_value.to_f)
|
240
|
+
end
|
234
241
|
end
|
235
242
|
@norm_data << [data_row[0], norm_data_points]
|
236
243
|
end
|
@@ -248,7 +255,7 @@ protected
|
|
248
255
|
# Draws horizontal background lines and labels
|
249
256
|
def draw_line_markers
|
250
257
|
return unless @show_line_markers
|
251
|
-
|
258
|
+
|
252
259
|
# Draw horizontal line markers and annotate with numbers
|
253
260
|
@d = @d.stroke(@marker_color)
|
254
261
|
@d = @d.stroke_width 1
|
@@ -362,6 +369,19 @@ protected
|
|
362
369
|
end
|
363
370
|
end
|
364
371
|
|
372
|
+
def draw_no_data
|
373
|
+
@d.fill = @marker_color
|
374
|
+
@d.font = @font
|
375
|
+
@d.stroke = 'transparent'
|
376
|
+
@d.font_weight = NormalWeight
|
377
|
+
@d.pointsize = scale_fontsize(80)
|
378
|
+
@d.gravity = CenterGravity
|
379
|
+
@d = @d.annotate_scaled( @base_image,
|
380
|
+
@raw_columns, @raw_rows/2.0,
|
381
|
+
0, 10,
|
382
|
+
'No Data', @scale)
|
383
|
+
end
|
384
|
+
|
365
385
|
# Use with a theme definition method to draw a gradiated (or solid color) background.
|
366
386
|
def render_gradiated_background(top_color, bottom_color)
|
367
387
|
Image.new(@columns, @rows,
|
@@ -411,6 +431,16 @@ protected
|
|
411
431
|
(value > max_value) ? max_value : value
|
412
432
|
end
|
413
433
|
|
434
|
+
# Overridden by subclasses such as stacked bar.
|
435
|
+
def larger_than_max?(data_point, index=0)
|
436
|
+
data_point > @maximum_value
|
437
|
+
end
|
438
|
+
|
439
|
+
def max(data_point, index)
|
440
|
+
data_point
|
441
|
+
end
|
442
|
+
|
443
|
+
|
414
444
|
# round down to significant digits
|
415
445
|
def significant(inc)
|
416
446
|
factor = 1.0
|
@@ -440,7 +470,7 @@ protected
|
|
440
470
|
|
441
471
|
def sums(data_set)
|
442
472
|
total_sum = 0
|
443
|
-
data_set.collect {|num| total_sum += num }
|
473
|
+
data_set.collect {|num| total_sum += num.to_f }
|
444
474
|
total_sum
|
445
475
|
end
|
446
476
|
|
data/lib/gruff/line.rb
CHANGED
@@ -3,6 +3,12 @@ require File.dirname(__FILE__) + '/base'
|
|
3
3
|
|
4
4
|
class Gruff::Line < Gruff::Base
|
5
5
|
|
6
|
+
# Draw a dashed line at the given value
|
7
|
+
attr_accessor :baseline_value
|
8
|
+
|
9
|
+
# Color of the baseline
|
10
|
+
attr_accessor :baseline_color
|
11
|
+
|
6
12
|
#
|
7
13
|
# Call with target pixel width of graph (800, 400, 300), and/or 'false' to omit lines (points only).
|
8
14
|
#
|
@@ -19,6 +25,7 @@ class Gruff::Line < Gruff::Base
|
|
19
25
|
super args.shift
|
20
26
|
end
|
21
27
|
@lines = args.empty? || args.shift # draw lines by default
|
28
|
+
@baseline_color = 'red'
|
22
29
|
end
|
23
30
|
|
24
31
|
def draw
|
@@ -31,15 +38,29 @@ class Gruff::Line < Gruff::Base
|
|
31
38
|
|
32
39
|
@d = @d.stroke_opacity 1.0
|
33
40
|
@d = @d.stroke_width clip_value_if_greater_than(@columns / (@norm_data.first[1].size * 4), 5.0)
|
34
|
-
|
41
|
+
|
42
|
+
if (defined?(@norm_baseline)) then
|
43
|
+
level = @graph_top + (@graph_height - @norm_baseline * @graph_height)
|
44
|
+
@d = @d.push
|
45
|
+
@d.stroke_color @baseline_color
|
46
|
+
@d.fill_opacity 0.0
|
47
|
+
@d.stroke_dasharray(10, 20)
|
48
|
+
@d.stroke_width 5
|
49
|
+
@d.line(@graph_left, level, @graph_left + @graph_width, level)
|
50
|
+
@d = @d.pop
|
51
|
+
end
|
52
|
+
|
35
53
|
@norm_data.each do |data_row|
|
36
54
|
prev_x = prev_y = 0.0
|
37
55
|
@d = @d.stroke current_color
|
38
56
|
@d = @d.fill current_color
|
39
57
|
|
40
58
|
data_row[1].each_with_index do |data_point, index|
|
41
|
-
# Use incremented x and scaled y
|
42
59
|
new_x = @graph_left + (@x_increment * index)
|
60
|
+
draw_label(new_x, index)
|
61
|
+
|
62
|
+
next if data_point.nil?
|
63
|
+
|
43
64
|
new_y = @graph_top + (@graph_height - data_point * @graph_height)
|
44
65
|
|
45
66
|
if @lines and prev_x > 0 and prev_y > 0 then
|
@@ -47,8 +68,6 @@ class Gruff::Line < Gruff::Base
|
|
47
68
|
end
|
48
69
|
@d = @d.circle(new_x, new_y, new_x - circle_radius, new_y)
|
49
70
|
|
50
|
-
draw_label(new_x, index)
|
51
|
-
|
52
71
|
prev_x = new_x
|
53
72
|
prev_y = new_y
|
54
73
|
end
|
@@ -59,5 +78,8 @@ class Gruff::Line < Gruff::Base
|
|
59
78
|
@d.draw(@base_image)
|
60
79
|
end
|
61
80
|
|
62
|
-
|
81
|
+
def normalize
|
82
|
+
super
|
83
|
+
@norm_baseline = (@baseline_value.to_f / @maximum_value.to_f) if @baseline_value
|
84
|
+
end
|
63
85
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
|
2
|
+
require File.dirname(__FILE__) + '/base'
|
3
|
+
|
4
|
+
class Gruff::StackedBar < Gruff::Base
|
5
|
+
|
6
|
+
def draw
|
7
|
+
super
|
8
|
+
|
9
|
+
return unless @has_data
|
10
|
+
|
11
|
+
# Setup spacing.
|
12
|
+
#
|
13
|
+
# Columns sit stacked.
|
14
|
+
spacing_factor = 0.9
|
15
|
+
@bar_width = @graph_width / @column_count.to_f
|
16
|
+
|
17
|
+
@d = @d.stroke_opacity 0.0
|
18
|
+
|
19
|
+
height = Array.new(@column_count, 0)
|
20
|
+
|
21
|
+
@norm_data.each_with_index do |data_row, row_index|
|
22
|
+
@d = @d.fill current_color
|
23
|
+
|
24
|
+
data_row[1].each_with_index do |data_point, point_index|
|
25
|
+
# Use incremented x and scaled y
|
26
|
+
left_x = @graph_left + (@bar_width * point_index)
|
27
|
+
left_y = @graph_top + (@graph_height -
|
28
|
+
data_point * @graph_height -
|
29
|
+
height[point_index]) + 1
|
30
|
+
right_x = left_x + @bar_width * spacing_factor
|
31
|
+
right_y = @graph_top + @graph_height - height[point_index] - 1
|
32
|
+
|
33
|
+
# update the total height of the current stacked bar
|
34
|
+
height[point_index] += (data_point * @graph_height - 2)
|
35
|
+
|
36
|
+
@d = @d.rectangle(left_x, left_y, right_x, right_y)
|
37
|
+
|
38
|
+
# Calculate center based on bar_width and current row
|
39
|
+
label_center = @graph_left + (@bar_width * point_index) + (@bar_width * spacing_factor / 2.0)
|
40
|
+
draw_label(label_center, point_index)
|
41
|
+
end
|
42
|
+
|
43
|
+
increment_color()
|
44
|
+
end
|
45
|
+
|
46
|
+
@d.draw(@base_image)
|
47
|
+
end
|
48
|
+
|
49
|
+
protected
|
50
|
+
|
51
|
+
def larger_than_max?(data_point, index=0)
|
52
|
+
max(data_point, index) > @maximum_value
|
53
|
+
end
|
54
|
+
|
55
|
+
def max(data_point, index)
|
56
|
+
@data.inject(0) {|sum, item| sum + item[1][index]}
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
data/test/line_test.rb
CHANGED
@@ -22,29 +22,8 @@ class TestGruffLine < Test::Unit::TestCase
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def test_line_graph_with_themes
|
25
|
-
|
26
|
-
|
27
|
-
g.labels = {
|
28
|
-
0 => '5/6',
|
29
|
-
2 => '5/15',
|
30
|
-
4 => '5/24',
|
31
|
-
6 => '5/30',
|
32
|
-
}
|
33
|
-
@datasets.each do |data|
|
34
|
-
g.data(data[0], data[1])
|
35
|
-
end
|
36
|
-
|
37
|
-
# Default theme
|
38
|
-
g.write("test/output/line_keynote.png")
|
39
|
-
|
40
|
-
g.theme_37signals
|
41
|
-
g.write("test/output/line_37signals.png")
|
42
|
-
|
43
|
-
g.theme_rails_keynote
|
44
|
-
g.write("test/output/line_rails_keynote.png")
|
45
|
-
|
46
|
-
g.theme_odeo
|
47
|
-
g.write("test/output/line_odeo.png")
|
25
|
+
line_graph_with_themes()
|
26
|
+
line_graph_with_themes(400)
|
48
27
|
end
|
49
28
|
|
50
29
|
def test_line_small_values
|
@@ -80,39 +59,15 @@ class TestGruffLine < Test::Unit::TestCase
|
|
80
59
|
|
81
60
|
g = Gruff::Line.new
|
82
61
|
g.title = "Very Large Values Line Graph Test"
|
62
|
+
g.baseline_value = 50_000
|
63
|
+
g.baseline_color = 'green'
|
83
64
|
@datasets.each do |data|
|
84
65
|
g.data(data[0], data[1])
|
85
66
|
end
|
86
67
|
|
87
68
|
g.write("test/output/line_large.png")
|
88
69
|
end
|
89
|
-
|
90
|
-
def test_resize
|
91
|
-
g = Gruff::Line.new(400)
|
92
|
-
g.title = "Small Size Multi-Line Graph Test"
|
93
|
-
g.labels = {
|
94
|
-
0 => '5/6',
|
95
|
-
2 => '5/15',
|
96
|
-
4 => '5/24',
|
97
|
-
6 => '5/30',
|
98
|
-
}
|
99
|
-
@datasets.each do |data|
|
100
|
-
g.data(data[0], data[1])
|
101
|
-
end
|
102
|
-
|
103
|
-
# Default theme
|
104
|
-
g.write("test/output/line_keynote_small.png")
|
105
70
|
|
106
|
-
g.theme_37signals
|
107
|
-
g.write("test/output/line_37signals_small.png")
|
108
|
-
|
109
|
-
g.theme_rails_keynote
|
110
|
-
g.write("test/output/line_rails_keynote_small.png")
|
111
|
-
|
112
|
-
g.theme_odeo
|
113
|
-
g.write("test/output/line_odeo_small.png")
|
114
|
-
end
|
115
|
-
|
116
71
|
def test_long_title
|
117
72
|
|
118
73
|
end
|
@@ -260,9 +215,9 @@ class TestGruffLine < Test::Unit::TestCase
|
|
260
215
|
end
|
261
216
|
|
262
217
|
|
263
|
-
def
|
218
|
+
def test_all_zeros
|
264
219
|
g = Gruff::Line.new(400)
|
265
|
-
g.title = "
|
220
|
+
g.title = "All Zeros"
|
266
221
|
|
267
222
|
g.data(:gus, [0,0,0,0])
|
268
223
|
|
@@ -270,6 +225,59 @@ class TestGruffLine < Test::Unit::TestCase
|
|
270
225
|
g.write("test/output/line_no_data_other.png")
|
271
226
|
end
|
272
227
|
|
228
|
+
|
229
|
+
def test_some_nil_points
|
230
|
+
g = Gruff::Line.new
|
231
|
+
g.title = "Some Nil Points"
|
232
|
+
|
233
|
+
@datasets = [
|
234
|
+
[:data1, [1, 2, 3, nil, 3, 5, 6]],
|
235
|
+
[:data2, [5, nil, nil, nil, nil, nil, 5]],
|
236
|
+
[:data3, [4, nil, 2, 1, 0]]
|
237
|
+
]
|
238
|
+
|
239
|
+
@datasets.each do |data|
|
240
|
+
g.data(*data)
|
241
|
+
end
|
242
|
+
|
243
|
+
# Default theme
|
244
|
+
g.write("test/output/line_some_nil_points.png")
|
245
|
+
end
|
246
|
+
|
247
|
+
protected
|
248
|
+
|
249
|
+
def line_graph_with_themes(size=nil)
|
250
|
+
if !size.nil?
|
251
|
+
g = Gruff::Line.new(size)
|
252
|
+
else
|
253
|
+
g = Gruff::Line.new
|
254
|
+
end
|
255
|
+
|
256
|
+
g.title = "Multi-Line Graph Test #{size}"
|
257
|
+
g.labels = {
|
258
|
+
0 => '5/6',
|
259
|
+
2 => '5/15',
|
260
|
+
4 => '5/24',
|
261
|
+
6 => '5/30',
|
262
|
+
}
|
263
|
+
@datasets.each do |data|
|
264
|
+
g.data(data[0], data[1])
|
265
|
+
end
|
266
|
+
|
267
|
+
g.baseline_value = 90
|
268
|
+
|
269
|
+
# Default theme
|
270
|
+
g.write("test/output/line_theme_keynote_#{size}.png")
|
271
|
+
|
272
|
+
g.theme_37signals
|
273
|
+
g.write("test/output/line_theme_37signals_#{size}.png")
|
274
|
+
|
275
|
+
g.theme_rails_keynote
|
276
|
+
g.write("test/output/line_theme_rails_keynote_#{size}.png")
|
277
|
+
|
278
|
+
g.theme_odeo
|
279
|
+
g.write("test/output/line_theme_odeo_#{size}.png")
|
280
|
+
end
|
273
281
|
|
274
282
|
end
|
275
283
|
|
@@ -0,0 +1,116 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
$:.unshift(File.dirname(__FILE__) + "/../lib/")
|
4
|
+
#$:.unshift File.dirname(__FILE__) + "/fixtures/helpers"
|
5
|
+
|
6
|
+
require 'test/unit'
|
7
|
+
require 'gruff'
|
8
|
+
|
9
|
+
class TestGruffStackedBar < Test::Unit::TestCase
|
10
|
+
|
11
|
+
# TODO Delete old output files once when starting tests
|
12
|
+
|
13
|
+
def setup
|
14
|
+
@datasets = [
|
15
|
+
[:Jimmy, [25, 36, 86, 39]],
|
16
|
+
[:Charles, [80, 54, 67, 54]],
|
17
|
+
[:Julie, [22, 29, 35, 38]],
|
18
|
+
#[:Jane, [95, 95, 95, 90, 85, 80, 88, 100]],
|
19
|
+
#[:Philip, [90, 34, 23, 12, 78, 89, 98, 88]],
|
20
|
+
#["Arthur", [5, 10, 13, 11, 6, 16, 22, 32]],
|
21
|
+
]
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_bar_graph
|
25
|
+
g = Gruff::StackedBar.new
|
26
|
+
g.title = "Visual Stacked Bar Graph Test"
|
27
|
+
g.labels = {
|
28
|
+
0 => '5/6',
|
29
|
+
1 => '5/15',
|
30
|
+
2 => '5/24',
|
31
|
+
3 => '5/30',
|
32
|
+
}
|
33
|
+
@datasets.each do |data|
|
34
|
+
g.data(data[0], data[1])
|
35
|
+
end
|
36
|
+
|
37
|
+
g.write("test/output/stacked_bar_keynote.png")
|
38
|
+
|
39
|
+
g.theme_rails_keynote
|
40
|
+
g.write("test/output/stacked_bar_rails_keynote.png")
|
41
|
+
|
42
|
+
g.theme_odeo
|
43
|
+
g.write("test/output/stacked_bar_odeo.png")
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
def test_bar_graph_small
|
48
|
+
g = Gruff::StackedBar.new(400)
|
49
|
+
g.title = "Visual Stacked Bar Graph Test"
|
50
|
+
g.labels = {
|
51
|
+
0 => '5/6',
|
52
|
+
1 => '5/15',
|
53
|
+
2 => '5/24',
|
54
|
+
3 => '5/30',
|
55
|
+
}
|
56
|
+
@datasets.each do |data|
|
57
|
+
g.data(data[0], data[1])
|
58
|
+
end
|
59
|
+
|
60
|
+
g.write("test/output/stacked_bar_keynote_small.png")
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_bar_image_bg
|
64
|
+
g = Gruff::StackedBar.new
|
65
|
+
g.title = "Stacked Bar Graph With Image Background"
|
66
|
+
g.labels = {
|
67
|
+
0 => '5/6',
|
68
|
+
1 => '5/15',
|
69
|
+
2 => '5/24',
|
70
|
+
3 => '5/30',
|
71
|
+
}
|
72
|
+
@datasets.each do |data|
|
73
|
+
g.data(data[0], data[1])
|
74
|
+
end
|
75
|
+
|
76
|
+
g.theme_image_based
|
77
|
+
g.write("test/output/stacked_bar_image.png")
|
78
|
+
|
79
|
+
g = Gruff::StackedBar.new(400)
|
80
|
+
g.title = "Stacked Bar Graph With Image Background Small"
|
81
|
+
g.labels = {
|
82
|
+
0 => '5/6',
|
83
|
+
1 => '5/15',
|
84
|
+
2 => '5/24',
|
85
|
+
3 => '5/30',
|
86
|
+
}
|
87
|
+
@datasets.each do |data|
|
88
|
+
g.data(data[0], data[1])
|
89
|
+
end
|
90
|
+
|
91
|
+
g.theme_image_based
|
92
|
+
g.write("test/output/stacked_bar_image_small.png")
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
class Gruff::Base
|
100
|
+
def theme_image_based
|
101
|
+
reset_themes()
|
102
|
+
# Colors
|
103
|
+
@green = '#00ff00'
|
104
|
+
@grey = '#333333'
|
105
|
+
@orange = '#ff5d00'
|
106
|
+
@red = '#f61100'
|
107
|
+
@white = 'white'
|
108
|
+
@light_grey = '#999999'
|
109
|
+
@black = 'black'
|
110
|
+
@colors = [@green, @grey, @orange, @red, @white, @light_grey, @black]
|
111
|
+
|
112
|
+
@marker_color = 'white'
|
113
|
+
|
114
|
+
@base_image = render_image_background('assets/bubble.png')
|
115
|
+
end
|
116
|
+
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
|
|
3
3
|
specification_version: 1
|
4
4
|
name: gruff
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.0.
|
7
|
-
date: 2005-
|
6
|
+
version: 0.0.7
|
7
|
+
date: 2005-12-04 00:00:00 -08:00
|
8
8
|
summary: Beautiful graphs for one or multiple datasets.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -40,13 +40,16 @@ files:
|
|
40
40
|
- lib/gruff/base.rb
|
41
41
|
- lib/gruff/line.rb
|
42
42
|
- lib/gruff/pie.rb
|
43
|
+
- lib/gruff/stacked_bar.rb
|
43
44
|
- assets/bubble.png
|
44
45
|
- assets/plastik
|
46
|
+
- assets/plastik-yellow.png
|
45
47
|
- test/area_test.rb
|
46
48
|
- test/bar_test.rb
|
47
49
|
- test/line_test.rb
|
48
50
|
- test/output
|
49
51
|
- test/pie_test.rb
|
52
|
+
- test/stacked_bar_test.rb
|
50
53
|
test_files: []
|
51
54
|
rdoc_options: []
|
52
55
|
extra_rdoc_files: []
|