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 CHANGED
@@ -2,10 +2,14 @@
2
2
 
3
3
  A library for making beautiful graphs.
4
4
 
5
- See samples at http://nubyonrails.topfunky.com/articles/2005/10/24/gruff-graphing-library-for-ruby
5
+ See samples at http://nubyonrails.com/pages/gruff
6
6
 
7
- See the test suite in test/line_test.rb for examples. (More documentation coming soon.)
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 alpha-quality software. It works well according to my tests, but the API may change and other features will be added. Backwards compatibility is not guaranteed until the 1.0 release.
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 current_color
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
- def draw
7
- super
8
-
9
- return unless @has_data
10
-
11
- # Setup spacing.
12
- #
13
- # Columns sit side-by-side.
14
- spacing_factor = 0.9
15
- @bar_width = @graph_width / (@column_count * @data.length).to_f
16
-
17
- @d = @d.stroke_opacity 0.0
18
-
19
- @norm_data.each_with_index do |data_row, row_index|
20
- @d = @d.fill current_color
21
-
22
- data_row[1].each_with_index do |data_point, point_index|
23
- # TODO Try this out
24
- ##data_point = 0 if data_point.nil?
25
-
26
- # Use incremented x and scaled y
27
- left_x = @graph_left + (@bar_width * (row_index + point_index + ((@data.length - 1) * point_index)))
28
- left_y = @graph_top + (@graph_height - data_point * @graph_height) + 1
29
- right_x = left_x + @bar_width * spacing_factor
30
- right_y = @graph_top + @graph_height - 1
31
-
32
- @d = @d.rectangle(left_x, left_y, right_x, right_y)
33
-
34
- # Calculate center based on bar_width and current row
35
- label_center = @graph_left + (@data.length * @bar_width * point_index) + (@data.length * @bar_width / 2.0)
36
- draw_label(label_center, point_index)
37
- end
38
-
39
- increment_color()
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.9'
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
- # dataset is an array where the first element is the name of the dataset
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
- # for a multi-valued graph.
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
- # data("Bart S.", [95, 45, 78, 89, 88, 76])
173
- def data(name, data_points=[])
174
- @data << [name, data_points]
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
- @maximum_value = larger_than_max?(data_point, index) ? max(data_point, index) : @maximum_value
183
- # @maximum_value = (data_point > @maximum_value) ? data_point : @maximum_value
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 = Array.new
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 = Array.new
245
- data_row[1].each do |data_point|
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/@maximum_value.to_f)
285
+ norm_data_points << ((data_point.to_f - min_val ) / @spread)
250
286
  end
251
287
  end
252
- @norm_data << [data_row[0], norm_data_points]
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
- increment = significant(@maximum_value.to_f / number_of_lines)
288
- inc_graph = @graph_height.to_f / (@maximum_value.to_f / increment)
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
- # Sort norm_data with avg largest values set first (for display)
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 = 20
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(20)
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 current_color
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 = 20
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
- 'No Data', @scale)
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
- # Round down to significant digits
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
- # TODO Remove and go back to normal. Call hide_dots or hide_lines instead
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
- 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
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 current_color
62
- @d = @d.fill current_color
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)
@@ -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 init_photo_bar_graphics() inside an overriden draw_legend()
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 = current_color.columns
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 = current_color.columns
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 = current_color.crop(0, 0, bar_image_width, bar_image_height)
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
- @norm_data.each do |data_row|
22
- @d = @d.stroke current_color
23
- @d = @d.fill 'transparent'
24
- @d.stroke_width(200.0)
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
- current_degrees = (data_row[1][0] / total_sum) * 360.0
27
- #@d = @d.pie_slice(center_x, center_y, radius,
28
- # current_percent * 100.0)
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
- prev_degrees += current_degrees
40
- increment_color()
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
- @norm_data.collect {|data_row| total_sum += data_row[1][0] }
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 current_color
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)
@@ -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 current_color
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.74 29.769 29.79 29.808 29.828 29.849 29.878).collect {|i| i.to_f} )
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
- [:Jimmy, [25]],
14
- [:Charles, [80]],
15
- [:Julie, [22]],
16
- [:Jane, [95]],
17
- [:Philip, [90]],
18
- ["Arthur", [5]],
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
- =begin
47
- def test_resize
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
- # Default theme
61
- g.write("test/output/pie_keynote_small.png")
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
- # Default theme
80
- g.write("test/output/pie_tiny.png")
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)
@@ -8,8 +8,6 @@ require 'gruff'
8
8
 
9
9
  class TestGruffStackedBar < Test::Unit::TestCase
10
10
 
11
- # TODO Delete old output files once when starting tests
12
-
13
11
  def setup
14
12
  @datasets = [
15
13
  [:Jimmy, [25, 36, 86, 39]],
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.8.11
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.9
7
- date: 2005-12-31 00:00:00 -08:00
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
- - !ruby/object:Gem::Version
23
- version: 0.0.0
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/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
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: []