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
@@ -1,52 +1,81 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'gruff/side_bar'
4
- require 'gruff/helper/stacked_mixin'
5
-
3
+ #
6
4
  # New gruff graph type added to enable sideways stacking bar charts
7
5
  # (basically looks like a x/y flip of a standard stacking bar chart)
8
6
  #
9
- # alun.eyre@googlemail.com
7
+ # Here's how to set up a Gruff::SideStackedBar.
8
+ #
9
+ # g = Gruff::SideStackedBar.new
10
+ # g.title = 'SideStackedBar Graph'
11
+ # g.labels = {
12
+ # 0 => '5/6',
13
+ # 1 => '5/15',
14
+ # 2 => '5/24',
15
+ # 3 => '5/30',
16
+ # }
17
+ # g.data :Art, [0, 5, 8, 15]
18
+ # g.data :Philosophy, [10, 3, 2, 8]
19
+ # g.data :Science, [2, 15, 8, 11]
20
+ # g.write('side_stacked_bar.png')
21
+ #
10
22
  class Gruff::SideStackedBar < Gruff::SideBar
11
23
  include StackedMixin
12
24
  include BarValueLabelMixin
13
25
 
14
26
  # Spacing factor applied between bars.
15
- attr_accessor :bar_spacing
27
+ attr_writer :bar_spacing
16
28
 
17
29
  # Number of pixels between bar segments.
18
- attr_accessor :segment_spacing
30
+ attr_writer :segment_spacing
19
31
 
20
32
  # Set the number output format for labels using sprintf.
21
33
  # Default is +"%.2f"+.
22
- attr_accessor :label_formatting
34
+ attr_writer :label_formatting
23
35
 
24
36
  # Output the values for the bars on a bar graph.
25
37
  # Default is +false+.
26
- attr_accessor :show_labels_for_bar_values
38
+ attr_writer :show_labels_for_bar_values
39
+
40
+ # Prevent drawing of column labels left of a side stacked bar graph. Default is +false+.
41
+ attr_writer :hide_labels
27
42
 
28
43
  def initialize_ivars
29
44
  super
45
+ @bar_spacing = 0.9
46
+ @segment_spacing = 2.0
30
47
  @label_formatting = nil
31
48
  @show_labels_for_bar_values = false
49
+ @hide_labels = false
32
50
  end
33
51
  private :initialize_ivars
34
52
 
35
53
  def draw
36
54
  @has_left_labels = true
37
- get_maximum_by_stack
55
+ calculate_maximum_by_stack
38
56
  super
39
57
  end
40
58
 
59
+ protected
60
+
61
+ def hide_labels?
62
+ @hide_labels
63
+ end
64
+
65
+ def hide_left_label_area?
66
+ hide_labels?
67
+ end
68
+
69
+ def hide_bottom_label_area?
70
+ @hide_line_markers
71
+ end
72
+
41
73
  private
42
74
 
43
75
  def draw_bars
44
76
  # Setup spacing.
45
77
  #
46
78
  # Columns sit stacked.
47
- @bar_spacing ||= 0.9
48
- @segment_spacing ||= 2.0
49
-
50
79
  bar_width = @graph_height / column_count.to_f
51
80
  height = Array.new(column_count, 0)
52
81
  length = Array.new(column_count, @graph_left)
@@ -55,7 +84,7 @@ private
55
84
 
56
85
  store.norm_data.each_with_index do |data_row, row_index|
57
86
  data_row.points.each_with_index do |data_point, point_index|
58
- ## using the original calcs from the stacked bar chart to get the difference between
87
+ ## using the original calculations from the stacked bar chart to get the difference between
59
88
  ## part of the bart chart we wish to stack.
60
89
  temp1 = @graph_left + (@graph_width -
61
90
  data_point * @graph_width -
@@ -93,7 +122,5 @@ private
93
122
  draw_value_label(x, y, text, true)
94
123
  end
95
124
  end
96
-
97
- Gruff::Renderer.finish
98
125
  end
99
126
  end
data/lib/gruff/spider.rb CHANGED
@@ -1,16 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'gruff/base'
4
-
5
3
  # Experimental!!! See also the Net graph.
6
4
  #
7
- # Submitted by Kevin Clark http://glu.ttono.us/
5
+ # Here's how to set up a Gruff::Spider.
6
+ #
7
+ # g = Gruff::Spider.new(30)
8
+ # g.title = "Spider Graph"
9
+ # g.data :Strength, [10]
10
+ # g.data :Dexterity, [16]
11
+ # g.data :Constitution, [12]
12
+ # g.data :Intelligence, [12]
13
+ # g.data :Wisdom, [10]
14
+ # g.data 'Charisma', [16]
15
+ # g.write("spider.png")
16
+ #
8
17
  class Gruff::Spider < Gruff::Base
9
18
  # Hide all text.
10
- attr_reader :hide_text
11
- attr_accessor :hide_axes
12
- attr_reader :transparent_background
13
- attr_accessor :rotation
19
+ attr_writer :hide_axes
20
+ attr_writer :rotation
14
21
 
15
22
  def transparent_background=(value)
16
23
  Gruff::Renderer.setup_transparent_background(@columns, @rows) if value
@@ -23,9 +30,16 @@ class Gruff::Spider < Gruff::Base
23
30
  def initialize(max_value, target_width = 800)
24
31
  super(target_width)
25
32
  @max_value = max_value
33
+ end
34
+
35
+ def initialize_ivars
36
+ super
26
37
  @hide_legend = true
38
+ @hide_axes = false
39
+ @hide_text = false
27
40
  @rotation = 0
28
41
  end
42
+ private :initialize_ivars
29
43
 
30
44
  def draw
31
45
  @hide_line_markers = true
@@ -44,12 +58,10 @@ class Gruff::Spider < Gruff::Base
44
58
  additive_angle = (2 * Math::PI) / store.length
45
59
 
46
60
  # Draw axes
47
- draw_axes(center_x, center_y, radius, additive_angle) unless hide_axes
61
+ draw_axes(center_x, center_y, radius, additive_angle) unless @hide_axes
48
62
 
49
63
  # Draw polygon
50
64
  draw_polygon(center_x, center_y, additive_angle)
51
-
52
- Gruff::Renderer.finish
53
65
  end
54
66
 
55
67
  private
@@ -66,14 +78,14 @@ private
66
78
  y = y_offset + ((radius + r_offset) * Math.sin(angle))
67
79
 
68
80
  # Draw label
69
- text_renderer = Gruff::Renderer::Text.new(amount, font: @font, size: legend_font_size, color: @marker_color, weight: Magick::BoldWeight)
70
- text_renderer.render(0, 0, x, y, Magick::CenterGravity)
81
+ text_renderer = Gruff::Renderer::Text.new(amount, font: @font, size: @legend_font_size, color: @marker_color, weight: Magick::BoldWeight)
82
+ text_renderer.add_to_render_queue(0, 0, x, y, Magick::CenterGravity)
71
83
  end
72
84
 
73
85
  def draw_axes(center_x, center_y, radius, additive_angle, line_color = nil)
74
- return if hide_axes
86
+ return if @hide_axes
75
87
 
76
- current_angle = rotation * Math::PI / 180.0
88
+ current_angle = @rotation * Math::PI / 180.0
77
89
 
78
90
  store.data.each do |data_row|
79
91
  x_offset = radius * Math.cos(current_angle)
@@ -82,7 +94,7 @@ private
82
94
  Gruff::Renderer::Line.new(color: line_color || data_row.color, width: 5.0)
83
95
  .render(center_x, center_y, center_x + x_offset, center_y + y_offset)
84
96
 
85
- draw_label(center_x, center_y, current_angle, radius, data_row.label.to_s) unless hide_text
97
+ draw_label(center_x, center_y, current_angle, radius, data_row.label.to_s) unless @hide_text
86
98
 
87
99
  current_angle += additive_angle
88
100
  end
@@ -90,7 +102,7 @@ private
90
102
 
91
103
  def draw_polygon(center_x, center_y, additive_angle, color = nil)
92
104
  points = []
93
- current_angle = rotation * Math::PI / 180.0
105
+ current_angle = @rotation * Math::PI / 180.0
94
106
 
95
107
  store.data.each do |data_row|
96
108
  points << center_x + normalize_points(data_row.points.first) * Math.cos(current_angle)
@@ -102,6 +114,6 @@ private
102
114
  end
103
115
 
104
116
  def sums_for_spider
105
- store.data.reduce(0.0) { |sum, data_row| sum + data_row.points.first }
117
+ store.data.sum { |data_row| data_row.points.first }
106
118
  end
107
119
  end
@@ -1,14 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'gruff/base'
4
- require 'gruff/helper/stacked_mixin'
5
-
3
+ #
4
+ # Here's how to set up a Gruff::StackedArea.
5
+ #
6
+ # g = Gruff::StackedArea.new
7
+ # g.title = 'StackedArea Graph'
8
+ # g.data :Jimmy, [25, 36, 86, 39, 25, 31, 79, 88]
9
+ # g.data :Charles, [80, 54, 67, 54, 68, 70, 90, 95]
10
+ # g.data :Julie, [22, 29, 35, 38, 36, 40, 46, 57]
11
+ # g.write('stacked_area.png')
12
+ #
6
13
  class Gruff::StackedArea < Gruff::Base
7
14
  include StackedMixin
8
- attr_accessor :last_series_goes_on_bottom
15
+ attr_writer :last_series_goes_on_bottom
16
+
17
+ def initialize_ivars
18
+ super
19
+ @last_series_goes_on_bottom = false
20
+ end
21
+ private :initialize_ivars
9
22
 
10
23
  def draw
11
- get_maximum_by_stack
24
+ calculate_maximum_by_stack
12
25
  super
13
26
 
14
27
  return unless data_given?
@@ -18,7 +31,7 @@ class Gruff::StackedArea < Gruff::Base
18
31
  height = Array.new(column_count, 0)
19
32
 
20
33
  data_points = nil
21
- iterator = last_series_goes_on_bottom ? :reverse_each : :each
34
+ iterator = @last_series_goes_on_bottom ? :reverse_each : :each
22
35
  store.norm_data.public_send(iterator) do |data_row|
23
36
  prev_data_points = data_points
24
37
  data_points = []
@@ -53,7 +66,5 @@ class Gruff::StackedArea < Gruff::Base
53
66
 
54
67
  Gruff::Renderer::Polygon.new(color: data_row.color).render(poly_points)
55
68
  end
56
-
57
- Gruff::Renderer.finish
58
69
  end
59
70
  end
@@ -1,46 +1,55 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'gruff/base'
4
- require 'gruff/helper/stacked_mixin'
5
- require 'gruff/helper/bar_value_label_mixin'
6
-
3
+ #
4
+ # Here's how to set up a Gruff::StackedBar.
5
+ #
6
+ # g = Gruff::StackedBar.new
7
+ # g.title = 'StackedBar Graph'
8
+ # g.data :Art, [0, 5, 8, 15]
9
+ # g.data :Philosophy, [10, 3, 2, 8]
10
+ # g.data :Science, [2, 15, 8, 11]
11
+ # g.write('stacked_bar.png')
12
+ #
7
13
  class Gruff::StackedBar < Gruff::Base
8
14
  include StackedMixin
9
15
  include BarValueLabelMixin
10
16
 
11
17
  # Spacing factor applied between bars.
12
- attr_accessor :bar_spacing
18
+ attr_writer :bar_spacing
13
19
 
14
20
  # Number of pixels between bar segments.
15
- attr_accessor :segment_spacing
21
+ attr_writer :segment_spacing
16
22
 
17
23
  # Set the number output format for labels using sprintf.
18
24
  # Default is +"%.2f"+.
19
- attr_accessor :label_formatting
25
+ attr_writer :label_formatting
20
26
 
21
27
  # Output the values for the bars on a bar graph.
22
28
  # Default is +false+.
23
- attr_accessor :show_labels_for_bar_values
29
+ attr_writer :show_labels_for_bar_values
30
+
31
+ # Prevent drawing of column labels below a stacked bar graph. Default is +false+.
32
+ attr_writer :hide_labels
24
33
 
25
34
  def initialize_ivars
26
35
  super
36
+ @bar_spacing = 0.9
37
+ @segment_spacing = 2
27
38
  @label_formatting = nil
28
39
  @show_labels_for_bar_values = false
40
+ @hide_labels = false
29
41
  end
30
42
  private :initialize_ivars
31
43
 
32
44
  # Draws a bar graph, but multiple sets are stacked on top of each other.
33
45
  def draw
34
- get_maximum_by_stack
46
+ calculate_maximum_by_stack
35
47
  super
36
48
  return unless data_given?
37
49
 
38
50
  # Setup spacing.
39
51
  #
40
52
  # Columns sit stacked.
41
- @bar_spacing ||= 0.9
42
- @segment_spacing ||= 2
43
-
44
53
  bar_width = @graph_width / column_count.to_f
45
54
  padding = (bar_width * (1 - @bar_spacing)) / 2
46
55
 
@@ -79,7 +88,19 @@ class Gruff::StackedBar < Gruff::Base
79
88
  draw_value_label(x, y, text, true)
80
89
  end
81
90
  end
91
+ end
92
+
93
+ protected
94
+
95
+ def hide_labels?
96
+ @hide_labels
97
+ end
98
+
99
+ def hide_left_label_area?
100
+ @hide_line_markers
101
+ end
82
102
 
83
- Gruff::Renderer.finish
103
+ def hide_bottom_label_area?
104
+ hide_labels?
84
105
  end
85
106
  end
@@ -1,13 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gruff
4
- # @private
5
4
  class Store
6
- class BaseData < Struct.new(:label, :points, :color)
5
+ # @private
6
+ class BasicData < Struct.new(:label, :points, :color)
7
7
  def initialize(label, points, color)
8
- self.label = label
9
- self.points = Array(points)
10
- self.color = color
8
+ super(label.to_s, Array(points), color)
9
+ end
10
+
11
+ def empty?
12
+ points.empty?
11
13
  end
12
14
 
13
15
  def columns
@@ -22,9 +24,9 @@ module Gruff
22
24
  points.compact.max
23
25
  end
24
26
 
25
- def normalize(args = {})
27
+ def normalize(minimum:, spread:)
26
28
  norm_points = points.map do |point|
27
- point.nil? ? nil : (point.to_f - args[:minimum].to_f) / args[:spread]
29
+ point.nil? ? nil : (point.to_f - minimum.to_f) / spread
28
30
  end
29
31
 
30
32
  self.class.new(label, norm_points, color)
@@ -2,12 +2,14 @@
2
2
 
3
3
  module Gruff
4
4
  class Store
5
+ # @private
5
6
  class CustomData < Struct.new(:label, :points, :color, :custom)
6
7
  def initialize(label, points, color, custom = nil)
7
- self.label = label
8
- self.points = Array(points)
9
- self.color = color
10
- self.custom = custom
8
+ super(label.to_s, Array(points), color, custom)
9
+ end
10
+
11
+ def empty?
12
+ points.empty?
11
13
  end
12
14
 
13
15
  def columns
@@ -22,9 +24,9 @@ module Gruff
22
24
  points.compact.max
23
25
  end
24
26
 
25
- def normalize(args = {})
27
+ def normalize(minimum:, spread:)
26
28
  norm_points = points.map do |point|
27
- point.nil? ? nil : (point.to_f - args[:minimum].to_f) / args[:spread]
29
+ point.nil? ? nil : (point.to_f - minimum.to_f) / spread
28
30
  end
29
31
 
30
32
  self.class.new(label, norm_points, color, custom)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gruff
4
+ # @private
4
5
  class Store
5
6
  attr_reader :data, :norm_data
6
7
 
@@ -15,10 +16,10 @@ module Gruff
15
16
  @data << @data_class.new(*args)
16
17
  end
17
18
 
18
- def normalize(args = {})
19
+ def normalize(**keywords)
19
20
  unless @normalized
20
21
  @data.each do |data_row|
21
- @norm_data << data_row.normalize(args)
22
+ @norm_data << data_row.normalize(**keywords)
22
23
  end
23
24
 
24
25
  @normalized = true
@@ -26,7 +27,7 @@ module Gruff
26
27
  end
27
28
 
28
29
  def empty?
29
- @data.empty?
30
+ @data.all?(&:empty?)
30
31
  end
31
32
 
32
33
  def length
@@ -56,18 +57,18 @@ module Gruff
56
57
  end
57
58
 
58
59
  def sort_data!
59
- @data = @data.sort_by { |a| -a.points.reduce(0) { |acc, elem| acc + elem.to_f } }
60
+ @data = @data.sort_by { |a| -a.points.sum(&:to_f) }
60
61
  end
61
62
 
62
63
  def sort_norm_data!
63
- @norm_data = @norm_data.sort_by { |a| -a.points.reduce(0) { |acc, elem| acc + elem.to_f } }
64
+ @norm_data = @norm_data.sort_by { |a| -a.points.sum(&:to_f) }
64
65
  end
65
66
 
66
67
  def reverse!
67
68
  @data.reverse!
68
69
  end
69
70
 
70
- def set_colors!(colors)
71
+ def change_colors(colors)
71
72
  index = 0
72
73
  @data.each do |data_row|
73
74
  data_row.color ||= begin