gruff 0.14.0 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +28 -12
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +20 -24
  5. data/CHANGELOG.md +52 -0
  6. data/README.md +10 -3
  7. data/gruff.gemspec +9 -10
  8. data/lib/gruff/accumulator_bar.rb +1 -1
  9. data/lib/gruff/area.rb +6 -4
  10. data/lib/gruff/bar.rb +53 -31
  11. data/lib/gruff/base.rb +292 -184
  12. data/lib/gruff/bezier.rb +4 -2
  13. data/lib/gruff/box_plot.rb +180 -0
  14. data/lib/gruff/bullet.rb +6 -6
  15. data/lib/gruff/candlestick.rb +120 -0
  16. data/lib/gruff/dot.rb +11 -12
  17. data/lib/gruff/font.rb +3 -0
  18. data/lib/gruff/helper/bar_conversion.rb +6 -10
  19. data/lib/gruff/helper/bar_mixin.rb +25 -0
  20. data/lib/gruff/helper/bar_value_label.rb +24 -40
  21. data/lib/gruff/helper/stacked_mixin.rb +19 -1
  22. data/lib/gruff/histogram.rb +9 -5
  23. data/lib/gruff/line.rb +49 -48
  24. data/lib/gruff/mini/legend.rb +11 -11
  25. data/lib/gruff/net.rb +23 -18
  26. data/lib/gruff/patch/rmagick.rb +0 -1
  27. data/lib/gruff/patch/string.rb +1 -0
  28. data/lib/gruff/pie.rb +26 -12
  29. data/lib/gruff/renderer/dash_line.rb +3 -2
  30. data/lib/gruff/renderer/dot.rb +28 -15
  31. data/lib/gruff/renderer/line.rb +1 -3
  32. data/lib/gruff/renderer/rectangle.rb +6 -2
  33. data/lib/gruff/renderer/renderer.rb +4 -8
  34. data/lib/gruff/renderer/text.rb +7 -1
  35. data/lib/gruff/scatter.rb +64 -56
  36. data/lib/gruff/side_bar.rb +64 -30
  37. data/lib/gruff/side_stacked_bar.rb +43 -54
  38. data/lib/gruff/spider.rb +52 -18
  39. data/lib/gruff/stacked_area.rb +18 -8
  40. data/lib/gruff/stacked_bar.rb +59 -29
  41. data/lib/gruff/store/xy_data.rb +2 -0
  42. data/lib/gruff/version.rb +1 -1
  43. data/lib/gruff.rb +67 -58
  44. metadata +22 -21
  45. data/.rubocop_todo.yml +0 -116
  46. data/lib/gruff/scene.rb +0 -200
  47. data/lib/gruff/store/custom_data.rb +0 -36
data/lib/gruff/scatter.rb CHANGED
@@ -114,16 +114,35 @@ private
114
114
  @x_label_margin = nil
115
115
  end
116
116
 
117
- def draw_graph
118
- # Check to see if more than one datapoint was given. NaN can result otherwise.
119
- @x_increment = (@x_spread > 1) ? (@graph_width / (@x_spread - 1).to_f) : @graph_width
117
+ def setup_drawing
118
+ @center_labels_over_point = false
119
+ super
120
+ end
121
+
122
+ def setup_data
123
+ # TODO: Need to get x-axis labels working. Current behavior will be to not allow.
124
+ @labels = {}
125
+
126
+ # Update the global min/max values for the x data
127
+ @maximum_x_value = (@maximum_x_value || store.max_x).to_f
128
+ @minimum_x_value = (@minimum_x_value || store.min_x).to_f
129
+
130
+ if @x_axis_increment
131
+ # TODO: Make this work for negative values
132
+ @maximum_x_value = [@maximum_x_value.ceil, @x_axis_increment.to_f].max
133
+ @minimum_x_value = @minimum_x_value.floor
134
+ end
120
135
 
136
+ super
137
+ end
138
+
139
+ def draw_graph
121
140
  store.norm_data.each do |data_row|
122
141
  data_row.coordinates.each do |x_value, y_value|
123
142
  next if y_value.nil? || x_value.nil?
124
143
 
125
144
  new_x = get_x_coord(x_value, @graph_width, @graph_left)
126
- new_y = @graph_top + (@graph_height - y_value * @graph_height)
145
+ new_y = @graph_top + (@graph_height - (y_value * @graph_height))
127
146
 
128
147
  # Reset each time to avoid thin-line errors
129
148
  stroke_width = @stroke_width || clip_value_if_greater_than(@columns / (store.norm_data.first[1].size * 4), 5.0)
@@ -133,22 +152,7 @@ private
133
152
  end
134
153
  end
135
154
 
136
- def setup_data
137
- # Update the global min/max values for the x data
138
- @maximum_x_value ||= store.max_x
139
- @minimum_x_value ||= store.min_x
140
-
141
- super
142
- end
143
-
144
- def setup_drawing
145
- # TODO: Need to get x-axis labels working. Current behavior will be to not allow.
146
- @labels = {}
147
-
148
- super
149
- end
150
-
151
- def calculate_spread #:nodoc:
155
+ def calculate_spread
152
156
  super
153
157
  @x_spread = @maximum_x_value.to_f - @minimum_x_value.to_f
154
158
  @x_spread = @x_spread > 0 ? @x_spread : 1
@@ -165,49 +169,21 @@ private
165
169
  super
166
170
  return if @hide_line_markers
167
171
 
168
- if @x_axis_increment.nil?
169
- # TODO: Do the same for larger numbers...100, 75, 50, 25
170
- if @marker_x_count.nil?
171
- (3..7).each do |lines|
172
- if @x_spread % lines == 0.0
173
- @marker_x_count = lines
174
- break
175
- end
176
- end
177
- @marker_x_count ||= 4
178
- end
179
- @x_increment = (@x_spread > 0) ? (@x_spread / @marker_x_count) : 1
180
- unless @disable_significant_rounding_x_axis
181
- @x_increment = significant(@x_increment)
182
- end
183
- else
184
- # TODO: Make this work for negative values
185
- @maximum_x_value = [@maximum_x_value.ceil, @x_axis_increment].max
186
- @minimum_x_value = @minimum_x_value.floor
187
- calculate_spread
188
- normalize
189
-
190
- @marker_x_count = (@x_spread / @x_axis_increment).to_i
191
- @x_increment = @x_axis_increment
192
- end
193
- increment_x_scaled = @graph_width.to_f / (@x_spread / @x_increment)
172
+ increment_x_scaled = @graph_width / (@x_spread / x_increment)
194
173
 
195
174
  # Draw vertical line markers and annotate with numbers
196
- (0..@marker_x_count).each do |index|
175
+ (0..marker_x_count).each do |index|
197
176
  # TODO: Fix the vertical lines, and enable them by default. Not pretty when they don't match up with top y-axis line
198
177
  if @show_vertical_markers
199
- x = @graph_left + @graph_width - index.to_f * increment_x_scaled
200
-
201
- line_renderer = Gruff::Renderer::Line.new(renderer, color: @marker_color, shadow_color: @marker_shadow_color)
202
- line_renderer.render(x, @graph_top, x, @graph_bottom)
178
+ draw_marker_vertical_line(@graph_left + @graph_width - (index * increment_x_scaled))
203
179
  end
204
180
 
205
181
  unless @hide_line_numbers
206
- marker_label = BigDecimal(index.to_s) * BigDecimal(@x_increment.to_s) + BigDecimal(@minimum_x_value.to_s)
182
+ marker_label = (BigDecimal(index.to_s) * BigDecimal(x_increment.to_s)) + BigDecimal(@minimum_x_value.to_s)
207
183
  y_offset = @graph_bottom + (@x_label_margin || LABEL_MARGIN)
208
- x_offset = get_x_coord(index.to_f, increment_x_scaled, @graph_left)
184
+ x_offset = get_x_coord(index, increment_x_scaled, @graph_left)
209
185
 
210
- label = x_axis_label(marker_label, @x_increment)
186
+ label = x_axis_label(marker_label, x_increment)
211
187
  rotation = -90.0 if @use_vertical_x_labels
212
188
  text_renderer = Gruff::Renderer::Text.new(renderer, label, font: @marker_font, rotation: rotation)
213
189
  text_renderer.add_to_render_queue(1.0, 1.0, x_offset, y_offset)
@@ -215,7 +191,39 @@ private
215
191
  end
216
192
  end
217
193
 
218
- def get_x_coord(x_data_point, width, offset) #:nodoc:
219
- x_data_point * width + offset
194
+ def get_x_coord(x_data_point, width, offset)
195
+ (x_data_point * width) + offset
196
+ end
197
+
198
+ def marker_x_count
199
+ # TODO: Do the same for larger numbers...100, 75, 50, 25
200
+ @marker_x_count ||= begin
201
+ if @x_axis_increment.nil?
202
+ count = nil
203
+ (3..7).each do |lines|
204
+ if @x_spread % lines == 0.0
205
+ count = lines and break
206
+ end
207
+ end
208
+ count || 4
209
+ else
210
+ (@x_spread / @x_axis_increment).to_i
211
+ end
212
+ end
213
+ end
214
+
215
+ def x_increment
216
+ @x_increment ||= begin
217
+ if @x_axis_increment.nil?
218
+ increment = @x_spread > 0 ? (@x_spread / marker_x_count) : 1
219
+ unless @disable_significant_rounding_x_axis
220
+ increment = significant(increment)
221
+ end
222
+ else
223
+ increment = @x_axis_increment
224
+ end
225
+
226
+ increment
227
+ end
220
228
  end
221
229
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'helper/bar_mixin'
4
+
3
5
  # Graph with individual horizontal bars instead of vertical bars.
4
6
  #
5
7
  # Here's how to set up a Gruff::SideBar.
@@ -19,6 +21,8 @@
19
21
  # g.write('sidebar.png')
20
22
  #
21
23
  class Gruff::SideBar < Gruff::Base
24
+ include BarMixin
25
+
22
26
  # Spacing factor applied between bars.
23
27
  attr_writer :bar_spacing
24
28
 
@@ -36,6 +40,15 @@ class Gruff::SideBar < Gruff::Base
36
40
  # Prevent drawing of column labels left of a side bar graph. Default is +false+.
37
41
  attr_writer :hide_labels
38
42
 
43
+ # Value to avoid completely overwriting the coordinate axis
44
+ AXIS_MARGIN = 0.5
45
+ private_constant :AXIS_MARGIN
46
+
47
+ def initialize(*)
48
+ super
49
+ @has_left_labels = true
50
+ end
51
+
39
52
  # With Side Bars use the data label for the marker value to the left of the bar.
40
53
  # @deprecated
41
54
  def use_data_label=(_value)
@@ -51,7 +64,6 @@ private
51
64
  @label_formatting = nil
52
65
  @show_labels_for_bar_values = false
53
66
  @hide_labels = false
54
- @has_left_labels = true
55
67
  end
56
68
 
57
69
  def hide_labels?
@@ -59,20 +71,37 @@ private
59
71
  end
60
72
 
61
73
  def hide_left_label_area?
62
- hide_labels?
74
+ hide_labels? && @y_axis_label.nil?
63
75
  end
64
76
 
65
77
  def hide_bottom_label_area?
66
- @hide_line_markers
78
+ @hide_line_markers && @x_axis_label.nil? && @legend_at_bottom == false
67
79
  end
68
80
 
69
- # Value to avoid completely overwriting the coordinate axis
70
- AXIS_MARGIN = 0.5
81
+ def setup_graph_measurements
82
+ super
83
+ return if @hide_line_markers
84
+
85
+ if @show_labels_for_bar_values
86
+ if maximum_value >= 0
87
+ _, metrics = Gruff::BarValueLabel.metrics(maximum_value, @label_formatting, proc_text_metrics)
88
+ @graph_right -= metrics.width
89
+ end
90
+
91
+ if minimum_value < 0
92
+ _, metrics = Gruff::BarValueLabel.metrics(minimum_value, @label_formatting, proc_text_metrics)
93
+ width = metrics.width + LABEL_MARGIN
94
+ @graph_left += width - @graph_left if width > @graph_left
95
+ end
96
+
97
+ @graph_width = @graph_right - @graph_left
98
+ end
99
+ end
71
100
 
72
101
  def draw_graph
73
102
  # Setup spacing.
74
103
  #
75
- bars_width = (@graph_height - calculate_spacing) / column_count.to_f
104
+ bars_width = (@graph_height - calculate_spacing) / column_count
76
105
  bar_width = bars_width / store.length
77
106
  padding = (bar_width * (1 - @bar_spacing)) / 2
78
107
 
@@ -82,30 +111,33 @@ private
82
111
  minimum_value: minimum_value, maximum_value: maximum_value, spread: @spread
83
112
  )
84
113
 
85
- store.norm_data.each_with_index do |data_row, row_index|
86
- data_row.points.each_with_index do |data_point, point_index|
87
- group_spacing = @group_spacing * @scale * point_index
88
-
89
- left_y = @graph_top + (bars_width * point_index) + (bar_width * row_index) + padding + group_spacing
90
- right_y = left_y + bar_width * @bar_spacing
114
+ group_spacing = @group_spacing * @scale
115
+ group_left_y = @graph_top
91
116
 
92
- left_x, right_x = conversion.get_top_bottom_scaled(data_point).sort
117
+ normalized_group_bars.each_with_index do |group_bars, group_index|
118
+ right_y = 0
119
+ group_bars.each_with_index do |bar, index|
120
+ left_y = group_left_y + (bar_width * index) + padding
121
+ right_y = left_y + (bar_width * @bar_spacing)
93
122
 
94
- rect_renderer = Gruff::Renderer::Rectangle.new(renderer, color: data_row.color)
95
- rect_renderer.render(left_x + AXIS_MARGIN, left_y, right_x + AXIS_MARGIN, right_y)
96
-
97
- # Calculate center based on bar_width and current row
98
- label_center = left_y + bars_width / 2
123
+ bottom_x, top_x = conversion.get_top_bottom_scaled(bar.point).sort
124
+ if bar.point != 0
125
+ rect_renderer = Gruff::Renderer::Rectangle.new(renderer, color: bar.color)
126
+ rect_renderer.render(bottom_x + AXIS_MARGIN, left_y, top_x, right_y)
127
+ end
99
128
 
100
- # Subtract half a bar width to center left if requested
101
- draw_label(label_center, point_index)
102
- if @show_labels_for_bar_values
103
- bar_value_label = Gruff::BarValueLabel::SideBar.new([left_x, left_y, right_x, right_y], store.data[row_index].points[point_index])
104
- bar_value_label.prepare_rendering(@label_formatting, bar_width) do |x, y, text|
105
- draw_value_label(x, y, text)
129
+ if @show_labels_for_bar_values && bar.value
130
+ bar_value_label = Gruff::BarValueLabel::SideBar.new([bottom_x, left_y, top_x, right_y], bar.value)
131
+ bar_value_label.prepare_rendering(@label_formatting, proc_text_metrics) do |x, y, text, text_width, _text_height|
132
+ draw_value_label(text_width, bar_width * @bar_spacing, x, y, text)
106
133
  end
107
134
  end
108
135
  end
136
+
137
+ label_center = group_left_y + (bars_width / 2.0)
138
+ draw_label(label_center, group_index)
139
+
140
+ group_left_y = right_y + padding + group_spacing
109
141
  end
110
142
  end
111
143
 
@@ -118,17 +150,15 @@ private
118
150
  number_of_lines = 1 if number_of_lines == 0
119
151
 
120
152
  # TODO: Round maximum marker value to a round number like 100, 0.1, 0.5, etc.
121
- increment = significant(@spread.to_f / number_of_lines)
153
+ increment = significant(@spread / number_of_lines)
122
154
  (0..number_of_lines).each do |index|
123
155
  line_diff = (@graph_right - @graph_left) / number_of_lines
124
156
  x = @graph_right - (line_diff * index) - 1
125
-
126
- line_renderer = Gruff::Renderer::Line.new(renderer, color: @marker_color, shadow_color: @marker_shadow_color)
127
- line_renderer.render(x, @graph_bottom, x, @graph_top)
157
+ draw_marker_vertical_line(x)
128
158
 
129
159
  unless @hide_line_numbers
130
160
  diff = index - number_of_lines
131
- marker_label = BigDecimal(diff.abs.to_s) * BigDecimal(increment.to_s) + BigDecimal(minimum_value.to_s)
161
+ marker_label = (BigDecimal(diff.abs.to_s) * BigDecimal(increment.to_s)) + BigDecimal(minimum_value.to_s)
132
162
  label = x_axis_label(marker_label, @increment)
133
163
  text_renderer = Gruff::Renderer::Text.new(renderer, label, font: @marker_font)
134
164
  text_renderer.add_to_render_queue(0, 0, x, @graph_bottom + LABEL_MARGIN, Magick::CenterGravity)
@@ -141,11 +171,15 @@ private
141
171
 
142
172
  def draw_label(y_offset, index)
143
173
  draw_unique_label(index) do
144
- draw_label_at(@graph_left - LABEL_MARGIN, 1.0, 0.0, y_offset, @labels[index], Magick::EastGravity)
174
+ draw_label_at(@graph_left - LABEL_MARGIN, 1.0, 0.0, y_offset, @labels[index], gravity: Magick::EastGravity)
145
175
  end
146
176
  end
147
177
 
148
178
  def calculate_spacing
149
179
  @scale * @group_spacing * (column_count - 1)
150
180
  end
181
+
182
+ def proc_text_metrics
183
+ ->(text) { text_metrics(@marker_font, text) }
184
+ end
151
185
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'helper/stacked_mixin'
4
+
3
5
  #
4
6
  # New gruff graph type added to enable sideways stacking bar charts
5
7
  # (basically looks like a x/y flip of a standard stacking bar chart)
@@ -39,6 +41,11 @@ class Gruff::SideStackedBar < Gruff::SideBar
39
41
  # Prevent drawing of column labels left of a side stacked bar graph. Default is +false+.
40
42
  attr_writer :hide_labels
41
43
 
44
+ def initialize(*)
45
+ super
46
+ @has_left_labels = true
47
+ end
48
+
42
49
  private
43
50
 
44
51
  def initialize_attributes
@@ -48,7 +55,7 @@ private
48
55
  @label_formatting = nil
49
56
  @show_labels_for_bar_values = false
50
57
  @hide_labels = false
51
- @has_left_labels = true
58
+ @minimum_value = 0.0
52
59
  end
53
60
 
54
61
  def setup_data
@@ -56,66 +63,48 @@ private
56
63
  super
57
64
  end
58
65
 
59
- def hide_labels?
60
- @hide_labels
61
- end
62
-
63
- def hide_left_label_area?
64
- hide_labels?
65
- end
66
-
67
- def hide_bottom_label_area?
68
- @hide_line_markers
69
- end
70
-
71
66
  def draw_graph
72
67
  # Setup spacing.
73
68
  #
74
69
  # Columns sit stacked.
75
- bar_width = @graph_height / column_count.to_f
76
- height = Array.new(column_count, 0)
77
- length = Array.new(column_count, @graph_left)
70
+ bar_width = @graph_height / column_count
78
71
  padding = (bar_width * (1 - @bar_spacing)) / 2
79
- stack_bar_value_label = Gruff::BarValueLabel::StackedBar.new
80
-
81
- store.norm_data.each_with_index do |data_row, row_index|
82
- data_row.points.each_with_index do |data_point, point_index|
83
- ## using the original calculations from the stacked bar chart to get the difference between
84
- ## part of the bart chart we wish to stack.
85
- temp1 = @graph_left + (@graph_width -
86
- data_point * @graph_width -
87
- height[point_index]) + 1
88
- temp2 = @graph_left + @graph_width - height[point_index] - 1
89
- difference = temp2 - temp1
90
-
91
- left_x = length[point_index]
92
- left_y = @graph_top + (bar_width * point_index) + padding
93
- right_x = left_x + difference - @segment_spacing
94
- right_y = left_y + bar_width * @bar_spacing
95
- length[point_index] += difference
96
- height[point_index] += (data_point * @graph_width - 2)
97
-
98
- bar_value_label = Gruff::BarValueLabel::SideBar.new([left_x, left_y, right_x, right_y], store.data[row_index].points[point_index])
99
- stack_bar_value_label.add(bar_value_label, point_index)
100
-
101
- # if a data point is 0 it can result in weird really thing lines
102
- # that shouldn't even be there being drawn on top of the existing
103
- # bar - this is bad
104
- if data_point != 0
105
- rect_renderer = Gruff::Renderer::Rectangle.new(renderer, color: data_row.color)
106
- rect_renderer.render(left_x, left_y, right_x, right_y)
107
- # Calculate center based on bar_width and current row
108
- end
109
- # we still need to draw the labels
110
- # Calculate center based on bar_width and current row
111
- label_center = left_y + bar_width / 2
112
- draw_label(label_center, point_index)
72
+
73
+ # Setup the BarConversion Object
74
+ conversion = Gruff::BarConversion.new(
75
+ top: @graph_right, bottom: @graph_left,
76
+ minimum_value: minimum_value, maximum_value: maximum_value, spread: @spread
77
+ )
78
+
79
+ proc_text_metrics = ->(text) { text_metrics(@marker_font, text) }
80
+
81
+ normalized_stacked_bars.each_with_index do |stacked_bars, stacked_index|
82
+ total = 0
83
+ left_y = @graph_top + (bar_width * stacked_index) + padding
84
+ right_y = left_y + (bar_width * @bar_spacing)
85
+
86
+ top_x = 0
87
+ stacked_bars.each do |bar|
88
+ next if bar.point == 0
89
+
90
+ bottom_x, = conversion.get_top_bottom_scaled(total)
91
+ bottom_x += @segment_spacing
92
+ top_x, = conversion.get_top_bottom_scaled(total + bar.point)
93
+
94
+ rect_renderer = Gruff::Renderer::Rectangle.new(renderer, color: bar.color)
95
+ rect_renderer.render(bottom_x, left_y, top_x, right_y)
96
+
97
+ total += bar.point
113
98
  end
114
- end
115
99
 
116
- if @show_labels_for_bar_values
117
- stack_bar_value_label.prepare_rendering(@label_formatting, bar_width) do |x, y, text|
118
- draw_value_label(x, y, text)
100
+ label_center = left_y + (bar_width / 2.0)
101
+ draw_label(label_center, stacked_index)
102
+
103
+ if @show_labels_for_bar_values
104
+ bar_value_label = Gruff::BarValueLabel::SideBar.new([@graph_left, left_y, top_x, right_y], stacked_bars.sum(&:value))
105
+ bar_value_label.prepare_rendering(@label_formatting, proc_text_metrics) do |x, y, text, text_width, _text_height|
106
+ draw_value_label(text_width, bar_width * @bar_spacing, x, y, text)
107
+ end
119
108
  end
120
109
  end
121
110
  end
data/lib/gruff/spider.rb CHANGED
@@ -19,6 +19,11 @@ class Gruff::Spider < Gruff::Base
19
19
  attr_writer :hide_axes
20
20
  attr_writer :rotation
21
21
 
22
+ def initialize(max_value, target_width = 800)
23
+ super(target_width)
24
+ @max_value = max_value
25
+ end
26
+
22
27
  def transparent_background=(value)
23
28
  renderer.transparent_background(@columns, @rows) if value
24
29
  end
@@ -27,11 +32,6 @@ class Gruff::Spider < Gruff::Base
27
32
  @hide_title = @hide_text = value
28
33
  end
29
34
 
30
- def initialize(max_value, target_width = 800)
31
- super(target_width)
32
- @max_value = max_value
33
- end
34
-
35
35
  private
36
36
 
37
37
  def initialize_attributes
@@ -46,6 +46,29 @@ private
46
46
  @hide_line_markers.freeze
47
47
  end
48
48
 
49
+ def setup_drawing
50
+ @center_labels_over_point = false
51
+ super
52
+ end
53
+
54
+ def setup_graph_measurements
55
+ super
56
+
57
+ @graph_left += LABEL_MARGIN
58
+ @graph_top += LABEL_MARGIN
59
+ @graph_right -= LABEL_MARGIN
60
+ @graph_bottom -= LABEL_MARGIN
61
+
62
+ @graph_width = @graph_right - @graph_left
63
+ @graph_height = @graph_bottom - @graph_top
64
+ end
65
+
66
+ def setup_data
67
+ raise(Gruff::IncorrectNumberOfDatasetsException, 'Requires 3 or more data sets') if store.length < 3
68
+
69
+ super
70
+ end
71
+
49
72
  def draw_graph
50
73
  # Setup basic positioning
51
74
  radius = @graph_height / 2.0
@@ -64,23 +87,38 @@ private
64
87
  end
65
88
 
66
89
  def normalize_points(value)
67
- value * @unit_length
90
+ value.to_f * @unit_length
68
91
  end
69
92
 
70
93
  def draw_label(center_x, center_y, angle, radius, amount)
71
- r_offset = 50 # The distance out from the center of the pie to get point
72
- x_offset = center_x # The label points need to be tweaked slightly
73
- y_offset = center_y + 0 # This one doesn't though
94
+ degree = rad2deg(angle)
95
+ metrics = text_metrics(@marker_font, amount)
96
+
97
+ r_offset = LABEL_MARGIN # The distance out from the center of the pie to get point
98
+ x_offset = center_x # The label points need to be tweaked slightly
99
+
100
+ x_offset -= begin
101
+ case degree
102
+ when 0..45, 315..360
103
+ 0
104
+ when 135..225
105
+ metrics.width
106
+ else
107
+ metrics.width / 2
108
+ end
109
+ end
110
+
111
+ y_offset = center_y - (metrics.height / 2.0) # This one doesn't though
74
112
  x = x_offset + ((radius + r_offset) * Math.cos(angle))
75
113
  y = y_offset + ((radius + r_offset) * Math.sin(angle))
76
114
 
77
- draw_label_at(1.0, 1.0, x, y, amount, Magick::CenterGravity)
115
+ draw_label_at(metrics.width, metrics.height, x, y, amount, gravity: Magick::CenterGravity)
78
116
  end
79
117
 
80
118
  def draw_axes(center_x, center_y, radius, additive_angle, line_color = nil)
81
119
  return if @hide_axes
82
120
 
83
- current_angle = @rotation * Math::PI / 180.0
121
+ current_angle = deg2rad(@rotation)
84
122
 
85
123
  store.data.each do |data_row|
86
124
  x_offset = radius * Math.cos(current_angle)
@@ -97,18 +135,14 @@ private
97
135
 
98
136
  def draw_polygon(center_x, center_y, additive_angle, color = nil)
99
137
  points = []
100
- current_angle = @rotation * Math::PI / 180.0
138
+ current_angle = deg2rad(@rotation)
101
139
 
102
140
  store.data.each do |data_row|
103
- points << center_x + normalize_points(data_row.points.first) * Math.cos(current_angle)
104
- points << center_y + normalize_points(data_row.points.first) * Math.sin(current_angle)
141
+ points << (center_x + (normalize_points(data_row.points.first) * Math.cos(current_angle)))
142
+ points << (center_y + (normalize_points(data_row.points.first) * Math.sin(current_angle)))
105
143
  current_angle += additive_angle
106
144
  end
107
145
 
108
146
  Gruff::Renderer::Polygon.new(renderer, color: color || @marker_color, opacity: 0.4).render(points)
109
147
  end
110
-
111
- def sums_for_spider
112
- store.data.sum { |data_row| data_row.points.first }
113
- end
114
148
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'helper/stacked_mixin'
4
+
3
5
  #
4
6
  # Here's how to set up a Gruff::StackedArea.
5
7
  #
@@ -20,25 +22,31 @@ class Gruff::StackedArea < Gruff::Base
20
22
 
21
23
  private
22
24
 
25
+ def initialize_attributes
26
+ super
27
+ @minimum_value = 0.0
28
+ end
29
+
23
30
  def setup_data
24
31
  calculate_maximum_by_stack
25
32
  super
26
33
  end
27
34
 
28
35
  def draw_graph
29
- x_increment = @graph_width / (column_count - 1).to_f
36
+ x_increment = @graph_width / (column_count - 1)
30
37
 
31
38
  height = Array.new(column_count, 0)
32
39
 
33
- data_points = nil
40
+ prev_data_points = nil
34
41
  store.norm_data.each do |data_row|
35
- prev_data_points = data_points
42
+ next if data_row.points.empty?
43
+
36
44
  data_points = []
37
45
 
38
46
  data_row.points.each_with_index do |data_point, index|
39
47
  # Use incremented x and scaled y
40
48
  new_x = @graph_left + (x_increment * index)
41
- new_y = @graph_top + (@graph_height - data_point * @graph_height - height[index])
49
+ new_y = @graph_top + (@graph_height - (data_point * @graph_height) - height[index])
42
50
 
43
51
  height[index] += (data_point * @graph_height)
44
52
 
@@ -50,20 +58,22 @@ private
50
58
 
51
59
  poly_points = data_points.dup
52
60
  if prev_data_points
53
- (prev_data_points.length / 2 - 1).downto(0) do |i|
61
+ ((prev_data_points.length / 2) - 1).downto(0) do |i|
54
62
  poly_points << prev_data_points[2 * i]
55
- poly_points << prev_data_points[2 * i + 1]
63
+ poly_points << prev_data_points[(2 * i) + 1]
56
64
  end
57
65
  else
58
66
  poly_points << @graph_right
59
- poly_points << @graph_bottom - 1
67
+ poly_points << (@graph_bottom - 1)
60
68
  poly_points << @graph_left
61
- poly_points << @graph_bottom - 1
69
+ poly_points << (@graph_bottom - 1)
62
70
  end
63
71
  poly_points << data_points[0]
64
72
  poly_points << data_points[1]
65
73
 
66
74
  Gruff::Renderer::Polygon.new(renderer, color: data_row.color).render(poly_points)
75
+
76
+ prev_data_points = data_points
67
77
  end
68
78
  end
69
79
  end