gruff 0.0.9 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +7 -3
- data/lib/gruff/area.rb +1 -2
- data/lib/gruff/bar.rb +58 -36
- data/lib/gruff/bar_conversion.rb +63 -0
- data/lib/gruff/base.rb +93 -46
- data/lib/gruff/line.rb +17 -18
- data/lib/gruff/photo_bar.rb +15 -7
- data/lib/gruff/pie.rb +50 -18
- data/lib/gruff/side_stacked_bar.rb +1 -2
- data/lib/gruff/stacked_bar.rb +1 -2
- data/rakefile +4 -2
- data/test/bar_test.rb +53 -1
- data/test/line_test.rb +132 -6
- data/test/pie_test.rb +46 -40
- data/test/stacked_bar_test.rb +0 -2
- metadata +37 -43
data/README
CHANGED
@@ -2,10 +2,14 @@
|
|
2
2
|
|
3
3
|
A library for making beautiful graphs.
|
4
4
|
|
5
|
-
See samples at http://nubyonrails.
|
5
|
+
See samples at http://nubyonrails.com/pages/gruff
|
6
6
|
|
7
|
-
See the test suite in test/line_test.rb for examples.
|
7
|
+
See the test suite in test/line_test.rb for examples.
|
8
|
+
|
9
|
+
== Documentation
|
10
|
+
|
11
|
+
Most of the documentation is in the Gruff::Base class.
|
8
12
|
|
9
13
|
== WARNING
|
10
14
|
|
11
|
-
This is
|
15
|
+
This is beta-quality software. It works well according to my tests, but the API may change and other features will be added.
|
data/lib/gruff/area.rb
CHANGED
@@ -14,7 +14,7 @@ class Gruff::Area < Gruff::Base
|
|
14
14
|
@norm_data.each do |data_row|
|
15
15
|
poly_points = Array.new
|
16
16
|
prev_x = prev_y = 0.0
|
17
|
-
@d = @d.fill
|
17
|
+
@d = @d.fill data_row[DATA_COLOR_INDEX]
|
18
18
|
|
19
19
|
data_row[1].each_with_index do |data_point, index|
|
20
20
|
# Use incremented x and scaled y
|
@@ -49,7 +49,6 @@ class Gruff::Area < Gruff::Base
|
|
49
49
|
|
50
50
|
@d = @d.polyline(*poly_points)
|
51
51
|
|
52
|
-
increment_color()
|
53
52
|
end
|
54
53
|
|
55
54
|
@d.draw(@base_image)
|
data/lib/gruff/bar.rb
CHANGED
@@ -1,45 +1,67 @@
|
|
1
1
|
|
2
2
|
require File.dirname(__FILE__) + '/base'
|
3
|
+
require File.dirname(__FILE__) + '/bar_conversion'
|
3
4
|
|
4
5
|
class Gruff::Bar < Gruff::Base
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
7
|
+
def draw
|
8
|
+
super
|
9
|
+
|
10
|
+
return unless @has_data
|
11
|
+
|
12
|
+
# Setup spacing.
|
13
|
+
#
|
14
|
+
# Columns sit side-by-side.
|
15
|
+
spacing_factor = 0.9
|
16
|
+
@bar_width = @graph_width / (@column_count * @data.length).to_f
|
17
|
+
|
18
|
+
@d = @d.stroke_opacity 0.0
|
19
|
+
|
20
|
+
# setup the BarConversion Object
|
21
|
+
conversion = Gruff::BarConversion.new()
|
22
|
+
conversion.graph_height = @graph_height
|
23
|
+
conversion.graph_top = @graph_top
|
24
|
+
# set up the right mode [1,2,3] see BarConversion for further explains
|
25
|
+
if @minimum_value >= 0 then
|
26
|
+
# all bars go from zero to positiv
|
27
|
+
conversion.mode = 1
|
28
|
+
else
|
29
|
+
# all bars go from 0 to negativ
|
30
|
+
if @maximum_value <= 0 then
|
31
|
+
conversion.mode = 2
|
32
|
+
else
|
33
|
+
# bars either go from zero to negativ or to positiv
|
34
|
+
conversion.mode = 3
|
35
|
+
conversion.spread = @spread
|
36
|
+
conversion.minimum_value = @minimum_value
|
37
|
+
conversion.zero = -@minimum_value/@spread
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# iterate over all normalised data
|
42
|
+
@norm_data.each_with_index do |data_row, row_index|
|
43
|
+
@d = @d.fill data_row[DATA_COLOR_INDEX]
|
44
|
+
|
45
|
+
data_row[1].each_with_index do |data_point, point_index|
|
46
|
+
# Use incremented x and scaled y
|
47
|
+
# x
|
48
|
+
left_x = @graph_left + (@bar_width * (row_index + point_index + ((@data.length - 1) * point_index)))
|
49
|
+
right_x = left_x + @bar_width * spacing_factor
|
50
|
+
# y
|
51
|
+
conv = []
|
52
|
+
conversion.getLeftYRightYscaled( data_point, conv )
|
53
|
+
|
54
|
+
# create new bar
|
55
|
+
@d = @d.rectangle(left_x, conv[0], right_x, conv[1])
|
56
|
+
|
57
|
+
# Calculate center based on bar_width and current row
|
58
|
+
label_center = @graph_left + (@data.length * @bar_width * point_index) + (@data.length * @bar_width / 2.0)
|
59
|
+
draw_label(label_center, point_index)
|
40
60
|
end
|
41
|
-
|
42
|
-
@d.draw(@base_image)
|
61
|
+
|
43
62
|
end
|
44
63
|
|
64
|
+
@d.draw(@base_image)
|
65
|
+
end
|
66
|
+
|
45
67
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
##############################################################################
|
2
|
+
#
|
3
|
+
# Ruby Gruff/bar_conversion
|
4
|
+
#
|
5
|
+
# Copyright: David Stokar
|
6
|
+
#
|
7
|
+
# Date Written: 2006/01/27
|
8
|
+
#
|
9
|
+
# $Revision: 0.2 $
|
10
|
+
#
|
11
|
+
# $Log: bar_conversion.rb $
|
12
|
+
# $Log: Added comments $
|
13
|
+
# ------------------------------------------------------------------------
|
14
|
+
# This program was written by David Stokar is
|
15
|
+
# licensed under the GNU GPL license. Please see below for details. This
|
16
|
+
# header contains information regarding licensing terms under the GPL, and
|
17
|
+
# information regarding obtaining source code from the Author. Consequently,
|
18
|
+
# pursuant to section 3(c) of the GPL, you must accompany the information
|
19
|
+
# found in this header with any distribution you make of this Program.
|
20
|
+
# ------------------------------------------------------------------------
|
21
|
+
##############################################################################
|
22
|
+
|
23
|
+
#
|
24
|
+
# This class perfoms the y coordinats conversion for the bar class
|
25
|
+
# There are 3 cases: 1. Bars all go from zero in positiv direction
|
26
|
+
# 2. Bars all go from zero to negativ direction
|
27
|
+
# 3. Bars either go from zero to positiv or from zero to negativ
|
28
|
+
#
|
29
|
+
class Gruff::BarConversion
|
30
|
+
attr_writer :mode
|
31
|
+
attr_writer :zero
|
32
|
+
attr_writer :graph_top
|
33
|
+
attr_writer :graph_height
|
34
|
+
attr_writer :minimum_value
|
35
|
+
attr_writer :spread
|
36
|
+
|
37
|
+
def getLeftYRightYscaled( data_point, result )
|
38
|
+
case @mode
|
39
|
+
when 1 then # Case one
|
40
|
+
# minimum value >= 0 ( only positiv values )
|
41
|
+
result[0] = @graph_top + @graph_height*(1 - data_point) + 1
|
42
|
+
result[1] = @graph_top + @graph_height - 1
|
43
|
+
when 2 then # Case two
|
44
|
+
# only negativ values
|
45
|
+
result[0] = @graph_top + 1
|
46
|
+
result[1] = @graph_top + @graph_height*(1 - data_point) - 1
|
47
|
+
when 3 then # Case three
|
48
|
+
# positiv and negativ values
|
49
|
+
val = data_point-@minimum_value/@spread
|
50
|
+
if ( data_point >= @zero ) then
|
51
|
+
result[0] = @graph_top + @graph_height*(1 - (val-@zero)) + 1
|
52
|
+
result[1] = @graph_top + @graph_height*(1 - @zero) - 1
|
53
|
+
else
|
54
|
+
result[0] = @graph_top + @graph_height*(1 - (val-@zero)) + 1
|
55
|
+
result[1] = @graph_top + @graph_height*(1 - @zero) - 1
|
56
|
+
end
|
57
|
+
else
|
58
|
+
result[0] = 0.0
|
59
|
+
result[1] = 0.0
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
data/lib/gruff/base.rb
CHANGED
@@ -8,21 +8,25 @@
|
|
8
8
|
#
|
9
9
|
# Other contributors:
|
10
10
|
#
|
11
|
-
# Jarkko Laine, Mike Perham, Andreas Schwarz, Alun Eyre, Guillaume Theoret, and others.
|
11
|
+
# Jarkko Laine, Mike Perham, Andreas Schwarz, Alun Eyre, Guillaume Theoret, David Stokar, and others.
|
12
12
|
#
|
13
13
|
|
14
14
|
require 'RMagick'
|
15
15
|
|
16
|
-
require 'yaml'
|
16
|
+
#require 'yaml'
|
17
17
|
|
18
18
|
module Gruff
|
19
19
|
|
20
|
-
VERSION = '0.0
|
20
|
+
VERSION = '0.1.0'
|
21
21
|
|
22
22
|
class Base
|
23
23
|
|
24
24
|
include Magick
|
25
25
|
|
26
|
+
DATA_LABEL_INDEX = 0
|
27
|
+
DATA_VALUES_INDEX = 1
|
28
|
+
DATA_COLOR_INDEX = 2
|
29
|
+
|
26
30
|
# A hash of names for the individual columns, where the key is the array index for the column this label represents.
|
27
31
|
#
|
28
32
|
# Not all columns need to be named.
|
@@ -30,6 +34,9 @@ module Gruff
|
|
30
34
|
# Example: 0 => 2005, 3 => 2006, 5 => 2007, 7 => 2008
|
31
35
|
attr_accessor :labels
|
32
36
|
|
37
|
+
# Get or set the list of colors that will be used to draw the bars or lines.
|
38
|
+
attr_accessor :colors
|
39
|
+
|
33
40
|
# The large title of the graph displayed at the top
|
34
41
|
attr_accessor :title
|
35
42
|
|
@@ -37,8 +44,17 @@ module Gruff
|
|
37
44
|
# RMagick must be built with the Freetype libraries for this to work properly.
|
38
45
|
attr_accessor :font
|
39
46
|
|
47
|
+
# Hide various elements
|
40
48
|
attr_accessor :hide_line_markers, :hide_legend, :hide_title
|
41
49
|
|
50
|
+
# Message shown when there is no data. Fits up to 20 characters. Defaults to "No Data."
|
51
|
+
attr_accessor :no_data_message
|
52
|
+
|
53
|
+
# Optionally set the size of the legend font. Based on an 800x600px graph. Default is 20.
|
54
|
+
#
|
55
|
+
# Will be scaled down if graph is smaller than 800px wide.
|
56
|
+
attr_accessor :legend_font_size
|
57
|
+
|
42
58
|
# Graph is drawn at 4/3 ratio (800x600, 400x300, etc.).
|
43
59
|
#
|
44
60
|
# Looks for Bitstream Vera as the default font. Expects an environment var of MAGICK_FONT_PATH to be set.
|
@@ -61,18 +77,22 @@ module Gruff
|
|
61
77
|
@raw_rows = 800.0 * (@rows/@columns)
|
62
78
|
@column_count = 0
|
63
79
|
@maximum_value = 0
|
80
|
+
@minimum_value = 0
|
64
81
|
@has_data = false
|
65
82
|
@data = Array.new
|
66
83
|
@labels = Hash.new
|
67
84
|
@labels_seen = Hash.new
|
68
85
|
@scale = @columns / @raw_columns
|
86
|
+
@legend_font_size = 20
|
87
|
+
|
88
|
+
@no_data_message = "No Data"
|
69
89
|
|
70
90
|
@hide_line_markers = @hide_legend = @hide_title = false
|
71
91
|
|
72
92
|
reset_themes()
|
73
93
|
theme_keynote()
|
74
94
|
end
|
75
|
-
|
95
|
+
|
76
96
|
# Add a color to the list of available colors for lines.
|
77
97
|
#
|
78
98
|
# Example:
|
@@ -82,7 +102,7 @@ module Gruff
|
|
82
102
|
end
|
83
103
|
|
84
104
|
# Replace the entire color list with a new array of colors. You need to have one more color
|
85
|
-
# than the number of datasets you intend to draw.
|
105
|
+
# than the number of datasets you intend to draw. Also aliased as the colors= setter method.
|
86
106
|
#
|
87
107
|
# Example:
|
88
108
|
# replace_colors('#cc99cc', '#d9e043', '#34d8a2')
|
@@ -162,16 +182,21 @@ module Gruff
|
|
162
182
|
@base_image = render_gradiated_background('#ff47a4', '#ff1f81')
|
163
183
|
end
|
164
184
|
|
165
|
-
#
|
185
|
+
# Parameters are an array where the first element is the name of the dataset
|
166
186
|
# and the value is an array of values to plot.
|
167
187
|
#
|
168
|
-
# Can be called multiple times with different datasets
|
169
|
-
#
|
188
|
+
# Can be called multiple times with different datasets for a multi-valued graph.
|
189
|
+
#
|
190
|
+
# If the color argument is nil, the next color from the default theme will be used.
|
191
|
+
#
|
192
|
+
# NOTE: If you want to use a preset theme, you must set it before calling data().
|
170
193
|
#
|
171
194
|
# Example:
|
172
|
-
#
|
173
|
-
|
174
|
-
|
195
|
+
#
|
196
|
+
# data("Bart S.", [95, 45, 78, 89, 88, 76], '#ffcc00')
|
197
|
+
#
|
198
|
+
def data(name, data_points=[], color=nil)
|
199
|
+
@data << [name, data_points, color || increment_color]
|
175
200
|
# Set column count if this is larger than previous counts
|
176
201
|
@column_count = (data_points.length > @column_count) ? data_points.length : @column_count
|
177
202
|
|
@@ -179,11 +204,19 @@ module Gruff
|
|
179
204
|
data_points.each_with_index do |data_point, index|
|
180
205
|
next if data_point.nil?
|
181
206
|
|
182
|
-
|
183
|
-
|
207
|
+
# Setup max/min so spread starts at the low end of the data points (instead of 0)
|
208
|
+
if @maximum_value == 0 && @minimum_value == 0 && data_point != 0
|
209
|
+
@maximum_value = @minimum_value = data_point
|
210
|
+
end
|
211
|
+
|
212
|
+
@maximum_value = larger_than_max?(data_point, index) ? max(data_point, index) : @maximum_value
|
184
213
|
if @maximum_value > 0
|
185
214
|
@has_data = true
|
186
215
|
end
|
216
|
+
@minimum_value = (data_point < @minimum_value) ? data_point : @minimum_value
|
217
|
+
if @minimum_value < 0
|
218
|
+
@has_data = true
|
219
|
+
end
|
187
220
|
end
|
188
221
|
end
|
189
222
|
|
@@ -228,6 +261,7 @@ protected
|
|
228
261
|
|
229
262
|
normalize()
|
230
263
|
setup_graph_measurements()
|
264
|
+
sort_norm_data() # Sort norm_data with avg largest values set first (for display)
|
231
265
|
|
232
266
|
draw_line_markers()
|
233
267
|
draw_legend()
|
@@ -237,19 +271,21 @@ protected
|
|
237
271
|
# Make copy of data with values scaled between 0-100
|
238
272
|
def normalize
|
239
273
|
if @norm_data.nil?
|
240
|
-
@norm_data =
|
274
|
+
@norm_data = []
|
241
275
|
return unless @has_data
|
242
|
-
|
276
|
+
@spread = @maximum_value.to_f - @minimum_value.to_f
|
277
|
+
@spread = 1 if @spread == 0 # Protect from divide by 0
|
278
|
+
min_val = @minimum_value.to_f
|
243
279
|
@data.each do |data_row|
|
244
|
-
norm_data_points =
|
245
|
-
data_row[
|
280
|
+
norm_data_points = []
|
281
|
+
data_row[DATA_VALUES_INDEX].each do |data_point|
|
246
282
|
if data_point.nil?
|
247
283
|
norm_data_points << nil
|
248
284
|
else
|
249
|
-
norm_data_points << (data_point.to_f
|
285
|
+
norm_data_points << ((data_point.to_f - min_val ) / @spread)
|
250
286
|
end
|
251
287
|
end
|
252
|
-
@norm_data << [data_row[
|
288
|
+
@norm_data << [data_row[DATA_LABEL_INDEX], norm_data_points, data_row[DATA_COLOR_INDEX]]
|
253
289
|
end
|
254
290
|
end
|
255
291
|
end
|
@@ -284,14 +320,18 @@ protected
|
|
284
320
|
number_of_lines = 4
|
285
321
|
|
286
322
|
# TODO Round maximum marker value to a round number like 100, 0.1, 0.5, etc.
|
287
|
-
|
288
|
-
|
323
|
+
|
324
|
+
# added relative height
|
325
|
+
spread = @maximum_value.to_f - @minimum_value.to_f
|
326
|
+
increment = significant(spread / number_of_lines)
|
327
|
+
inc_graph = @graph_height.to_f / (spread / increment)
|
328
|
+
#inc_graph = @graph_height.to_f / increment
|
289
329
|
|
290
330
|
(0..number_of_lines).each do |index|
|
291
331
|
y = @graph_top + @graph_height - index.to_f * inc_graph
|
292
332
|
@d = @d.line(@graph_left, y, @graph_right, y)
|
293
333
|
|
294
|
-
marker_label = index * increment
|
334
|
+
marker_label = index * increment + @minimum_value.to_f
|
295
335
|
|
296
336
|
@d.fill = @marker_color
|
297
337
|
@d.font = @font
|
@@ -302,6 +342,7 @@ protected
|
|
302
342
|
100, 20,
|
303
343
|
-10, y - (@marker_pointsize/2.0),
|
304
344
|
marker_label.to_s, @scale)
|
345
|
+
# "help", @scale)
|
305
346
|
end
|
306
347
|
end
|
307
348
|
|
@@ -309,17 +350,13 @@ protected
|
|
309
350
|
def draw_legend
|
310
351
|
return if @hide_legend
|
311
352
|
|
312
|
-
|
313
|
-
sort_norm_data()
|
314
|
-
|
315
|
-
@color_index = 0
|
316
|
-
@legend_labels = @norm_data.collect {|item| item[0] }
|
353
|
+
@legend_labels = @data.collect {|item| item[DATA_LABEL_INDEX] }
|
317
354
|
|
318
355
|
legend_square_width = 20 # small square with color of this item
|
319
356
|
|
320
357
|
# May fix legend drawing problem at small sizes
|
321
358
|
@d.font = @font
|
322
|
-
@d.pointsize =
|
359
|
+
@d.pointsize = legend_font_size
|
323
360
|
|
324
361
|
metrics = @d.get_type_metrics(@base_image, @legend_labels.join(''))
|
325
362
|
legend_text_width = metrics.width
|
@@ -332,7 +369,7 @@ protected
|
|
332
369
|
# Draw label
|
333
370
|
@d.fill = @marker_color
|
334
371
|
@d.font = @font
|
335
|
-
@d.pointsize = scale_fontsize(
|
372
|
+
@d.pointsize = scale_fontsize(legend_font_size)
|
336
373
|
@d.stroke = 'transparent'
|
337
374
|
@d.font_weight = NormalWeight
|
338
375
|
@d.gravity = WestGravity
|
@@ -344,13 +381,11 @@ protected
|
|
344
381
|
# Now draw box with color of this dataset
|
345
382
|
legend_box_y_offset = 2 # Move box down slightly to center
|
346
383
|
@d = @d.stroke 'transparent'
|
347
|
-
@d = @d.fill
|
384
|
+
@d = @d.fill @data[index][DATA_COLOR_INDEX]
|
348
385
|
@d = @d.rectangle(current_x_offset, 70 + legend_box_y_offset,
|
349
386
|
current_x_offset + legend_square_width, 70 + legend_square_width + legend_box_y_offset)
|
350
|
-
|
351
|
-
increment_color()
|
352
387
|
|
353
|
-
@d.pointsize =
|
388
|
+
@d.pointsize = legend_font_size
|
354
389
|
metrics = @d.get_type_metrics(@base_image, legend_label.to_s)
|
355
390
|
current_string_offset = metrics.width + (legend_square_width * 2.7)
|
356
391
|
current_x_offset += current_string_offset
|
@@ -403,7 +438,7 @@ protected
|
|
403
438
|
@d = @d.annotate_scaled( @base_image,
|
404
439
|
@raw_columns, @raw_rows/2.0,
|
405
440
|
0, 10,
|
406
|
-
|
441
|
+
@no_data_message, @scale)
|
407
442
|
end
|
408
443
|
|
409
444
|
# Use with a theme definition method to draw a gradiated (or solid color) background.
|
@@ -421,16 +456,6 @@ protected
|
|
421
456
|
image[0]
|
422
457
|
end
|
423
458
|
|
424
|
-
def current_color
|
425
|
-
@colors[@color_index]
|
426
|
-
end
|
427
|
-
|
428
|
-
def increment_color
|
429
|
-
@color_index += 1
|
430
|
-
raise(ColorlistExhaustedException, "There are no more colors left to use.") if @color_index == @colors.length
|
431
|
-
current_color
|
432
|
-
end
|
433
|
-
|
434
459
|
def reset_themes
|
435
460
|
@color_index = 0
|
436
461
|
@labels_seen = Hash.new
|
@@ -460,12 +485,18 @@ protected
|
|
460
485
|
data_point > @maximum_value
|
461
486
|
end
|
462
487
|
|
488
|
+
def less_than_min?(data_point, index=0)
|
489
|
+
data_point < @minimum_value
|
490
|
+
end
|
491
|
+
|
463
492
|
def max(data_point, index)
|
464
493
|
data_point
|
465
494
|
end
|
466
495
|
|
467
|
-
|
468
|
-
|
496
|
+
def min(data_point, index)
|
497
|
+
data_point
|
498
|
+
end
|
499
|
+
|
469
500
|
def significant(inc)
|
470
501
|
factor = 1.0
|
471
502
|
while (inc < 10)
|
@@ -498,6 +529,22 @@ protected
|
|
498
529
|
total_sum
|
499
530
|
end
|
500
531
|
|
532
|
+
private
|
533
|
+
|
534
|
+
def increment_color
|
535
|
+
if @color_index == 0
|
536
|
+
@color_index += 1
|
537
|
+
return @colors[0]
|
538
|
+
else
|
539
|
+
if @color_index < @colors.length
|
540
|
+
@color_index += 1
|
541
|
+
return @colors[@color_index - 1]
|
542
|
+
else
|
543
|
+
raise(ColorlistExhaustedException, "There are no more colors left to use.")
|
544
|
+
end
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
501
548
|
end
|
502
549
|
|
503
550
|
class ColorlistExhaustedException < StandardError; end
|
data/lib/gruff/line.rb
CHANGED
@@ -9,18 +9,18 @@ class Gruff::Line < Gruff::Base
|
|
9
9
|
# Color of the baseline
|
10
10
|
attr_accessor :baseline_color
|
11
11
|
|
12
|
+
# Hide parts of the graph to fit more datapoints, or for a different appearance.
|
12
13
|
attr_accessor :hide_dots, :hide_lines
|
13
|
-
|
14
|
-
#
|
14
|
+
|
15
15
|
# Call with target pixel width of graph (800, 400, 300), and/or 'false' to omit lines (points only).
|
16
16
|
#
|
17
17
|
# g = Gruff::Line.new(400) # 400px wide with lines
|
18
18
|
#
|
19
|
-
# g = Gruff::Line.new(400, false) # 400px wide, no lines
|
19
|
+
# g = Gruff::Line.new(400, false) # 400px wide, no lines (for backwards compatibility)
|
20
20
|
#
|
21
|
-
# g = Gruff::Line.new(false) # Defaults to 800px wide, no lines
|
21
|
+
# g = Gruff::Line.new(false) # Defaults to 800px wide, no lines (for backwards compatibility)
|
22
22
|
#
|
23
|
-
#
|
23
|
+
# The preferred way is to call hide_dots or hide_lines instead.
|
24
24
|
def initialize(*args)
|
25
25
|
raise ArgumentError, "Wrong number of arguments" if args.length > 2
|
26
26
|
if args.empty? or ((not Numeric === args.first) && (not String === args.first)) then
|
@@ -46,27 +46,27 @@ class Gruff::Line < Gruff::Base
|
|
46
46
|
@d = @d.stroke_width clip_value_if_greater_than(@columns / (@norm_data.first[1].size * 4), 5.0)
|
47
47
|
|
48
48
|
if (defined?(@norm_baseline)) then
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
49
|
+
level = @graph_top + (@graph_height - @norm_baseline * @graph_height)
|
50
|
+
@d = @d.push
|
51
|
+
@d.stroke_color @baseline_color
|
52
|
+
@d.fill_opacity 0.0
|
53
|
+
@d.stroke_dasharray(10, 20)
|
54
|
+
@d.stroke_width 5
|
55
|
+
@d.line(@graph_left, level, @graph_left + @graph_width, level)
|
56
|
+
@d = @d.pop
|
57
57
|
end
|
58
58
|
|
59
59
|
@norm_data.each do |data_row|
|
60
60
|
prev_x = prev_y = 0.0
|
61
|
-
@d = @d.stroke
|
62
|
-
@d = @d.fill
|
61
|
+
@d = @d.stroke data_row[DATA_COLOR_INDEX]
|
62
|
+
@d = @d.fill data_row[DATA_COLOR_INDEX]
|
63
63
|
|
64
64
|
data_row[1].each_with_index do |data_point, index|
|
65
65
|
new_x = @graph_left + (@x_increment * index)
|
66
|
-
draw_label(new_x, index)
|
67
|
-
|
68
66
|
next if data_point.nil?
|
69
67
|
|
68
|
+
draw_label(new_x, index)
|
69
|
+
|
70
70
|
new_y = @graph_top + (@graph_height - data_point * @graph_height)
|
71
71
|
|
72
72
|
if !@hide_lines and prev_x > 0 and prev_y > 0 then
|
@@ -78,7 +78,6 @@ class Gruff::Line < Gruff::Base
|
|
78
78
|
prev_y = new_y
|
79
79
|
end
|
80
80
|
|
81
|
-
increment_color()
|
82
81
|
end
|
83
82
|
|
84
83
|
@d.draw(@base_image)
|
data/lib/gruff/photo_bar.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/base'
|
2
2
|
|
3
|
+
# EXPERIMENTAL!
|
4
|
+
#
|
5
|
+
# Doesn't work yet.
|
6
|
+
#
|
3
7
|
class Gruff::PhotoBar < Gruff::Base
|
4
8
|
|
5
9
|
# TODO
|
@@ -14,14 +18,19 @@ class Gruff::PhotoBar < Gruff::Base
|
|
14
18
|
# The name of a pre-packaged photo-based theme.
|
15
19
|
attr_accessor :theme
|
16
20
|
|
21
|
+
# def initialize(target_width=800)
|
22
|
+
# super
|
23
|
+
# init_photo_bar_graphics()
|
24
|
+
# end
|
25
|
+
|
17
26
|
def draw
|
18
27
|
super
|
19
|
-
|
20
28
|
return unless @has_data
|
21
29
|
|
22
|
-
# TODO
|
23
|
-
init_photo_bar_graphics()
|
30
|
+
return # TODO Remove for further development
|
24
31
|
|
32
|
+
init_photo_bar_graphics()
|
33
|
+
|
25
34
|
#Draw#define_clip_path()
|
26
35
|
#Draw#clip_path(pathname)
|
27
36
|
#Draw#composite....with bar graph image OverCompositeOp
|
@@ -35,7 +44,7 @@ class Gruff::PhotoBar < Gruff::Base
|
|
35
44
|
#
|
36
45
|
# Columns sit side-by-side.
|
37
46
|
spacing_factor = 0.9
|
38
|
-
@bar_width =
|
47
|
+
@bar_width = @norm_data[0][DATA_COLOR_INDEX].columns
|
39
48
|
|
40
49
|
@norm_data.each_with_index do |data_row, row_index|
|
41
50
|
|
@@ -47,11 +56,11 @@ class Gruff::PhotoBar < Gruff::Base
|
|
47
56
|
right_x = left_x + @bar_width * spacing_factor
|
48
57
|
right_y = @graph_top + @graph_height - 1
|
49
58
|
|
50
|
-
bar_image_width =
|
59
|
+
bar_image_width = data_row[DATA_COLOR_INDEX].columns
|
51
60
|
bar_image_height = right_y.to_f - left_y.to_f
|
52
61
|
|
53
62
|
# Crop to scale for data
|
54
|
-
bar_image =
|
63
|
+
bar_image = data_row[DATA_COLOR_INDEX].crop(0, 0, bar_image_width, bar_image_height)
|
55
64
|
|
56
65
|
@d.gravity = NorthWestGravity
|
57
66
|
@d = @d.composite(left_x, left_y, bar_image_width, bar_image_height, bar_image)
|
@@ -61,7 +70,6 @@ class Gruff::PhotoBar < Gruff::Base
|
|
61
70
|
draw_label(label_center, point_index)
|
62
71
|
end
|
63
72
|
|
64
|
-
increment_color()
|
65
73
|
end
|
66
74
|
|
67
75
|
@d.draw(@base_image)
|
data/lib/gruff/pie.rb
CHANGED
@@ -14,30 +14,33 @@ class Gruff::Pie < Gruff::Base
|
|
14
14
|
radius = @graph_height / 2.0
|
15
15
|
top_x = @graph_left + (@graph_width - diameter) / 2.0
|
16
16
|
center_x = @graph_left + (@graph_width / 2.0)
|
17
|
-
center_y = @graph_top + (@graph_height / 2.0)
|
17
|
+
center_y = @graph_top + (@graph_height / 2.0) - 10 # Move graph up a bit
|
18
18
|
total_sum = sums_for_pie()
|
19
19
|
prev_degrees = 0.0
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
# Use full data since we can easily calculate percentages
|
22
|
+
@data.each do |data_row|
|
23
|
+
if data_row[1][0] > 0
|
24
|
+
@d = @d.stroke data_row[DATA_COLOR_INDEX]
|
25
|
+
@d = @d.fill 'transparent'
|
26
|
+
@d.stroke_width(200.0)
|
25
27
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
#@d = @d.arc(top_x, @graph_top,
|
31
|
-
# top_x + diameter, @graph_top + diameter,
|
32
|
-
# prev_degrees, prev_degrees + current_degrees)
|
33
|
-
radius = 100.0
|
34
|
-
@d = @d.ellipse(center_x, center_y,
|
28
|
+
current_degrees = (data_row[1][0] / total_sum) * 360.0
|
29
|
+
radius = 100.0
|
30
|
+
@d = @d.ellipse(center_x, center_y,
|
35
31
|
radius, radius,
|
36
|
-
prev_degrees, prev_degrees + current_degrees)
|
32
|
+
prev_degrees, prev_degrees + current_degrees + 0.5) # <= +0.5 'fudge factor' gets rid of the ugly gaps
|
33
|
+
|
34
|
+
half_angle = prev_degrees + ((prev_degrees + current_degrees) - prev_degrees) / 2
|
37
35
|
|
36
|
+
|
37
|
+
@d = draw_label(center_x,center_y,
|
38
|
+
half_angle,
|
39
|
+
radius,
|
40
|
+
((data_row[1][0] / total_sum) * 100).round.to_s + '% ')
|
38
41
|
|
39
|
-
|
40
|
-
|
42
|
+
prev_degrees += current_degrees
|
43
|
+
end
|
41
44
|
end
|
42
45
|
|
43
46
|
@d.draw(@base_image)
|
@@ -45,14 +48,43 @@ class Gruff::Pie < Gruff::Base
|
|
45
48
|
|
46
49
|
private
|
47
50
|
|
51
|
+
def draw_label(center_x, center_y, angle, radius, amount)
|
52
|
+
r_offset = 130 # The distance out from the center of the pie to get point
|
53
|
+
x_offset = center_x + 15 # The label points need to be tweaked slightly
|
54
|
+
y_offset = center_y + 0 # This one doesn't though
|
55
|
+
x = x_offset + ((radius + r_offset) * Math.cos(angle.deg2rad))
|
56
|
+
y = y_offset + ((radius + r_offset) * Math.sin(angle.deg2rad))
|
57
|
+
|
58
|
+
# Draw label
|
59
|
+
@d.fill = @marker_color
|
60
|
+
@d.font = @font
|
61
|
+
@d.pointsize = scale_fontsize(20)
|
62
|
+
@d.stroke = 'transparent'
|
63
|
+
@d.font_weight = BoldWeight
|
64
|
+
@d.gravity = CenterGravity
|
65
|
+
@d.annotate_scaled( @base_image,
|
66
|
+
0, 0,
|
67
|
+
x, y,
|
68
|
+
amount, @scale)
|
69
|
+
end
|
70
|
+
|
48
71
|
def sums_for_pie
|
49
72
|
total_sum = 0.0
|
50
|
-
@
|
73
|
+
@data.collect {|data_row| total_sum += data_row[1][0] }
|
51
74
|
total_sum
|
52
75
|
end
|
53
76
|
|
54
77
|
end
|
55
78
|
|
79
|
+
|
80
|
+
class Float
|
81
|
+
# Used for degree => radian conversions
|
82
|
+
def deg2rad
|
83
|
+
self * (Math::PI/180.0)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# From sparklines...not currently used
|
56
88
|
class Magick::Draw
|
57
89
|
|
58
90
|
def pie_slice(center_x=0.0, center_y=0.0, radius=100.0, percent=0.0, rot_x=0.0)
|
@@ -74,7 +74,7 @@ class Gruff::SideStackedBar < Gruff::Base
|
|
74
74
|
length = Array.new(@column_count, @graph_left)
|
75
75
|
|
76
76
|
@norm_data.each_with_index do |data_row, row_index|
|
77
|
-
@d = @d.fill
|
77
|
+
@d = @d.fill data_row[DATA_COLOR_INDEX]
|
78
78
|
|
79
79
|
data_row[1].each_with_index do |data_point, point_index|
|
80
80
|
|
@@ -100,7 +100,6 @@ class Gruff::SideStackedBar < Gruff::Base
|
|
100
100
|
draw_label(label_center, point_index)
|
101
101
|
end
|
102
102
|
|
103
|
-
increment_color()
|
104
103
|
end
|
105
104
|
|
106
105
|
@d.draw(@base_image)
|
data/lib/gruff/stacked_bar.rb
CHANGED
@@ -20,7 +20,7 @@ class Gruff::StackedBar < Gruff::Base
|
|
20
20
|
height = Array.new(@column_count, 0)
|
21
21
|
|
22
22
|
@norm_data.each_with_index do |data_row, row_index|
|
23
|
-
@d = @d.fill
|
23
|
+
@d = @d.fill data_row[DATA_COLOR_INDEX]
|
24
24
|
|
25
25
|
data_row[1].each_with_index do |data_point, point_index|
|
26
26
|
# Use incremented x and scaled y
|
@@ -41,7 +41,6 @@ class Gruff::StackedBar < Gruff::Base
|
|
41
41
|
draw_label(label_center, point_index)
|
42
42
|
end
|
43
43
|
|
44
|
-
increment_color()
|
45
44
|
end
|
46
45
|
|
47
46
|
@d.draw(@base_image)
|
data/rakefile
CHANGED
@@ -19,11 +19,13 @@ RUBY_FORGE_PROJECT = "gruff"
|
|
19
19
|
RUBY_FORGE_USER = "topfunky"
|
20
20
|
|
21
21
|
desc "Default Task"
|
22
|
-
task :default => [ :test ]
|
22
|
+
task :default => [ :clean, :test ]
|
23
23
|
|
24
24
|
desc "Clean images generated by tests"
|
25
25
|
task :clean do
|
26
26
|
rm FileList['test/output/*.png']
|
27
|
+
rm_rf 'pkg'
|
28
|
+
rm_rf 'doc'
|
27
29
|
end
|
28
30
|
|
29
31
|
# Run the unit tests
|
@@ -38,7 +40,7 @@ Rake::TestTask.new { |t|
|
|
38
40
|
Rake::RDocTask.new { |rdoc|
|
39
41
|
rdoc.rdoc_dir = 'doc'
|
40
42
|
rdoc.title = "Gruff -- Beautiful graphs"
|
41
|
-
rdoc.options << '--line-numbers --inline-source --main README --accessor adv_attr_accessor=M'
|
43
|
+
# rdoc.options << '--line-numbers --inline-source --main README --accessor adv_attr_accessor=M'
|
42
44
|
rdoc.template = "#{ENV['template']}.rb" if ENV['template']
|
43
45
|
rdoc.rdoc_files.include('README', 'CHANGELOG')
|
44
46
|
rdoc.rdoc_files.include('lib/gruff.rb')
|
data/test/bar_test.rb
CHANGED
@@ -33,16 +33,52 @@ class TestGruffBar < Test::Unit::TestCase
|
|
33
33
|
@datasets.each do |data|
|
34
34
|
g.data(data[0], data[1])
|
35
35
|
end
|
36
|
-
|
37
36
|
g.write("test/output/bar_keynote.png")
|
38
37
|
|
38
|
+
g = Gruff::Bar.new
|
39
|
+
g.title = "Visual Multi-Line Bar Graph Test"
|
40
|
+
g.labels = {
|
41
|
+
0 => '5/6',
|
42
|
+
1 => '5/15',
|
43
|
+
2 => '5/24',
|
44
|
+
3 => '5/30',
|
45
|
+
}
|
39
46
|
g.theme_rails_keynote
|
47
|
+
@datasets.each do |data|
|
48
|
+
g.data(data[0], data[1])
|
49
|
+
end
|
40
50
|
g.write("test/output/bar_rails_keynote.png")
|
41
51
|
|
52
|
+
g = Gruff::Bar.new
|
53
|
+
g.title = "Visual Multi-Line Bar Graph Test"
|
54
|
+
g.labels = {
|
55
|
+
0 => '5/6',
|
56
|
+
1 => '5/15',
|
57
|
+
2 => '5/24',
|
58
|
+
3 => '5/30',
|
59
|
+
}
|
42
60
|
g.theme_odeo
|
61
|
+
@datasets.each do |data|
|
62
|
+
g.data(data[0], data[1])
|
63
|
+
end
|
43
64
|
g.write("test/output/bar_odeo.png")
|
44
65
|
end
|
45
66
|
|
67
|
+
def test_bar_graph_set_colors
|
68
|
+
g = Gruff::Bar.new
|
69
|
+
g.title = "Bar Graph With Manual Colors"
|
70
|
+
g.labels = {
|
71
|
+
0 => '5/6',
|
72
|
+
1 => '5/15',
|
73
|
+
2 => '5/24',
|
74
|
+
3 => '5/30',
|
75
|
+
}
|
76
|
+
g.data(:Art, [0, 5, 8, 15], '#990000')
|
77
|
+
g.data(:Philosophy, [10, 3, 2, 8], '#009900')
|
78
|
+
g.data(:Science, [2, 15, 8, 11], '#990099')
|
79
|
+
|
80
|
+
g.write("test/output/bar_manual_colors.png")
|
81
|
+
end
|
46
82
|
|
47
83
|
def test_bar_graph_small
|
48
84
|
g = Gruff::Bar.new(400)
|
@@ -95,6 +131,22 @@ class TestGruffBar < Test::Unit::TestCase
|
|
95
131
|
end
|
96
132
|
|
97
133
|
|
134
|
+
def test_negative
|
135
|
+
g = Gruff::Bar.new
|
136
|
+
g.title = "Pos/Neg Bar Graph Test"
|
137
|
+
g.labels = {
|
138
|
+
0 => '5/6',
|
139
|
+
1 => '5/15',
|
140
|
+
2 => '5/24',
|
141
|
+
3 => '5/30',
|
142
|
+
}
|
143
|
+
g.data(:apples, [-1, 0, 4, -4])
|
144
|
+
g.data(:peaches, [10, 8, 6, 3])
|
145
|
+
|
146
|
+
g.write("test/output/bar_pos_neg.png")
|
147
|
+
end
|
148
|
+
|
149
|
+
|
98
150
|
protected
|
99
151
|
|
100
152
|
def setup_basic_graph(size=800)
|
data/test/line_test.rb
CHANGED
@@ -48,7 +48,6 @@ class TestGruffLine < Test::Unit::TestCase
|
|
48
48
|
@datasets.each do |data|
|
49
49
|
g.data(data[0], data[1])
|
50
50
|
end
|
51
|
-
|
52
51
|
g.write("test/output/line_small.png")
|
53
52
|
|
54
53
|
g = Gruff::Line.new(400)
|
@@ -56,7 +55,6 @@ class TestGruffLine < Test::Unit::TestCase
|
|
56
55
|
@datasets.each do |data|
|
57
56
|
g.data(data[0], data[1])
|
58
57
|
end
|
59
|
-
|
60
58
|
g.write("test/output/line_small_small.png")
|
61
59
|
end
|
62
60
|
|
@@ -114,7 +112,7 @@ class TestGruffLine < Test::Unit::TestCase
|
|
114
112
|
def test_similar_high_end_values
|
115
113
|
g = Gruff::Line.new
|
116
114
|
g.title = "Similar High End Values Test"
|
117
|
-
g.data('similar points', %w(29.43 29.459 29.498 29.53 29.548 29.589 29.619 29.66 29.689 29.
|
115
|
+
g.data('similar points', %w(29.43 29.459 29.498 29.53 29.548 29.589 29.619 29.66 29.689 29.849 29.878 29.74 29.769 29.79 29.808 29.828).collect {|i| i.to_f} )
|
118
116
|
|
119
117
|
# Default theme
|
120
118
|
g.write("test/output/line_similar_high_end_values.png")
|
@@ -220,9 +218,13 @@ class TestGruffLine < Test::Unit::TestCase
|
|
220
218
|
def test_no_data
|
221
219
|
g = Gruff::Line.new(400)
|
222
220
|
g.title = "No Data"
|
223
|
-
|
224
221
|
# Default theme
|
225
|
-
g.write("test/output/line_no_data.png")
|
222
|
+
g.write("test/output/line_no_data.png")
|
223
|
+
|
224
|
+
g = Gruff::Line.new(400)
|
225
|
+
g.title = "No Data Title"
|
226
|
+
g.no_data_message = 'There is no data'
|
227
|
+
g.write("test/output/line_no_data_msg.png")
|
226
228
|
end
|
227
229
|
|
228
230
|
|
@@ -244,7 +246,8 @@ class TestGruffLine < Test::Unit::TestCase
|
|
244
246
|
@datasets = [
|
245
247
|
[:data1, [1, 2, 3, nil, 3, 5, 6]],
|
246
248
|
[:data2, [5, nil, nil, nil, nil, nil, 5]],
|
247
|
-
[:data3, [4, nil, 2, 1, 0]]
|
249
|
+
[:data3, [4, nil, 2, 1, 0]],
|
250
|
+
[:data4, [nil, nil, 3, 1, 2]]
|
248
251
|
]
|
249
252
|
|
250
253
|
@datasets.each do |data|
|
@@ -320,6 +323,100 @@ class TestGruffLine < Test::Unit::TestCase
|
|
320
323
|
g.write("test/output/line_wide_graph_small.png")
|
321
324
|
end
|
322
325
|
|
326
|
+
def test_negative
|
327
|
+
g = setup_pos_neg(800)
|
328
|
+
g.write("test/output/line_pos_neg.png")
|
329
|
+
|
330
|
+
g = setup_pos_neg(400)
|
331
|
+
g.title = 'Pos/Neg Line Test Small'
|
332
|
+
g.write("test/output/line_pos_neg_400.png")
|
333
|
+
end
|
334
|
+
|
335
|
+
def test_all_negative
|
336
|
+
g = setup_all_neg(800)
|
337
|
+
g.write("test/output/line_all_neg.png")
|
338
|
+
|
339
|
+
g = setup_all_neg(400)
|
340
|
+
g.title = 'All Neg Line Test Small'
|
341
|
+
g.write("test/output/line_all_neg_400.png")
|
342
|
+
end
|
343
|
+
|
344
|
+
|
345
|
+
def test_many_numbers
|
346
|
+
g = Gruff::Line.new('400x170')
|
347
|
+
g.title = "Line Test, Many Numbers"
|
348
|
+
|
349
|
+
data = [
|
350
|
+
{ :date => '01',
|
351
|
+
:wpm => 0,
|
352
|
+
:errors => 0,
|
353
|
+
:accuracy => 0 },
|
354
|
+
{ :date => '02',
|
355
|
+
:wpm => 10,
|
356
|
+
:errors => 2,
|
357
|
+
:accuracy => 80 },
|
358
|
+
{ :date => '03',
|
359
|
+
:wpm => 15,
|
360
|
+
:errors => 0,
|
361
|
+
:accuracy => 100 },
|
362
|
+
{ :date => '04',
|
363
|
+
:wpm => 16,
|
364
|
+
:errors => 2,
|
365
|
+
:accuracy => 87 },
|
366
|
+
{ :date => '05',
|
367
|
+
:wpm => nil,
|
368
|
+
:errors => nil,
|
369
|
+
:accuracy => nil },
|
370
|
+
{ :date => '06',
|
371
|
+
:wpm => 18,
|
372
|
+
:errors => 1,
|
373
|
+
:accuracy => 94 },
|
374
|
+
{ :date => '07'},
|
375
|
+
{ :date => '08' },
|
376
|
+
{ :date => '09',
|
377
|
+
:wpm => 21,
|
378
|
+
:errors => 1,
|
379
|
+
:accuracy => 95 },
|
380
|
+
{ :date => '10'},
|
381
|
+
{ :date => '11'},
|
382
|
+
{ :date => '12'},
|
383
|
+
{ :date => '13'},
|
384
|
+
{ :date => '14'},
|
385
|
+
{ :date => '15'},
|
386
|
+
{ :date => '16'},
|
387
|
+
{ :date => '17'},
|
388
|
+
{ :date => '18'},
|
389
|
+
{ :date => '19',
|
390
|
+
:wpm => 28,
|
391
|
+
:errors => 5,
|
392
|
+
:accuracy => 82 },
|
393
|
+
{ :date => '20'},
|
394
|
+
{ :date => '21'},
|
395
|
+
{ :date => '22'},
|
396
|
+
{ :date => '23'},
|
397
|
+
{ :date => '24'},
|
398
|
+
{ :date => '25'},
|
399
|
+
{ :date => '26'},
|
400
|
+
{ :date => '27',
|
401
|
+
:wpm => 37,
|
402
|
+
:errors => 3,
|
403
|
+
:accuracy => 92 },
|
404
|
+
]
|
405
|
+
|
406
|
+
[:wpm, :errors, :accuracy].each do |field|
|
407
|
+
g.data(field.to_s, data.collect {|d| d[field] })
|
408
|
+
end
|
409
|
+
|
410
|
+
labels = Hash.new
|
411
|
+
data.each_with_index do |d, i|
|
412
|
+
labels[i] = d[:date]
|
413
|
+
end
|
414
|
+
g.labels = labels
|
415
|
+
|
416
|
+
g.write('test/output/line_many_numbers.png')
|
417
|
+
end
|
418
|
+
|
419
|
+
|
323
420
|
|
324
421
|
protected
|
325
422
|
|
@@ -360,6 +457,35 @@ protected
|
|
360
457
|
end
|
361
458
|
return g
|
362
459
|
end
|
460
|
+
|
461
|
+
def setup_pos_neg(size=800)
|
462
|
+
g = Gruff::Line.new(size)
|
463
|
+
g.title = "Pos/Neg Line Graph Test"
|
464
|
+
g.labels = {
|
465
|
+
0 => '5/6',
|
466
|
+
1 => '5/15',
|
467
|
+
2 => '5/24',
|
468
|
+
3 => '5/30',
|
469
|
+
}
|
470
|
+
g.data(:apples, [-1, 0, 4, -4])
|
471
|
+
g.data(:peaches, [10, 8, 6, 3])
|
472
|
+
return g
|
473
|
+
end
|
474
|
+
|
475
|
+
def setup_all_neg(size=800)
|
476
|
+
g = Gruff::Line.new(size)
|
477
|
+
g.title = "All Neg Line Graph Test"
|
478
|
+
g.labels = {
|
479
|
+
0 => '5/6',
|
480
|
+
1 => '5/15',
|
481
|
+
2 => '5/24',
|
482
|
+
3 => '5/30',
|
483
|
+
}
|
484
|
+
g.data(:apples, [-1, -5, -20, -4])
|
485
|
+
g.data(:peaches, [-10, -8, -6, -3])
|
486
|
+
return g
|
487
|
+
end
|
488
|
+
|
363
489
|
|
364
490
|
end
|
365
491
|
|
data/test/pie_test.rb
CHANGED
@@ -10,14 +10,14 @@ class TestGruffPie < Test::Unit::TestCase
|
|
10
10
|
|
11
11
|
def setup
|
12
12
|
@datasets = [
|
13
|
-
[:
|
14
|
-
[:
|
15
|
-
[:
|
16
|
-
[:
|
17
|
-
[:
|
18
|
-
["
|
13
|
+
[:Darren, [25]],
|
14
|
+
[:Chris, [80]],
|
15
|
+
[:Egbert, [22]],
|
16
|
+
[:Adam, [95]],
|
17
|
+
[:Bill, [90]],
|
18
|
+
["Frank", [5]],
|
19
|
+
["Zero", [0]],
|
19
20
|
]
|
20
|
-
|
21
21
|
end
|
22
22
|
|
23
23
|
def test_pie_graph
|
@@ -42,44 +42,37 @@ class TestGruffPie < Test::Unit::TestCase
|
|
42
42
|
g.write("test/output/pie_keynote_small.png")
|
43
43
|
end
|
44
44
|
|
45
|
+
def test_pie_graph_nearly_equal
|
46
|
+
g = Gruff::Pie.new
|
47
|
+
g.title = "Pie Graph Nearly Equal"
|
48
|
+
|
49
|
+
g.data(:Blake, [41])
|
50
|
+
g.data(:Aaron, [42])
|
51
|
+
# g.data(:Grouch, [40])
|
52
|
+
# g.data(:Snuffleupagus, [43])
|
45
53
|
|
46
|
-
|
47
|
-
|
48
|
-
g = Gruff::Pie.new(400)
|
49
|
-
g.title = "Small Size Multi-Pie Graph Test"
|
50
|
-
g.labels = {
|
51
|
-
0 => '5/6',
|
52
|
-
2 => '5/15',
|
53
|
-
4 => '5/24',
|
54
|
-
6 => '5/30',
|
55
|
-
}
|
56
|
-
@datasets.each do |data|
|
57
|
-
g.data(data[0], data[1])
|
58
|
-
end
|
54
|
+
g.write("test/output/pie_nearly_equal.png")
|
55
|
+
end
|
59
56
|
|
60
|
-
|
61
|
-
g.
|
57
|
+
def test_pie_graph_equal
|
58
|
+
g = Gruff::Pie.new
|
59
|
+
g.title = "Pie Graph Equal"
|
60
|
+
|
61
|
+
g.data(:Bert, [41])
|
62
|
+
g.data(:Adam, [41])
|
63
|
+
|
64
|
+
g.write("test/output/pie_equal.png")
|
62
65
|
end
|
63
|
-
|
64
|
-
def test_pie_graph_tiny
|
65
|
-
g = Gruff::Pie.new(300)
|
66
|
-
g.title = "Pie Test 300px"
|
67
|
-
g.labels = {
|
68
|
-
0 => '5/6',
|
69
|
-
10 => '5/15',
|
70
|
-
20 => '5/24',
|
71
|
-
30 => '5/30',
|
72
|
-
40 => '6/4',
|
73
|
-
50 => '6/16'
|
74
|
-
}
|
75
|
-
%w{jimmy jane philip arthur julie bert}.each do |student_name|
|
76
|
-
g.data(student_name, (0..50).collect { |i| rand 100 })
|
77
|
-
end
|
78
66
|
|
79
|
-
|
80
|
-
g.
|
67
|
+
def test_pie_graph_zero
|
68
|
+
g = Gruff::Pie.new
|
69
|
+
g.title = "Pie Graph One Zero"
|
70
|
+
|
71
|
+
g.data(:Bert, [0])
|
72
|
+
g.data(:Adam, [1])
|
73
|
+
|
74
|
+
g.write("test/output/pie_zero.png")
|
81
75
|
end
|
82
|
-
=end
|
83
76
|
|
84
77
|
def test_wide
|
85
78
|
g = setup_basic_graph('800x400')
|
@@ -87,6 +80,19 @@ class TestGruffPie < Test::Unit::TestCase
|
|
87
80
|
g.write("test/output/pie_wide.png")
|
88
81
|
end
|
89
82
|
|
83
|
+
def test_label_size
|
84
|
+
g = setup_basic_graph()
|
85
|
+
g.title = "Pie With Small Legend"
|
86
|
+
g.legend_font_size = 10
|
87
|
+
g.write("test/output/pie_legend.png")
|
88
|
+
|
89
|
+
g = setup_basic_graph(400)
|
90
|
+
g.title = "Small Pie With Small Legend"
|
91
|
+
g.legend_font_size = 10
|
92
|
+
g.write("test/output/pie_legend_small.png")
|
93
|
+
end
|
94
|
+
|
95
|
+
|
90
96
|
protected
|
91
97
|
|
92
98
|
def setup_basic_graph(size=800)
|
data/test/stacked_bar_test.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.8.
|
2
|
+
rubygems_version: 0.8.10
|
3
3
|
specification_version: 1
|
4
4
|
name: gruff
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.0
|
7
|
-
date:
|
6
|
+
version: 0.1.0
|
7
|
+
date: 2006-02-03
|
8
8
|
summary: Beautiful graphs for one or multiple datasets.
|
9
9
|
require_paths:
|
10
|
-
- lib
|
10
|
+
- lib
|
11
11
|
email: boss@topfunky.com
|
12
12
|
homepage: http://www.topfunky.com
|
13
13
|
rubyforge_project: gruff
|
@@ -18,52 +18,46 @@ bindir: bin
|
|
18
18
|
has_rdoc: true
|
19
19
|
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
20
|
requirements:
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
-
|
22
|
+
- ">"
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 0.0.0
|
24
25
|
version:
|
25
26
|
platform: ruby
|
26
|
-
signing_key:
|
27
|
-
cert_chain:
|
28
27
|
authors:
|
29
|
-
- Geoffrey Grosenbach
|
28
|
+
- Geoffrey Grosenbach
|
30
29
|
files:
|
31
|
-
- rakefile
|
32
|
-
- README
|
33
|
-
- CHANGELOG
|
34
|
-
- MIT-LICENSE
|
35
|
-
- lib/gruff
|
36
|
-
- lib/gruff.rb
|
37
|
-
- lib/gruff/area.rb
|
38
|
-
- lib/gruff/bar.rb
|
39
|
-
- lib/gruff/
|
40
|
-
- lib/gruff/
|
41
|
-
- lib/gruff/
|
42
|
-
- lib/gruff/
|
43
|
-
- lib/gruff/
|
44
|
-
- lib/gruff/
|
45
|
-
-
|
46
|
-
- assets/
|
47
|
-
- assets/
|
48
|
-
-
|
49
|
-
- test/
|
50
|
-
- test/
|
51
|
-
- test/
|
52
|
-
- test/
|
53
|
-
- test/
|
54
|
-
- test/
|
55
|
-
- test/
|
30
|
+
- rakefile
|
31
|
+
- README
|
32
|
+
- CHANGELOG
|
33
|
+
- MIT-LICENSE
|
34
|
+
- lib/gruff
|
35
|
+
- lib/gruff.rb
|
36
|
+
- lib/gruff/area.rb
|
37
|
+
- lib/gruff/bar.rb
|
38
|
+
- lib/gruff/bar_conversion.rb
|
39
|
+
- lib/gruff/base.rb
|
40
|
+
- lib/gruff/line.rb
|
41
|
+
- lib/gruff/photo_bar.rb
|
42
|
+
- lib/gruff/pie.rb
|
43
|
+
- lib/gruff/side_stacked_bar.rb
|
44
|
+
- lib/gruff/stacked_bar.rb
|
45
|
+
- assets/bubble.png
|
46
|
+
- assets/pc306715.jpg
|
47
|
+
- assets/plastik
|
48
|
+
- test/area_test.rb
|
49
|
+
- test/bar_test.rb
|
50
|
+
- test/line_test.rb
|
51
|
+
- test/output
|
52
|
+
- test/photo_test.rb
|
53
|
+
- test/pie_test.rb
|
54
|
+
- test/sidestacked_bar_test.rb
|
55
|
+
- test/stacked_bar_test.rb
|
56
56
|
test_files: []
|
57
|
-
|
58
57
|
rdoc_options: []
|
59
|
-
|
60
58
|
extra_rdoc_files: []
|
61
|
-
|
62
59
|
executables: []
|
63
|
-
|
64
60
|
extensions: []
|
65
|
-
|
66
61
|
requirements:
|
67
|
-
- none
|
68
|
-
dependencies: []
|
69
|
-
|
62
|
+
- none
|
63
|
+
dependencies: []
|