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 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: []