gruff 0.0.9 → 0.1.0
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.
- 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: []
|