gruff 0.16.0-java → 0.19.0-java

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.devcontainer/devcontainer.json +26 -0
  3. data/.github/workflows/ci.yml +9 -6
  4. data/.gitignore +1 -0
  5. data/.rubocop.yml +0 -6
  6. data/CHANGELOG.md +32 -0
  7. data/README.md +7 -3
  8. data/gruff.gemspec +2 -4
  9. data/lib/gruff/area.rb +3 -1
  10. data/lib/gruff/bar.rb +26 -24
  11. data/lib/gruff/base.rb +214 -93
  12. data/lib/gruff/bezier.rb +7 -8
  13. data/lib/gruff/{box_plot.rb → box.rb} +12 -6
  14. data/lib/gruff/bubble.rb +99 -0
  15. data/lib/gruff/candlestick.rb +22 -14
  16. data/lib/gruff/dot.rb +5 -6
  17. data/lib/gruff/helper/bar_conversion.rb +1 -5
  18. data/lib/gruff/helper/bar_mixin.rb +25 -0
  19. data/lib/gruff/helper/bar_value_label.rb +0 -22
  20. data/lib/gruff/helper/stacked_mixin.rb +16 -0
  21. data/lib/gruff/histogram.rb +8 -5
  22. data/lib/gruff/line.rb +47 -27
  23. data/lib/gruff/mini/bar.rb +1 -1
  24. data/lib/gruff/mini/legend.rb +13 -9
  25. data/lib/gruff/mini/pie.rb +2 -2
  26. data/lib/gruff/net.rb +26 -13
  27. data/lib/gruff/pie.rb +7 -2
  28. data/lib/gruff/renderer/bezier.rb +1 -1
  29. data/lib/gruff/renderer/circle.rb +5 -3
  30. data/lib/gruff/renderer/dash_line.rb +1 -1
  31. data/lib/gruff/renderer/dot.rb +26 -15
  32. data/lib/gruff/renderer/line.rb +1 -1
  33. data/lib/gruff/renderer/polygon.rb +1 -1
  34. data/lib/gruff/renderer/polyline.rb +4 -2
  35. data/lib/gruff/renderer/renderer.rb +0 -4
  36. data/lib/gruff/renderer/text.rb +7 -1
  37. data/lib/gruff/scatter.rb +52 -55
  38. data/lib/gruff/side_bar.rb +28 -24
  39. data/lib/gruff/side_stacked_bar.rb +36 -41
  40. data/lib/gruff/spider.rb +7 -2
  41. data/lib/gruff/stacked_area.rb +8 -3
  42. data/lib/gruff/stacked_bar.rb +48 -40
  43. data/lib/gruff/store/xy_data.rb +8 -9
  44. data/lib/gruff/store/xy_pointsizes_data.rb +60 -0
  45. data/lib/gruff/version.rb +1 -1
  46. data/lib/gruff.rb +3 -4
  47. metadata +9 -5
@@ -11,8 +11,8 @@ module Gruff
11
11
 
12
12
  def render(points)
13
13
  @renderer.draw.push
14
- @renderer.draw.stroke(@color)
15
14
  @renderer.draw.stroke_width(@width)
15
+ @renderer.draw.stroke(@color)
16
16
  @renderer.draw.fill_opacity(0.0)
17
17
  @renderer.draw.bezier(*points)
18
18
  @renderer.draw.pop
@@ -3,17 +3,19 @@
3
3
  module Gruff
4
4
  # @private
5
5
  class Renderer::Circle
6
- def initialize(renderer, color:, width: 1.0)
6
+ def initialize(renderer, color:, width: 1.0, opacity: 1.0)
7
7
  @renderer = renderer
8
8
  @color = color
9
9
  @width = width
10
+ @opacity = opacity
10
11
  end
11
12
 
12
13
  def render(origin_x, origin_y, perim_x, perim_y)
13
14
  @renderer.draw.push
14
- @renderer.draw.fill(@color)
15
- @renderer.draw.stroke(@color)
16
15
  @renderer.draw.stroke_width(@width)
16
+ @renderer.draw.stroke(@color)
17
+ @renderer.draw.fill_opacity(@opacity)
18
+ @renderer.draw.fill(@color)
17
19
  @renderer.draw.circle(origin_x, origin_y, perim_x, perim_y)
18
20
  @renderer.draw.pop
19
21
  end
@@ -13,9 +13,9 @@ module Gruff
13
13
  def render(start_x, start_y, end_x, end_y)
14
14
  @renderer.draw.push
15
15
  @renderer.draw.stroke_color(@color)
16
- @renderer.draw.fill_opacity(0.0)
17
16
  @renderer.draw.stroke_dasharray(*@dasharray)
18
17
  @renderer.draw.stroke_width(@width)
18
+ @renderer.draw.fill_opacity(0.0)
19
19
  @renderer.draw.line(start_x, start_y, end_x, end_y)
20
20
  @renderer.draw.pop
21
21
  end
@@ -11,31 +11,42 @@ module Gruff
11
11
  @opacity = opacity
12
12
  end
13
13
 
14
- def render(new_x, new_y, circle_radius)
15
- # @renderer.draw.push # TODO
14
+ def render(new_x, new_y, radius)
15
+ @renderer.draw.push
16
16
  @renderer.draw.stroke_width(@width)
17
17
  @renderer.draw.stroke(@color)
18
- @renderer.draw.fill(@color)
19
18
  @renderer.draw.fill_opacity(@opacity)
20
- if @style.to_s == 'square'
21
- square(new_x, new_y, circle_radius)
19
+ @renderer.draw.fill(@color)
20
+ case @style.to_sym
21
+ when :square
22
+ square(new_x, new_y, radius)
23
+ when :diamond
24
+ diamond(new_x, new_y, radius)
22
25
  else
23
- circle(new_x, new_y, circle_radius)
26
+ circle(new_x, new_y, radius)
24
27
  end
25
- # @renderer.draw.pop # TODO
28
+ @renderer.draw.pop
26
29
  end
27
30
 
28
- def circle(new_x, new_y, circle_radius)
29
- @renderer.draw.circle(new_x, new_y, new_x - circle_radius, new_y)
31
+ def circle(new_x, new_y, radius)
32
+ @renderer.draw.circle(new_x, new_y, new_x - radius, new_y)
30
33
  end
31
34
 
32
- def square(new_x, new_y, circle_radius)
33
- offset = (circle_radius * 0.8).to_i
34
- corner1 = new_x - offset
35
- corner2 = new_y - offset
36
- corner3 = new_x + offset
37
- corner4 = new_y + offset
35
+ def square(new_x, new_y, radius)
36
+ corner1 = new_x - radius
37
+ corner2 = new_y - radius
38
+ corner3 = new_x + radius
39
+ corner4 = new_y + radius
38
40
  @renderer.draw.rectangle(corner1, corner2, corner3, corner4)
39
41
  end
42
+
43
+ def diamond(new_x, new_y, radius)
44
+ polygon = []
45
+ polygon += [new_x - radius, new_y]
46
+ polygon += [new_x, new_y + radius]
47
+ polygon += [new_x + radius, new_y]
48
+ polygon += [new_x, new_y - radius]
49
+ @renderer.draw.polygon(*polygon)
50
+ end
40
51
  end
41
52
  end
@@ -30,9 +30,9 @@ module Gruff
30
30
  end
31
31
 
32
32
  @renderer.draw.push
33
+ @renderer.draw.stroke_width(@width) if @width
33
34
  @renderer.draw.stroke(color)
34
35
  @renderer.draw.fill(color)
35
- @renderer.draw.stroke_width(@width) if @width
36
36
  @renderer.draw.line(start_x, start_y, end_x, end_y)
37
37
  @renderer.draw.pop
38
38
  end
@@ -14,8 +14,8 @@ module Gruff
14
14
  @renderer.draw.push
15
15
  @renderer.draw.stroke_width(@width)
16
16
  @renderer.draw.stroke(@color)
17
- @renderer.draw.fill(@color)
18
17
  @renderer.draw.fill_opacity(@opacity)
18
+ @renderer.draw.fill(@color)
19
19
  @renderer.draw.polygon(*points)
20
20
  @renderer.draw.pop
21
21
  end
@@ -3,17 +3,19 @@
3
3
  module Gruff
4
4
  # @private
5
5
  class Renderer::Polyline
6
- def initialize(renderer, color:, width:)
6
+ def initialize(renderer, color:, width: 1.0, linejoin: 'bevel')
7
7
  @renderer = renderer
8
8
  @color = color
9
9
  @width = width
10
+ @linejoin = linejoin
10
11
  end
11
12
 
12
13
  def render(points)
13
14
  @renderer.draw.push
15
+ @renderer.draw.stroke_linejoin(@linejoin)
16
+ @renderer.draw.stroke_width(@width)
14
17
  @renderer.draw.stroke(@color)
15
18
  @renderer.draw.fill('transparent')
16
- @renderer.draw.stroke_width(@width)
17
19
  @renderer.draw.polyline(*points)
18
20
  @renderer.draw.pop
19
21
  end
@@ -23,10 +23,6 @@ module Gruff
23
23
  end
24
24
  end
25
25
 
26
- def background_image=(image)
27
- @image = image
28
- end
29
-
30
26
  def background(columns, rows, scale, theme_options)
31
27
  case theme_options[:background_colors]
32
28
  when Array
@@ -25,6 +25,7 @@ module Gruff
25
25
  end
26
26
 
27
27
  def render(width, height, x, y, gravity = Magick::NorthGravity)
28
+ @renderer.draw.push
28
29
  @renderer.draw.rotation = @rotation if @rotation
29
30
  @renderer.draw.fill = @font.color
30
31
  @renderer.draw.stroke = 'transparent'
@@ -37,9 +38,11 @@ module Gruff
37
38
  x, y,
38
39
  @text, @renderer.scale)
39
40
  @renderer.draw.rotation = -@rotation if @rotation
41
+ @renderer.draw.pop
40
42
  end
41
43
 
42
44
  def metrics
45
+ @renderer.draw.push
43
46
  @renderer.draw.font = @font.file_path
44
47
  @renderer.draw.font_weight = @font.weight
45
48
  @renderer.draw.pointsize = @font.size
@@ -50,7 +53,10 @@ module Gruff
50
53
  # So, in here, it just escape % in order to avoid SEGV.
51
54
  text = @text.to_s.gsub(/(%+)/) { ('%' * Regexp.last_match(1).size * 2).to_s }
52
55
 
53
- @renderer.draw.get_type_metrics(@renderer.image, text)
56
+ metrics = @renderer.draw.get_type_metrics(@renderer.image, text)
57
+ @renderer.draw.pop
58
+
59
+ metrics
54
60
  end
55
61
  end
56
62
  end
data/lib/gruff/scatter.rb CHANGED
@@ -24,24 +24,35 @@ class Gruff::Scatter < Gruff::Base
24
24
  attr_writer :circle_radius
25
25
  attr_writer :stroke_width
26
26
 
27
- # Allow disabling the significant rounding when labeling the X axis.
28
- # This is useful when working with a small range of high values (for example, a date range of months, while seconds as units).
29
- attr_writer :disable_significant_rounding_x_axis
30
-
31
27
  # Allow for vertical marker lines.
32
28
  attr_writer :show_vertical_markers
33
29
 
34
- # Allow using vertical labels in the X axis (and setting the label margin).
35
- attr_writer :x_label_margin
36
- attr_writer :use_vertical_x_labels
37
-
38
30
  # Allow enabling vertical lines. When you have a lot of data, they can work great.
39
- # @deprecated Please use +show_vertical_markers+ attribute instead.
31
+ # @deprecated Please use {#show_vertical_markers=} instead.
40
32
  def enable_vertical_line_markers=(value)
41
33
  warn '#enable_vertical_line_markers= is deprecated. Please use `show_vertical_markers` attribute instead'
42
34
  @show_vertical_markers = value
43
35
  end
44
36
 
37
+ # Allow using vertical labels in the X axis.
38
+ # @deprecated Please use {Gruff::Base#label_rotation=} instead.
39
+ def use_vertical_x_labels=(_value)
40
+ warn '#use_vertical_x_labels= is deprecated. It is no longer effective. Please use `#label_rotation=` instead'
41
+ end
42
+
43
+ # Allow using vertical labels in the X axis (and setting the label margin).
44
+ # @deprecated
45
+ def x_label_margin=(_value)
46
+ warn '#x_label_margin= is deprecated. It is no longer effective.'
47
+ end
48
+
49
+ # Allow disabling the significant rounding when labeling the X axis.
50
+ # This is useful when working with a small range of high values (for example, a date range of months, while seconds as units).
51
+ # @deprecated
52
+ def disable_significant_rounding_x_axis=(_value)
53
+ warn '#disable_significant_rounding_x_axis= is deprecated. It is no longer effective.'
54
+ end
55
+
45
56
  # The first parameter is the name of the dataset. The next two are the
46
57
  # x and y axis data points contain in their own array in that respective
47
58
  # order. The final parameter is the color.
@@ -55,11 +66,10 @@ class Gruff::Scatter < Gruff::Base
55
66
  # @note If you want to use a preset theme, you must set it before calling {#data}.
56
67
  #
57
68
  # @param name [String, Symbol] containing the name of the dataset.
58
- # @param x_data_points [Array] An Array of of x-axis data points.
59
- # @param y_data_points [Array] An Array of of y-axis data points.
69
+ # @param x_data_points [Array] An Array of x-axis data points.
70
+ # @param y_data_points [Array] An Array of y-axis data points.
60
71
  # @param color [String] The hex string for the color of the dataset. Defaults to nil.
61
72
  #
62
- #
63
73
  # @raise [ArgumentError] Data points contain nil values.
64
74
  # This error will get raised if either the x or y axis data points array
65
75
  # contains a +nil+ value. The graph will not make an assumption
@@ -88,7 +98,7 @@ class Gruff::Scatter < Gruff::Base
88
98
  raise ArgumentError, 'x_data_points.length != y_data_points.length!' if x_data_points.length != y_data_points.length
89
99
 
90
100
  # Call the existing data routine for the x/y axis data
91
- store.add(name, y_data_points, color, x_data_points)
101
+ store.add(name, x_data_points, y_data_points, color)
92
102
  end
93
103
 
94
104
  alias dataxy data
@@ -102,32 +112,16 @@ private
102
112
  def initialize_attributes
103
113
  super
104
114
 
105
- @baseline_x_color = @baseline_y_color = 'red'
106
- @baseline_x_value = @baseline_y_value = nil
107
115
  @circle_radius = nil
108
- @disable_significant_rounding_x_axis = false
109
116
  @show_vertical_markers = false
110
117
  @marker_x_count = nil
111
118
  @maximum_x_value = @minimum_x_value = nil
112
119
  @stroke_width = nil
113
- @use_vertical_x_labels = false
114
- @x_label_margin = nil
115
120
  end
116
121
 
117
- def draw_graph
118
- store.norm_data.each do |data_row|
119
- data_row.coordinates.each do |x_value, y_value|
120
- next if y_value.nil? || x_value.nil?
121
-
122
- new_x = get_x_coord(x_value, @graph_width, @graph_left)
123
- new_y = @graph_top + (@graph_height - (y_value * @graph_height))
124
-
125
- # Reset each time to avoid thin-line errors
126
- stroke_width = @stroke_width || clip_value_if_greater_than(@columns / (store.norm_data.first[1].size * 4), 5.0)
127
- circle_radius = @circle_radius || clip_value_if_greater_than(@columns / (store.norm_data.first[1].size * 2.5), 5.0)
128
- Gruff::Renderer::Circle.new(renderer, color: data_row.color, width: stroke_width).render(new_x, new_y, new_x - circle_radius, new_y)
129
- end
130
- end
122
+ def setup_drawing
123
+ @center_labels_over_point = false
124
+ super
131
125
  end
132
126
 
133
127
  def setup_data
@@ -147,10 +141,26 @@ private
147
141
  super
148
142
  end
149
143
 
144
+ def draw_graph
145
+ stroke_width = @stroke_width || clip_value_if_greater_than(@columns / (store.norm_data.first[1].size * 4.0), 5.0)
146
+ circle_radius = @circle_radius || clip_value_if_greater_than(@columns / (store.norm_data.first[1].size * 2.5), 5.0)
147
+
148
+ store.norm_data.each do |data_row|
149
+ data_row.coordinates.each do |x_value, y_value|
150
+ next if y_value.nil? || x_value.nil?
151
+
152
+ new_x = @graph_left + (x_value * @graph_width)
153
+ new_y = @graph_bottom - (y_value * @graph_height)
154
+
155
+ Gruff::Renderer::Circle.new(renderer, color: data_row.color, width: stroke_width).render(new_x, new_y, new_x - circle_radius, new_y)
156
+ end
157
+ end
158
+ end
159
+
150
160
  def calculate_spread
151
161
  super
152
162
  @x_spread = @maximum_x_value.to_f - @minimum_x_value.to_f
153
- @x_spread = @x_spread > 0 ? @x_spread : 1
163
+ @x_spread = @x_spread > 0 ? @x_spread : 1.0
154
164
  end
155
165
 
156
166
  def normalize
@@ -164,35 +174,27 @@ private
164
174
  super
165
175
  return if @hide_line_markers
166
176
 
167
- increment_x_scaled = @graph_width / (@x_spread / x_increment)
177
+ increment_x_scaled = (@graph_width / (@x_spread / x_increment)).to_f
168
178
 
169
179
  # Draw vertical line markers and annotate with numbers
170
180
  (0..marker_x_count).each do |index|
171
181
  # TODO: Fix the vertical lines, and enable them by default. Not pretty when they don't match up with top y-axis line
172
182
  if @show_vertical_markers
173
- x = @graph_left + @graph_width - (index * increment_x_scaled)
174
-
175
- Gruff::Renderer::Line.new(renderer, color: @marker_color).render(x, @graph_top, x, @graph_bottom)
176
- Gruff::Renderer::Line.new(renderer, color: @marker_shadow_color).render(x, @graph_top + 1, x, @graph_bottom + 1) if @marker_shadow_color
183
+ draw_marker_vertical_line(@graph_left + (index * increment_x_scaled))
177
184
  end
178
185
 
179
186
  unless @hide_line_numbers
180
187
  marker_label = (BigDecimal(index.to_s) * BigDecimal(x_increment.to_s)) + BigDecimal(@minimum_x_value.to_s)
181
- y_offset = @graph_bottom + (@x_label_margin || LABEL_MARGIN)
182
- x_offset = get_x_coord(index, increment_x_scaled, @graph_left)
183
-
184
188
  label = x_axis_label(marker_label, x_increment)
185
- rotation = -90.0 if @use_vertical_x_labels
186
- text_renderer = Gruff::Renderer::Text.new(renderer, label, font: @marker_font, rotation: rotation)
187
- text_renderer.add_to_render_queue(1.0, 1.0, x_offset, y_offset)
189
+ x = @graph_left + (increment_x_scaled * index)
190
+ y = @graph_bottom
191
+ x_offset, y_offset = calculate_label_offset(@marker_font, label, LABEL_MARGIN, @label_rotation)
192
+
193
+ draw_label_at(1.0, 1.0, x + x_offset, y + y_offset, label, rotation: @label_rotation)
188
194
  end
189
195
  end
190
196
  end
191
197
 
192
- def get_x_coord(x_data_point, width, offset)
193
- (x_data_point * width) + offset
194
- end
195
-
196
198
  def marker_x_count
197
199
  # TODO: Do the same for larger numbers...100, 75, 50, 25
198
200
  @marker_x_count ||= begin
@@ -213,15 +215,10 @@ private
213
215
  def x_increment
214
216
  @x_increment ||= begin
215
217
  if @x_axis_increment.nil?
216
- increment = @x_spread > 0 ? (@x_spread / marker_x_count) : 1
217
- unless @disable_significant_rounding_x_axis
218
- increment = significant(increment)
219
- end
218
+ @x_spread > 0 ? significant(@x_spread / marker_x_count) : 1.0
220
219
  else
221
- increment = @x_axis_increment
220
+ @x_axis_increment.to_f
222
221
  end
223
-
224
- increment
225
222
  end
226
223
  end
227
224
  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
 
@@ -79,8 +83,6 @@ private
79
83
  return if @hide_line_markers
80
84
 
81
85
  if @show_labels_for_bar_values
82
- proc_text_metrics = ->(text) { text_metrics(@marker_font, text) }
83
-
84
86
  if maximum_value >= 0
85
87
  _, metrics = Gruff::BarValueLabel.metrics(maximum_value, @label_formatting, proc_text_metrics)
86
88
  @graph_right -= metrics.width
@@ -109,32 +111,32 @@ private
109
111
  minimum_value: minimum_value, maximum_value: maximum_value, spread: @spread
110
112
  )
111
113
 
112
- proc_text_metrics = ->(text) { text_metrics(@marker_font, text) }
114
+ group_left_y = @graph_top
113
115
 
114
- store.norm_data.each_with_index do |data_row, row_index|
115
- data_row.points.each_with_index do |data_point, point_index|
116
- group_spacing = @group_spacing * @scale * point_index
117
-
118
- left_y = @graph_top + (bars_width * point_index) + (bar_width * row_index) + padding + group_spacing
116
+ normalized_group_bars.each_with_index do |group_bars, group_index|
117
+ right_y = 0
118
+ group_bars.each_with_index do |bar, index|
119
+ left_y = group_left_y + (bar_width * index) + padding
119
120
  right_y = left_y + (bar_width * @bar_spacing)
120
121
 
121
- left_x, right_x = conversion.get_top_bottom_scaled(data_point).sort
122
-
123
- rect_renderer = Gruff::Renderer::Rectangle.new(renderer, color: data_row.color)
124
- rect_renderer.render(left_x + AXIS_MARGIN, left_y, right_x + AXIS_MARGIN, right_y)
125
-
126
- # Calculate center based on bar_width and current row
127
- label_center = left_y + (bars_width / 2.0)
122
+ bottom_x, top_x = conversion.get_top_bottom_scaled(bar.point).sort
123
+ if bar.point != 0
124
+ rect_renderer = Gruff::Renderer::Rectangle.new(renderer, color: bar.color)
125
+ rect_renderer.render(bottom_x + AXIS_MARGIN, left_y, top_x, right_y)
126
+ end
128
127
 
129
- # Subtract half a bar width to center left if requested
130
- draw_label(label_center, point_index)
131
- if @show_labels_for_bar_values
132
- bar_value_label = Gruff::BarValueLabel::SideBar.new([left_x, left_y, right_x, right_y], store.data[row_index].points[point_index])
128
+ if @show_labels_for_bar_values && bar.value
129
+ bar_value_label = Gruff::BarValueLabel::SideBar.new([bottom_x, left_y, top_x, right_y], bar.value)
133
130
  bar_value_label.prepare_rendering(@label_formatting, proc_text_metrics) do |x, y, text, text_width, _text_height|
134
131
  draw_value_label(text_width, bar_width * @bar_spacing, x, y, text)
135
132
  end
136
133
  end
137
134
  end
135
+
136
+ label_center = group_left_y + (bars_width / 2.0)
137
+ draw_label(label_center, group_index)
138
+
139
+ group_left_y = right_y + padding + @group_spacing
138
140
  end
139
141
  end
140
142
 
@@ -151,9 +153,7 @@ private
151
153
  (0..number_of_lines).each do |index|
152
154
  line_diff = (@graph_right - @graph_left) / number_of_lines
153
155
  x = @graph_right - (line_diff * index) - 1
154
-
155
- Gruff::Renderer::Line.new(renderer, color: @marker_color).render(x, @graph_bottom, x, @graph_top)
156
- Gruff::Renderer::Line.new(renderer, color: @marker_shadow_color).render(x, @graph_bottom + 1, x, @graph_top + 1) if @marker_shadow_color
156
+ draw_marker_vertical_line(x)
157
157
 
158
158
  unless @hide_line_numbers
159
159
  diff = index - number_of_lines
@@ -170,11 +170,15 @@ private
170
170
 
171
171
  def draw_label(y_offset, index)
172
172
  draw_unique_label(index) do
173
- draw_label_at(@graph_left - LABEL_MARGIN, 1.0, 0.0, y_offset, @labels[index], Magick::EastGravity)
173
+ draw_label_at(@graph_left - LABEL_MARGIN, 1.0, 0.0, y_offset, @labels[index], gravity: Magick::EastGravity)
174
174
  end
175
175
  end
176
176
 
177
177
  def calculate_spacing
178
- @scale * @group_spacing * (column_count - 1)
178
+ @group_spacing * (column_count - 1)
179
+ end
180
+
181
+ def proc_text_metrics
182
+ ->(text) { text_metrics(@marker_font, text) }
179
183
  end
180
184
  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)
@@ -66,50 +68,43 @@ private
66
68
  #
67
69
  # Columns sit stacked.
68
70
  bar_width = @graph_height / column_count
69
- height = Array.new(column_count, 0)
70
- length = Array.new(column_count, @graph_left)
71
71
  padding = (bar_width * (1 - @bar_spacing)) / 2
72
- stack_bar_value_labels = Gruff::BarValueLabel::StackedBar.new
73
-
74
- store.norm_data.each_with_index do |data_row, row_index|
75
- data_row.points.each_with_index do |data_point, point_index|
76
- ## using the original calculations from the stacked bar chart to get the difference between
77
- ## part of the bart chart we wish to stack.
78
- temp1 = @graph_left + (@graph_width - (data_point * @graph_width) - height[point_index])
79
- temp2 = @graph_left + @graph_width - height[point_index]
80
- difference = temp2 - temp1
81
- difference = 0 if difference < 0
82
-
83
- left_x = length[point_index]
84
- left_y = @graph_top + (bar_width * point_index) + padding
85
- right_x = left_x + difference
86
- right_x -= @segment_spacing if row_index != store.columns - 1
87
- right_y = left_y + (bar_width * @bar_spacing)
88
- length[point_index] += difference
89
- height[point_index] += (data_point * @graph_width)
90
-
91
- bar_value_label = Gruff::BarValueLabel::SideBar.new([left_x, left_y, right_x, right_y], store.data[row_index].points[point_index])
92
- stack_bar_value_labels.add(bar_value_label, point_index)
93
-
94
- # if a data point is 0 it can result in weird really thing lines
95
- # that shouldn't even be there being drawn on top of the existing
96
- # bar - this is bad
97
- if data_point != 0
98
- rect_renderer = Gruff::Renderer::Rectangle.new(renderer, color: data_row.color)
99
- rect_renderer.render(left_x, left_y, right_x, right_y)
100
- # Calculate center based on bar_width and current row
101
- end
102
- # we still need to draw the labels
103
- # Calculate center based on bar_width and current row
104
- label_center = left_y + (bar_width / 2.0)
105
- 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
106
98
  end
107
- end
108
99
 
109
- if @show_labels_for_bar_values
110
- proc_text_metrics = ->(text) { text_metrics(@marker_font, text) }
111
- stack_bar_value_labels.prepare_rendering(@label_formatting, proc_text_metrics) do |x, y, text, text_width, _text_height|
112
- draw_value_label(text_width, bar_width * @bar_spacing, 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
113
108
  end
114
109
  end
115
110
  end
data/lib/gruff/spider.rb CHANGED
@@ -46,6 +46,11 @@ 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
+
49
54
  def setup_graph_measurements
50
55
  super
51
56
 
@@ -82,7 +87,7 @@ private
82
87
  end
83
88
 
84
89
  def normalize_points(value)
85
- value * @unit_length
90
+ value.to_f * @unit_length
86
91
  end
87
92
 
88
93
  def draw_label(center_x, center_y, angle, radius, amount)
@@ -107,7 +112,7 @@ private
107
112
  x = x_offset + ((radius + r_offset) * Math.cos(angle))
108
113
  y = y_offset + ((radius + r_offset) * Math.sin(angle))
109
114
 
110
- draw_label_at(metrics.width, metrics.height, x, y, amount, Magick::CenterGravity)
115
+ draw_label_at(metrics.width, metrics.height, x, y, amount, gravity: Magick::CenterGravity)
111
116
  end
112
117
 
113
118
  def draw_axes(center_x, center_y, radius, additive_angle, line_color = nil)
@@ -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
  #
@@ -31,13 +33,14 @@ private
31
33
  end
32
34
 
33
35
  def draw_graph
34
- x_increment = @graph_width / (column_count - 1)
36
+ x_increment = (@graph_width / (column_count - 1)).to_f
35
37
 
36
38
  height = Array.new(column_count, 0)
37
39
 
38
- data_points = nil
40
+ prev_data_points = nil
39
41
  store.norm_data.each do |data_row|
40
- prev_data_points = data_points
42
+ next if data_row.points.empty?
43
+
41
44
  data_points = []
42
45
 
43
46
  data_row.points.each_with_index do |data_point, index|
@@ -69,6 +72,8 @@ private
69
72
  poly_points << data_points[1]
70
73
 
71
74
  Gruff::Renderer::Polygon.new(renderer, color: data_row.color).render(poly_points)
75
+
76
+ prev_data_points = data_points
72
77
  end
73
78
  end
74
79
  end