gruff 0.9.0 → 0.12.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +21 -4
  3. data/.rubocop_todo.yml +103 -49
  4. data/.travis.yml +3 -6
  5. data/CHANGELOG.md +30 -0
  6. data/README.md +4 -0
  7. data/gruff.gemspec +8 -3
  8. data/lib/gruff.rb +9 -3
  9. data/lib/gruff/accumulator_bar.rb +13 -5
  10. data/lib/gruff/area.rb +22 -5
  11. data/lib/gruff/bar.rb +38 -10
  12. data/lib/gruff/base.rb +325 -236
  13. data/lib/gruff/bezier.rb +18 -4
  14. data/lib/gruff/bullet.rb +22 -14
  15. data/lib/gruff/dot.rb +20 -33
  16. data/lib/gruff/helper/bar_conversion.rb +7 -7
  17. data/lib/gruff/helper/bar_value_label_mixin.rb +3 -0
  18. data/lib/gruff/helper/stacked_mixin.rb +1 -1
  19. data/lib/gruff/histogram.rb +59 -0
  20. data/lib/gruff/line.rb +33 -28
  21. data/lib/gruff/mini/bar.rb +10 -2
  22. data/lib/gruff/mini/legend.rb +9 -4
  23. data/lib/gruff/mini/pie.rb +9 -3
  24. data/lib/gruff/mini/side_bar.rb +18 -4
  25. data/lib/gruff/net.rb +41 -21
  26. data/lib/gruff/patch/rmagick.rb +22 -24
  27. data/lib/gruff/patch/string.rb +9 -4
  28. data/lib/gruff/photo_bar.rb +12 -16
  29. data/lib/gruff/pie.rb +24 -34
  30. data/lib/gruff/renderer/bezier.rb +4 -3
  31. data/lib/gruff/renderer/circle.rb +4 -3
  32. data/lib/gruff/renderer/dash_line.rb +4 -3
  33. data/lib/gruff/renderer/dot.rb +4 -3
  34. data/lib/gruff/renderer/ellipse.rb +4 -3
  35. data/lib/gruff/renderer/line.rb +14 -5
  36. data/lib/gruff/renderer/polygon.rb +5 -4
  37. data/lib/gruff/renderer/polyline.rb +4 -3
  38. data/lib/gruff/renderer/rectangle.rb +3 -2
  39. data/lib/gruff/renderer/renderer.rb +31 -38
  40. data/lib/gruff/renderer/text.rb +29 -7
  41. data/lib/gruff/scatter.rb +26 -24
  42. data/lib/gruff/scene.rb +0 -1
  43. data/lib/gruff/side_bar.rb +51 -20
  44. data/lib/gruff/side_stacked_bar.rb +42 -15
  45. data/lib/gruff/spider.rb +29 -17
  46. data/lib/gruff/stacked_area.rb +19 -8
  47. data/lib/gruff/stacked_bar.rb +34 -13
  48. data/lib/gruff/store/{base_data.rb → basic_data.rb} +9 -7
  49. data/lib/gruff/store/custom_data.rb +8 -6
  50. data/lib/gruff/store/store.rb +7 -6
  51. data/lib/gruff/store/xy_data.rb +10 -7
  52. data/lib/gruff/version.rb +1 -1
  53. metadata +50 -11
  54. data/Rakefile +0 -23
  55. data/docker/Dockerfile +0 -14
  56. data/docker/build.sh +0 -4
  57. data/docker/launch.sh +0 -4
data/lib/gruff/bezier.rb CHANGED
@@ -1,7 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'gruff/base'
4
-
3
+ #
4
+ # Gruff::Bezier is a special line graph that have
5
+ # the bezier curve.
6
+ #
7
+ # Here's how to set up a Gruff::Bezier.
8
+ #
9
+ # dataset = [
10
+ # +0.00, +0.09, +0.19, +0.29, +0.38, +0.47, +0.56, +0.64, +0.71, +0.78,
11
+ # +0.84, +0.89, +0.93, +0.96, +0.98, +0.99, +0.99, +0.99, +0.97, +0.94,
12
+ # +0.90, +0.86, +0.80, +0.74, +0.67, +0.59, +0.51, +0.42, +0.33, +0.23,
13
+ # +0.14, +0.04, -0.06, -0.16, -0.26, -0.36, -0.45, -0.53, -0.62, -0.69,
14
+ # -0.76, -0.82, -0.88, -0.92, -0.96, -0.98, -1.00, -1.00, -1.00, -0.99,
15
+ # -0.96, -0.93, -0.89, -0.84, -0.78, -0.71, -0.64, -0.56, -0.47, -0.38,
16
+ # ]
17
+ # g = Gruff::Bezier.new
18
+ # g.data 'sin', dataset
19
+ # g.write('bezier.png')
20
+ #
5
21
  class Gruff::Bezier < Gruff::Base
6
22
  def draw
7
23
  super
@@ -37,7 +53,5 @@ class Gruff::Bezier < Gruff::Base
37
53
  Gruff::Renderer::Bezier.new(color: data_row.color, width: stroke_width).render(poly_points)
38
54
  end
39
55
  end
40
-
41
- Gruff::Renderer.finish
42
56
  end
43
57
  end
data/lib/gruff/bullet.rb CHANGED
@@ -1,15 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'gruff/base'
4
- require 'gruff/themes'
5
-
3
+ #
4
+ # A bullet graph is a variation of a bar graph.
6
5
  # http://en.wikipedia.org/wiki/Bullet_graph
6
+ #
7
+ # Here's how to set up a Gruff::Bullet.
8
+ #
9
+ # g = Gruff::Bullet.new
10
+ # g.title = 'Monthly Revenue'
11
+ # g.data 75, 100, { target: 80, low: 50, high: 90 }
12
+ # g.write('bullet.png')
13
+ #
7
14
  class Gruff::Bullet < Gruff::Base
8
15
  def initialize(target_width = '400x40')
16
+ super
17
+
9
18
  if target_width.is_a?(String)
10
- geometric_width, geometric_height = target_width.split('x')
11
- @columns = geometric_width.to_f
12
- @rows = geometric_height.to_f
19
+ @columns, @rows = target_width.split('x').map(&:to_f)
13
20
  else
14
21
  @columns = target_width.to_f
15
22
  @rows = target_width.to_f / 5.0
@@ -17,12 +24,15 @@ class Gruff::Bullet < Gruff::Base
17
24
  @columns.freeze
18
25
  @rows.freeze
19
26
 
20
- initialize_ivars
21
-
22
- reset_themes
23
27
  self.theme = Gruff::Themes::GREYSCALE
28
+ end
29
+
30
+ def initialize_ivars
31
+ super
32
+
24
33
  @title_font_size = 20
25
34
  end
35
+ private :initialize_ivars
26
36
 
27
37
  def data(value, maximum_value, options = {})
28
38
  @value = value.to_f
@@ -45,7 +55,7 @@ class Gruff::Bullet < Gruff::Base
45
55
  margin = 30.0
46
56
  thickness = @raw_rows / 6.0
47
57
  right_margin = margin
48
- graph_left = (@title && (title_width * 1.3)) || margin
58
+ graph_left = [title_width * 1.3, margin].max
49
59
  graph_width = @raw_columns - graph_left - right_margin
50
60
  graph_height = thickness * 3.0
51
61
 
@@ -73,18 +83,16 @@ class Gruff::Bullet < Gruff::Base
73
83
  # Value
74
84
  rect_renderer = Gruff::Renderer::Rectangle.new(color: @font_color)
75
85
  rect_renderer.render(graph_left, thickness, graph_left + graph_width * (@value / maximum_value), thickness * 2)
76
-
77
- Gruff::Renderer.finish
78
86
  end
79
87
 
80
88
  private
81
89
 
82
90
  def draw_title
83
- return unless @title
91
+ return if hide_title?
84
92
 
85
93
  font_height = calculate_caps_height(scale_fontsize(@title_font_size))
86
94
 
87
95
  text_renderer = Gruff::Renderer::Text.new(@title, font: @font, size: @title_font_size, color: @font_color)
88
- text_renderer.render(1.0, 1.0, font_height / 2, font_height / 2, Magick::NorthWestGravity)
96
+ text_renderer.add_to_render_queue(1.0, 1.0, font_height / 2, font_height / 2, Magick::NorthWestGravity)
89
97
  end
90
98
  end
data/lib/gruff/dot.rb CHANGED
@@ -1,9 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'gruff/base'
4
-
5
- # Graph with dots and labels along a vertical access
3
+ #
4
+ # Graph with dots and labels along a vertical access.
6
5
  # see: 'Creating More Effective Graphs' by Robbins
6
+ #
7
+ # Here's how to set up a Gruff::Dot.
8
+ #
9
+ # g = Gruff::Dot.new
10
+ # g.title = 'Dot Graph'
11
+ # g.data :Art, [0, 5, 8, 15]
12
+ # g.data :Philosophy, [10, 3, 2, 8]
13
+ # g.data :Science, [2, 15, 8, 11]
14
+ # g.write('dot.png')
15
+ #
7
16
  class Gruff::Dot < Gruff::Base
8
17
  def draw
9
18
  @has_left_labels = true
@@ -33,8 +42,6 @@ class Gruff::Dot < Gruff::Base
33
42
  draw_label(y_pos, point_index)
34
43
  end
35
44
  end
36
-
37
- Gruff::Renderer.finish
38
45
  end
39
46
 
40
47
  protected
@@ -43,37 +50,17 @@ protected
43
50
  def draw_line_markers
44
51
  return if @hide_line_markers
45
52
 
46
- # Draw horizontal line markers and annotate with numbers
47
- if @y_axis_increment
48
- increment = @y_axis_increment
49
- number_of_lines = (@spread / @y_axis_increment).to_i
50
- else
51
- # Try to use a number of horizontal lines that will come out even.
52
- #
53
- # TODO Do the same for larger numbers...100, 75, 50, 25
54
- if @marker_count.nil?
55
- (3..7).each do |lines|
56
- if @spread % lines == 0.0
57
- @marker_count = lines
58
- break
59
- end
60
- end
61
- @marker_count ||= 5
62
- end
63
- # TODO: Round maximum marker value to a round number like 100, 0.1, 0.5, etc.
64
- increment = (@spread > 0 && @marker_count > 0) ? significant(@spread / @marker_count) : 1
65
- number_of_lines = @marker_count
66
- end
67
-
68
- (0..number_of_lines).each do |index|
69
- marker_label = minimum_value + index * increment
53
+ (0..marker_count).each do |index|
54
+ marker_label = minimum_value + index * @increment
70
55
  x = @graph_left + (marker_label - minimum_value) * @graph_width / @spread
71
- Gruff::Renderer::Line.new(color: @marker_color).render(x, @graph_bottom, x, @graph_bottom + 0.5 * LABEL_MARGIN)
56
+
57
+ line_renderer = Gruff::Renderer::Line.new(color: @marker_color, shadow_color: @marker_shadow_color)
58
+ line_renderer.render(x, @graph_bottom, x, @graph_bottom + 5)
72
59
 
73
60
  unless @hide_line_numbers
74
- label = label(marker_label, increment)
61
+ label = label(marker_label, @increment)
75
62
  text_renderer = Gruff::Renderer::Text.new(label, font: @font, size: @marker_font_size, color: @font_color)
76
- text_renderer.render(0, 0, x, @graph_bottom + (LABEL_MARGIN * 2.0), Magick::CenterGravity)
63
+ text_renderer.add_to_render_queue(0, 0, x, @graph_bottom + (LABEL_MARGIN * 1.5), Magick::CenterGravity)
77
64
  end
78
65
  end
79
66
  end
@@ -84,7 +71,7 @@ protected
84
71
  def draw_label(y_offset, index)
85
72
  draw_unique_label(index) do
86
73
  text_renderer = Gruff::Renderer::Text.new(@labels[index], font: @font, size: @marker_font_size, color: @font_color)
87
- text_renderer.render(1, 1, -@graph_left + LABEL_MARGIN * 2.0, y_offset, Magick::EastGravity)
74
+ text_renderer.add_to_render_queue(@graph_left - LABEL_MARGIN, 1.0, 0.0, y_offset, Magick::EastGravity)
88
75
  end
89
76
  end
90
77
  end
@@ -3,7 +3,7 @@
3
3
  ##
4
4
  # Original Author: David Stokar
5
5
  #
6
- # This class perfoms the y coordinats conversion for the bar class.
6
+ # This class performs the y coordinates conversion for the bar class.
7
7
  #
8
8
  # There are three cases:
9
9
  #
@@ -24,16 +24,16 @@ class Gruff::BarConversion
24
24
  result = []
25
25
 
26
26
  case @mode
27
- when 1 then # Case one
28
- # minimum value >= 0 ( only positive values )
27
+ when 1
28
+ # minimum value >= 0 ( only positive values )
29
29
  result[0] = @graph_top + @graph_height * (1 - data_point) + 1
30
30
  result[1] = @graph_top + @graph_height - 1
31
- when 2 then # Case two
32
- # only negative values
31
+ when 2
32
+ # only negative values
33
33
  result[0] = @graph_top + 1
34
34
  result[1] = @graph_top + @graph_height * (1 - data_point) - 1
35
- when 3 then # Case three
36
- # positive and negative values
35
+ when 3
36
+ # positive and negative values
37
37
  val = data_point - @minimum_value / @spread
38
38
  result[0] = @graph_top + @graph_height * (1 - (val - @zero)) + 1
39
39
  result[1] = @graph_top + @graph_height * (1 - @zero) - 1
@@ -2,6 +2,9 @@
2
2
 
3
3
  # @private
4
4
  module Gruff::Base::BarValueLabelMixin
5
+ using String::GruffCommify
6
+
7
+ # @private
5
8
  class BarValueLabel
6
9
  attr_accessor :coordinates, :values
7
10
 
@@ -5,7 +5,7 @@ module Gruff::Base::StackedMixin
5
5
  # Used by StackedBar and child classes.
6
6
  #
7
7
  # tsal: moved from Base 03 FEB 2007
8
- def get_maximum_by_stack
8
+ def calculate_maximum_by_stack
9
9
  # Get sum of each stack
10
10
  max_hash = {}
11
11
  store.data.each do |data_set|
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'histogram'
4
+
5
+ #
6
+ # Here's how to set up a Gruff::Histogram.
7
+ #
8
+ # g = Gruff::Histogram.new
9
+ # g.title = 'Histogram Graph'
10
+ # g.minimum_bin = 10
11
+ # g.bin_width = 20
12
+ # g.data :A, [10, 10, 20, 30, 40, 40, 40, 40, 40, 40, 50, 10, 10, 10]
13
+ # g.data :B, [100, 100, 100, 100, 90, 90, 80, 30, 30, 30, 30, 30]
14
+ # g.write('histogram.png')
15
+ #
16
+ class Gruff::Histogram < Gruff::Bar
17
+ # Specifies interpolation between the min and max of the set. Default is +10+.
18
+ attr_writer :bin_width
19
+
20
+ # Specifies minimum value for bin.
21
+ attr_writer :minimum_bin
22
+
23
+ # Specifies maximum value for bin.
24
+ attr_writer :maximum_bin
25
+
26
+ def initialize(*)
27
+ super
28
+ @data = []
29
+ end
30
+
31
+ def initialize_ivars
32
+ super
33
+ @bin_width = 10
34
+ @minimum_bin = nil
35
+ @maximum_bin = nil
36
+ end
37
+ private :initialize_ivars
38
+
39
+ def data(name, data_points = [], color = nil)
40
+ @data << [name, data_points, color]
41
+ end
42
+
43
+ def draw
44
+ @data.each do |(name, data_points, color)|
45
+ bins, freqs = HistogramArray.new(data_points).histogram(bin_width: @bin_width, min: @minimum_bin, max: @maximum_bin)
46
+ bins.each_with_index do |bin, index|
47
+ @labels[index] = bin
48
+ end
49
+ store.add(name, freqs, color)
50
+ end
51
+
52
+ super
53
+ end
54
+
55
+ # @private
56
+ class HistogramArray < Array
57
+ include ::Histogram
58
+ end
59
+ end
data/lib/gruff/line.rb CHANGED
@@ -1,41 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'gruff/base'
4
-
5
- # Here's how to make a Line graph:
3
+ #
4
+ # Here's how to make a Gruff::Line.
6
5
  #
7
6
  # g = Gruff::Line.new
8
7
  # g.title = "A Line Graph"
9
8
  # g.data 'Fries', [20, 23, 19, 8]
10
9
  # g.data 'Hamburgers', [50, 19, 99, 29]
11
- # g.write("test/output/line.png")
10
+ # g.write("line.png")
12
11
  #
13
12
  # There are also other options described below, such as {#baseline_value}, {#baseline_color},
14
- # {#hide_dots}, and {#hide_lines}.
13
+ # {#hide_dots=}, and {#hide_lines=}.
14
+ #
15
15
  class Gruff::Line < Gruff::Base
16
16
  # Allow for reference lines ( which are like baseline ... just allowing for more & on both axes ).
17
17
  attr_accessor :reference_lines
18
- attr_accessor :reference_line_default_color
19
- attr_accessor :reference_line_default_width
18
+ attr_writer :reference_line_default_color
19
+ attr_writer :reference_line_default_width
20
20
 
21
21
  # Allow for vertical marker lines.
22
- attr_accessor :show_vertical_markers
22
+ attr_writer :show_vertical_markers
23
23
 
24
24
  # Dimensions of lines and dots; calculated based on dataset size if left unspecified.
25
- attr_accessor :line_width
26
- attr_accessor :dot_radius
25
+ attr_writer :line_width
26
+ attr_writer :dot_radius
27
27
 
28
28
  # default is +'circle'+, other options include square.
29
- attr_accessor :dot_style
29
+ attr_writer :dot_style
30
30
 
31
- # Hide parts of the graph to fit more datapoints, or for a different appearance.
32
- attr_accessor :hide_dots, :hide_lines
31
+ # Hide parts of the graph to fit more data points, or for a different appearance.
32
+ attr_writer :hide_dots, :hide_lines
33
33
 
34
34
  # accessors for support of xy data.
35
- attr_accessor :minimum_x_value
35
+ attr_writer :minimum_x_value
36
36
 
37
37
  # accessors for support of xy data.
38
- attr_accessor :maximum_x_value
38
+ attr_writer :maximum_x_value
39
39
 
40
40
  # Get the value if somebody has defined it.
41
41
  def baseline_value
@@ -67,7 +67,7 @@ class Gruff::Line < Gruff::Base
67
67
  # g = Gruff::Line.new(400, false) # 400px wide, no lines (for backwards compatibility)
68
68
  # g = Gruff::Line.new(false) # Defaults to 800px wide, no lines (for backwards compatibility)
69
69
  #
70
- # The preferred way is to call {#hide_dots} or {#hide_lines} instead.
70
+ # The preferred way is to call {#hide_dots=} or {#hide_lines=} instead.
71
71
  def initialize(*args)
72
72
  raise ArgumentError, 'Wrong number of arguments' if args.length > 2
73
73
 
@@ -76,7 +76,15 @@ class Gruff::Line < Gruff::Base
76
76
  else
77
77
  super args.shift
78
78
  end
79
+ end
79
80
 
81
+ def initialize_store
82
+ @store = Gruff::Store.new(Gruff::Store::XYData)
83
+ end
84
+ private :initialize_store
85
+
86
+ def initialize_ivars
87
+ super
80
88
  @reference_lines = {}
81
89
  @reference_line_default_color = 'red'
82
90
  @reference_line_default_width = 5
@@ -85,12 +93,13 @@ class Gruff::Line < Gruff::Base
85
93
  @maximum_x_value = nil
86
94
  @minimum_x_value = nil
87
95
 
96
+ @line_width = nil
97
+ @dot_radius = nil
88
98
  @dot_style = 'circle'
89
99
 
90
100
  @show_vertical_markers = false
91
-
92
- @store = Gruff::Store.new(Gruff::Store::XYData)
93
101
  end
102
+ private :initialize_ivars
94
103
 
95
104
  # This method allows one to plot a dataset with both X and Y data.
96
105
  #
@@ -123,7 +132,7 @@ class Gruff::Line < Gruff::Base
123
132
  # g.data("Capples", [1, 1, 2, 2, 3, 3])
124
133
  #
125
134
  # # labels will be drawn at the x locations of the keys passed in.
126
- # In this example the lables are drawn at x positions 2, 4, and 6:
135
+ # In this example the labels are drawn at x positions 2, 4, and 6:
127
136
  # g.labels = {0 => '2003', 2 => '2004', 4 => '2005', 6 => '2006'}
128
137
  # # The 0 => '2003' label will be ignored since it is outside the chart range.
129
138
  def dataxy(name, x_data_points = [], y_data_points = [], color = nil)
@@ -144,11 +153,9 @@ class Gruff::Line < Gruff::Base
144
153
  end
145
154
 
146
155
  def draw_reference_line(reference_line, left, right, top, bottom)
147
- config = {
148
- color: reference_line[:color] || @reference_line_default_color,
149
- width: reference_line[:width] || @reference_line_default_width
150
- }
151
- Gruff::Renderer::DashLine.new(config).render(left, top, right, bottom)
156
+ color = reference_line[:color] || @reference_line_default_color
157
+ width = reference_line[:width] || @reference_line_default_width
158
+ Gruff::Renderer::DashLine.new(color: color, width: width).render(left, top, right, bottom)
152
159
  end
153
160
 
154
161
  def draw_horizontal_reference_line(reference_line)
@@ -210,8 +217,8 @@ class Gruff::Line < Gruff::Base
210
217
  new_y = @graph_top + (@graph_height - y_data * @graph_height)
211
218
 
212
219
  # Reset each time to avoid thin-line errors
213
- stroke_width = line_width || clip_value_if_greater_than(@columns / (store.norm_data.first.y_points.size * 4), 5.0)
214
- circle_radius = dot_radius || clip_value_if_greater_than(@columns / (store.norm_data.first.y_points.size * 2.5), 5.0)
220
+ stroke_width = @line_width || clip_value_if_greater_than(@columns / (store.norm_data.first.y_points.size * 4), 5.0)
221
+ circle_radius = @dot_radius || clip_value_if_greater_than(@columns / (store.norm_data.first.y_points.size * 2.5), 5.0)
215
222
 
216
223
  if !@hide_lines && prev_x && prev_y
217
224
  Gruff::Renderer::Line.new(color: data_row.color, width: stroke_width)
@@ -226,8 +233,6 @@ class Gruff::Line < Gruff::Base
226
233
  prev_y = new_y
227
234
  end
228
235
  end
229
-
230
- Gruff::Renderer.finish
231
236
  end
232
237
 
233
238
  private