gruff 0.12.2 → 0.15.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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +66 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +34 -27
  5. data/CHANGELOG.md +29 -0
  6. data/README.md +15 -7
  7. data/assets/fonts/LICENSE.txt +202 -0
  8. data/assets/fonts/Roboto-Bold.ttf +0 -0
  9. data/assets/fonts/Roboto-Regular.ttf +0 -0
  10. data/gruff.gemspec +9 -10
  11. data/lib/gruff/accumulator_bar.rb +3 -1
  12. data/lib/gruff/area.rb +5 -8
  13. data/lib/gruff/bar.rb +32 -49
  14. data/lib/gruff/base.rb +199 -115
  15. data/lib/gruff/bezier.rb +4 -6
  16. data/lib/gruff/bullet.rb +12 -11
  17. data/lib/gruff/dot.rb +13 -14
  18. data/lib/gruff/font.rb +39 -0
  19. data/lib/gruff/helper/bar_conversion.rb +27 -12
  20. data/lib/gruff/helper/bar_value_label.rb +71 -0
  21. data/lib/gruff/helper/stacked_mixin.rb +1 -2
  22. data/lib/gruff/histogram.rb +9 -7
  23. data/lib/gruff/line.rb +57 -50
  24. data/lib/gruff/mini/bar.rb +9 -6
  25. data/lib/gruff/mini/legend.rb +12 -8
  26. data/lib/gruff/mini/pie.rb +9 -6
  27. data/lib/gruff/mini/side_bar.rb +9 -6
  28. data/lib/gruff/net.rb +9 -15
  29. data/lib/gruff/patch/rmagick.rb +0 -1
  30. data/lib/gruff/patch/string.rb +1 -1
  31. data/lib/gruff/pie.rb +23 -65
  32. data/lib/gruff/renderer/bezier.rb +8 -9
  33. data/lib/gruff/renderer/circle.rb +8 -9
  34. data/lib/gruff/renderer/dash_line.rb +9 -10
  35. data/lib/gruff/renderer/dot.rb +13 -14
  36. data/lib/gruff/renderer/ellipse.rb +8 -9
  37. data/lib/gruff/renderer/line.rb +8 -9
  38. data/lib/gruff/renderer/polygon.rb +9 -10
  39. data/lib/gruff/renderer/polyline.rb +8 -9
  40. data/lib/gruff/renderer/rectangle.rb +7 -8
  41. data/lib/gruff/renderer/renderer.rb +25 -40
  42. data/lib/gruff/renderer/text.rb +21 -29
  43. data/lib/gruff/scatter.rb +55 -75
  44. data/lib/gruff/scene.rb +28 -18
  45. data/lib/gruff/side_bar.rb +35 -54
  46. data/lib/gruff/side_stacked_bar.rb +14 -17
  47. data/lib/gruff/spider.rb +11 -20
  48. data/lib/gruff/stacked_area.rb +10 -11
  49. data/lib/gruff/stacked_bar.rb +14 -15
  50. data/lib/gruff/store/store.rb +6 -10
  51. data/lib/gruff/store/xy_data.rb +2 -0
  52. data/lib/gruff/themes.rb +6 -6
  53. data/lib/gruff/version.rb +1 -1
  54. data/lib/gruff.rb +68 -55
  55. data/rails_generators/gruff/templates/controller.rb +1 -1
  56. metadata +34 -20
  57. data/.rubocop_todo.yml +0 -190
  58. data/.travis.yml +0 -23
  59. data/assets/plastik/blue.png +0 -0
  60. data/assets/plastik/green.png +0 -0
  61. data/assets/plastik/red.png +0 -0
  62. data/lib/gruff/helper/bar_value_label_mixin.rb +0 -33
  63. data/lib/gruff/photo_bar.rb +0 -93
@@ -5,12 +5,10 @@ module Gruff
5
5
  class Renderer::Text
6
6
  using Magick::GruffAnnotate
7
7
 
8
- def initialize(text, font:, size:, color:, weight: Magick::NormalWeight, rotation: nil)
8
+ def initialize(renderer, text, font:, rotation: nil)
9
+ @renderer = renderer
9
10
  @text = text.to_s
10
11
  @font = font
11
- @font_size = size
12
- @font_color = color
13
- @font_weight = weight
14
12
  @rotation = rotation
15
13
  end
16
14
 
@@ -23,42 +21,36 @@ module Gruff
23
21
  @y = y
24
22
  @gravity = gravity
25
23
 
26
- Renderer.instance.text_renderers << self
24
+ @renderer.text_renderers << self
27
25
  end
28
26
 
29
27
  def render(width, height, x, y, gravity = Magick::NorthGravity)
30
- draw = Renderer.instance.draw
31
- image = Renderer.instance.image
32
- scale = Renderer.instance.scale
33
-
34
- draw.rotation = @rotation if @rotation
35
- draw.fill = @font_color
36
- draw.stroke = 'transparent'
37
- draw.font = @font if @font
38
- draw.font_weight = @font_weight
39
- draw.pointsize = @font_size * scale
40
- draw.gravity = gravity
41
- draw.annotate_scaled(image,
42
- width, height,
43
- x, y,
44
- @text, scale)
45
- draw.rotation = -@rotation if @rotation
28
+ @renderer.draw.rotation = @rotation if @rotation
29
+ @renderer.draw.fill = @font.color
30
+ @renderer.draw.stroke = 'transparent'
31
+ @renderer.draw.font = @font.file_path
32
+ @renderer.draw.font_weight = @font.weight
33
+ @renderer.draw.pointsize = @font.size * @renderer.scale
34
+ @renderer.draw.gravity = gravity
35
+ @renderer.draw.annotate_scaled(@renderer.image,
36
+ width, height,
37
+ x, y,
38
+ @text, @renderer.scale)
39
+ @renderer.draw.rotation = -@rotation if @rotation
46
40
  end
47
41
 
48
- def self.metrics(text, size, font_weight = Magick::NormalWeight)
49
- draw = Renderer.instance.draw
50
- image = Renderer.instance.image
51
-
52
- draw.font_weight = font_weight
53
- draw.pointsize = size
42
+ def metrics
43
+ @renderer.draw.font = @font.file_path
44
+ @renderer.draw.font_weight = @font.weight
45
+ @renderer.draw.pointsize = @font.size
54
46
 
55
47
  # The old ImageMagick causes SEGV with string which has '%' + alphabet (eg. '%S').
56
48
  # This format is used to embed value into a string using image properties.
57
49
  # However, gruff use plain image as canvas which does not have any property.
58
50
  # So, in here, it just escape % in order to avoid SEGV.
59
- text = text.to_s.gsub(/(%+)/) { ('%' * Regexp.last_match(1).size * 2).to_s }
51
+ text = @text.to_s.gsub(/(%+)/) { ('%' * Regexp.last_match(1).size * 2).to_s }
60
52
 
61
- draw.get_type_metrics(image, text)
53
+ @renderer.draw.get_type_metrics(@renderer.image, text)
62
54
  end
63
55
  end
64
56
  end
data/lib/gruff/scatter.rb CHANGED
@@ -28,60 +28,18 @@ class Gruff::Scatter < Gruff::Base
28
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
29
  attr_writer :disable_significant_rounding_x_axis
30
30
 
31
- # Allow enabling vertical lines. When you have a lot of data, they can work great.
32
- attr_writer :enable_vertical_line_markers
31
+ # Allow for vertical marker lines.
32
+ attr_writer :show_vertical_markers
33
33
 
34
34
  # Allow using vertical labels in the X axis (and setting the label margin).
35
35
  attr_writer :x_label_margin
36
36
  attr_writer :use_vertical_x_labels
37
37
 
38
- # Allow passing lambdas to format labels.
39
- attr_writer :y_axis_label_format
40
- attr_writer :x_axis_label_format
41
-
42
- def initialize_store
43
- @store = Gruff::Store.new(Gruff::Store::XYData)
44
- end
45
- private :initialize_store
46
-
47
- def initialize_ivars
48
- super
49
-
50
- @baseline_x_color = @baseline_y_color = 'red'
51
- @baseline_x_value = @baseline_y_value = nil
52
- @circle_radius = nil
53
- @disable_significant_rounding_x_axis = false
54
- @enable_vertical_line_markers = false
55
- @marker_x_count = nil
56
- @maximum_x_value = @minimum_x_value = nil
57
- @stroke_width = nil
58
- @use_vertical_x_labels = false
59
- @x_axis_label_format = nil
60
- @x_label_margin = nil
61
- @y_axis_label_format = nil
62
- end
63
- private :initialize_ivars
64
-
65
- def draw
66
- super
67
- return unless data_given?
68
-
69
- # Check to see if more than one datapoint was given. NaN can result otherwise.
70
- @x_increment = (@x_spread > 1) ? (@graph_width / (@x_spread - 1).to_f) : @graph_width
71
-
72
- store.norm_data.each do |data_row|
73
- data_row.coordinates.each do |x_value, y_value|
74
- next if y_value.nil? || x_value.nil?
75
-
76
- new_x = get_x_coord(x_value, @graph_width, @graph_left)
77
- new_y = @graph_top + (@graph_height - y_value * @graph_height)
78
-
79
- # Reset each time to avoid thin-line errors
80
- stroke_width = @stroke_width || clip_value_if_greater_than(@columns / (store.norm_data.first[1].size * 4), 5.0)
81
- circle_radius = @circle_radius || clip_value_if_greater_than(@columns / (store.norm_data.first[1].size * 2.5), 5.0)
82
- Gruff::Renderer::Circle.new(color: data_row.color, width: stroke_width).render(new_x, new_y, new_x - circle_radius, new_y)
83
- end
84
- end
38
+ # Allow enabling vertical lines. When you have a lot of data, they can work great.
39
+ # @deprecated Please use +show_vertical_markers+ attribute instead.
40
+ def enable_vertical_line_markers=(value)
41
+ warn '#enable_vertical_line_markers= is deprecated. Please use `show_vertical_markers` attribute instead'
42
+ @show_vertical_markers = value
85
43
  end
86
44
 
87
45
  # The first parameter is the name of the dataset. The next two are the
@@ -137,6 +95,44 @@ class Gruff::Scatter < Gruff::Base
137
95
 
138
96
  private
139
97
 
98
+ def initialize_store
99
+ @store = Gruff::Store.new(Gruff::Store::XYData)
100
+ end
101
+
102
+ def initialize_attributes
103
+ super
104
+
105
+ @baseline_x_color = @baseline_y_color = 'red'
106
+ @baseline_x_value = @baseline_y_value = nil
107
+ @circle_radius = nil
108
+ @disable_significant_rounding_x_axis = false
109
+ @show_vertical_markers = false
110
+ @marker_x_count = nil
111
+ @maximum_x_value = @minimum_x_value = nil
112
+ @stroke_width = nil
113
+ @use_vertical_x_labels = false
114
+ @x_label_margin = nil
115
+ end
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
120
+
121
+ store.norm_data.each do |data_row|
122
+ data_row.coordinates.each do |x_value, y_value|
123
+ next if y_value.nil? || x_value.nil?
124
+
125
+ new_x = get_x_coord(x_value, @graph_width, @graph_left)
126
+ new_y = @graph_top + (@graph_height - y_value * @graph_height)
127
+
128
+ # Reset each time to avoid thin-line errors
129
+ stroke_width = @stroke_width || clip_value_if_greater_than(@columns / (store.norm_data.first[1].size * 4), 5.0)
130
+ circle_radius = @circle_radius || clip_value_if_greater_than(@columns / (store.norm_data.first[1].size * 2.5), 5.0)
131
+ Gruff::Renderer::Circle.new(renderer, color: data_row.color, width: stroke_width).render(new_x, new_y, new_x - circle_radius, new_y)
132
+ end
133
+ end
134
+ end
135
+
140
136
  def setup_data
141
137
  # Update the global min/max values for the x data
142
138
  @maximum_x_value ||= store.max_x
@@ -152,7 +148,7 @@ private
152
148
  super
153
149
  end
154
150
 
155
- def calculate_spread #:nodoc:
151
+ def calculate_spread
156
152
  super
157
153
  @x_spread = @maximum_x_value.to_f - @minimum_x_value.to_f
158
154
  @x_spread = @x_spread > 0 ? @x_spread : 1
@@ -180,18 +176,18 @@ private
180
176
  end
181
177
  @marker_x_count ||= 4
182
178
  end
183
- @x_increment = (@x_spread > 0) ? (@x_spread / @marker_x_count) : 1
179
+ @x_increment = @x_spread > 0 ? (@x_spread / @marker_x_count) : 1
184
180
  unless @disable_significant_rounding_x_axis
185
181
  @x_increment = significant(@x_increment)
186
182
  end
187
183
  else
188
184
  # TODO: Make this work for negative values
189
- @maximum_x_value = [maximum_value.ceil, @x_axis_increment].max
185
+ @maximum_x_value = [@maximum_x_value.ceil, @x_axis_increment].max
190
186
  @minimum_x_value = @minimum_x_value.floor
191
187
  calculate_spread
192
188
  normalize
193
189
 
194
- self.marker_count = (@x_spread / @x_axis_increment).to_i
190
+ @marker_x_count = (@x_spread / @x_axis_increment).to_i
195
191
  @x_increment = @x_axis_increment
196
192
  end
197
193
  increment_x_scaled = @graph_width.to_f / (@x_spread / @x_increment)
@@ -199,43 +195,27 @@ private
199
195
  # Draw vertical line markers and annotate with numbers
200
196
  (0..@marker_x_count).each do |index|
201
197
  # TODO: Fix the vertical lines, and enable them by default. Not pretty when they don't match up with top y-axis line
202
- if @enable_vertical_line_markers
198
+ if @show_vertical_markers
203
199
  x = @graph_left + @graph_width - index.to_f * increment_x_scaled
204
200
 
205
- line_renderer = Gruff::Renderer::Line.new(color: @marker_color, shadow_color: @marker_shadow_color)
201
+ line_renderer = Gruff::Renderer::Line.new(renderer, color: @marker_color, shadow_color: @marker_shadow_color)
206
202
  line_renderer.render(x, @graph_top, x, @graph_bottom)
207
203
  end
208
204
 
209
205
  unless @hide_line_numbers
210
- marker_label = index * @x_increment + @minimum_x_value.to_f
206
+ marker_label = BigDecimal(index.to_s) * BigDecimal(@x_increment.to_s) + BigDecimal(@minimum_x_value.to_s)
211
207
  y_offset = @graph_bottom + (@x_label_margin || LABEL_MARGIN)
212
208
  x_offset = get_x_coord(index.to_f, increment_x_scaled, @graph_left)
213
209
 
214
- label = vertical_label(marker_label, @x_increment)
210
+ label = x_axis_label(marker_label, @x_increment)
215
211
  rotation = -90.0 if @use_vertical_x_labels
216
- text_renderer = Gruff::Renderer::Text.new(label, font: @font, size: @marker_font_size, color: @font_color, rotation: rotation)
212
+ text_renderer = Gruff::Renderer::Text.new(renderer, label, font: @marker_font, rotation: rotation)
217
213
  text_renderer.add_to_render_queue(1.0, 1.0, x_offset, y_offset)
218
214
  end
219
215
  end
220
216
  end
221
217
 
222
- def label(value, increment)
223
- if @y_axis_label_format
224
- @y_axis_label_format.call(value)
225
- else
226
- super
227
- end
228
- end
229
-
230
- def vertical_label(value, increment)
231
- if @x_axis_label_format
232
- @x_axis_label_format.call(value)
233
- else
234
- label(value, increment)
235
- end
236
- end
237
-
238
- def get_x_coord(x_data_point, width, offset) #:nodoc:
218
+ def get_x_coord(x_data_point, width, offset)
239
219
  x_data_point * width + offset
240
220
  end
241
221
  end
data/lib/gruff/scene.rb CHANGED
@@ -52,7 +52,7 @@ class Gruff::Scene < Gruff::Base
52
52
  # Join all the custom paths and filter out the empty ones
53
53
  image_paths = @layers.map(&:path).reject(&:empty?)
54
54
  images = Magick::ImageList.new(*image_paths)
55
- Gruff::Renderer.background_image = images.flatten_images
55
+ renderer.background_image = images.flatten_images
56
56
  end
57
57
 
58
58
  def layers=(ordered_list)
@@ -73,12 +73,20 @@ class Gruff::Scene < Gruff::Base
73
73
  case method_name.to_s
74
74
  when /^(\w+)_group=$/
75
75
  add_group Regexp.last_match(1), *args
76
- return
77
76
  when /^(\w+)=$/
78
77
  set_input Regexp.last_match(1), args.first
79
- return
78
+ else
79
+ super
80
+ end
81
+ end
82
+
83
+ def respond_to_missing?(method_sym, include_private)
84
+ case method_sym.to_s
85
+ when /^(\w+)_group=$/, /^(\w+)=$/
86
+ true
87
+ else
88
+ super
80
89
  end
81
- super
82
90
  end
83
91
 
84
92
  private
@@ -90,7 +98,7 @@ private
90
98
  def set_input(input_name, input_value)
91
99
  if !@groups[input_name].nil?
92
100
  @groups[input_name].send_updates(input_value)
93
- elsif chosen_layer = @layers.find { |layer| layer.name == input_name }
101
+ elsif (chosen_layer = @layers.find { |layer| layer.name == input_name })
94
102
  chosen_layer.update input_value
95
103
  end
96
104
  end
@@ -121,7 +129,7 @@ class Gruff::Layer
121
129
  def initialize(base_dir, folder_name)
122
130
  @base_dir = base_dir.to_s
123
131
  @name = folder_name.to_s
124
- @filenames = Dir.open(File.join(base_dir, folder_name)).entries.select { |file| file =~ /^[^.]+\.png$/ }.sort
132
+ @filenames = Dir.open(File.join(base_dir, folder_name)).entries.grep(/^[^.]+\.png$/).sort
125
133
  @selected_filename = select_default
126
134
  end
127
135
 
@@ -132,18 +140,20 @@ class Gruff::Layer
132
140
 
133
141
  # Choose the appropriate filename for this layer, based on the input
134
142
  def update(value)
135
- @selected_filename = case value.to_s
136
- when /^(true|false)$/
137
- select_boolean value
138
- when /^(\w|\s)+$/
139
- select_string value
140
- when /^-?(\d+\.)?\d+$/
141
- select_numeric value
142
- when /(\d\d):(\d\d):\d\d/
143
- select_time "#{Regexp.last_match(1)}#{Regexp.last_match(2)}"
144
- else
145
- select_default
146
- end
143
+ @selected_filename = begin
144
+ case value.to_s
145
+ when /^(true|false)$/
146
+ select_boolean value
147
+ when /^(\w|\s)+$/
148
+ select_string value
149
+ when /^-?(\d+\.)?\d+$/
150
+ select_numeric value
151
+ when /(\d\d):(\d\d):\d\d/
152
+ select_time "#{Regexp.last_match(1)}#{Regexp.last_match(2)}"
153
+ else
154
+ select_default
155
+ end
156
+ end
147
157
  # Finally, try to use 'default' if we're still blank
148
158
  @selected_filename ||= select_default
149
159
  end
@@ -19,15 +19,13 @@
19
19
  # g.write('sidebar.png')
20
20
  #
21
21
  class Gruff::SideBar < Gruff::Base
22
- using String::GruffCommify
23
-
24
22
  # Spacing factor applied between bars.
25
23
  attr_writer :bar_spacing
26
24
 
27
25
  # Spacing factor applied between a group of bars belonging to the same label.
28
26
  attr_writer :group_spacing
29
27
 
30
- # Set the number output format for labels using sprintf.
28
+ # Set the number output format string or lambda.
31
29
  # Default is +"%.2f"+.
32
30
  attr_writer :label_formatting
33
31
 
@@ -38,27 +36,24 @@ class Gruff::SideBar < Gruff::Base
38
36
  # Prevent drawing of column labels left of a side bar graph. Default is +false+.
39
37
  attr_writer :hide_labels
40
38
 
41
- def initialize_ivars
39
+ # With Side Bars use the data label for the marker value to the left of the bar.
40
+ # @deprecated
41
+ def use_data_label=(_value)
42
+ warn '#use_data_label is deprecated. It is no longer effective.'
43
+ end
44
+
45
+ private
46
+
47
+ def initialize_attributes
42
48
  super
43
49
  @bar_spacing = 0.9
44
50
  @group_spacing = 10
45
51
  @label_formatting = nil
46
52
  @show_labels_for_bar_values = false
47
53
  @hide_labels = false
48
- end
49
- private :initialize_ivars
50
-
51
- def draw
52
54
  @has_left_labels = true
53
- super
54
-
55
- return unless data_given?
56
-
57
- draw_bars
58
55
  end
59
56
 
60
- protected
61
-
62
57
  def hide_labels?
63
58
  @hide_labels
64
59
  end
@@ -71,55 +66,44 @@ protected
71
66
  @hide_line_markers
72
67
  end
73
68
 
74
- private
69
+ # Value to avoid completely overwriting the coordinate axis
70
+ AXIS_MARGIN = 0.5
75
71
 
76
- def draw_bars
72
+ def draw_graph
77
73
  # Setup spacing.
78
74
  #
79
75
  bars_width = (@graph_height - calculate_spacing) / column_count.to_f
80
76
  bar_width = bars_width / store.length
81
- height = Array.new(column_count, 0)
82
- length = Array.new(column_count, @graph_left)
83
77
  padding = (bar_width * (1 - @bar_spacing)) / 2
84
78
 
85
- # if we're a side stacked bar then we don't need to draw ourself at all
86
- # because sometimes (due to different heights/min/max) you can actually
87
- # see both graphs and it looks like crap
88
- return if is_a?(Gruff::SideStackedBar)
79
+ # Setup the BarConversion Object
80
+ conversion = Gruff::BarConversion.new(
81
+ top: @graph_right, bottom: @graph_left,
82
+ minimum_value: minimum_value, maximum_value: maximum_value, spread: @spread
83
+ )
89
84
 
90
85
  store.norm_data.each_with_index do |data_row, row_index|
91
86
  data_row.points.each_with_index do |data_point, point_index|
92
87
  group_spacing = @group_spacing * @scale * point_index
93
88
 
94
- # Using the original calculations from the stacked bar chart
95
- # to get the difference between
96
- # part of the bart chart we wish to stack.
97
- temp1 = @graph_left + (@graph_width - data_point * @graph_width - height[point_index])
98
- temp2 = @graph_left + @graph_width - height[point_index]
99
- difference = temp2 - temp1
100
-
101
- left_x = length[point_index] - 1
102
89
  left_y = @graph_top + (bars_width * point_index) + (bar_width * row_index) + padding + group_spacing
103
- right_x = left_x + difference
104
90
  right_y = left_y + bar_width * @bar_spacing
105
91
 
106
- height[point_index] += (data_point * @graph_width)
92
+ left_x, right_x = conversion.get_top_bottom_scaled(data_point).sort
107
93
 
108
- rect_renderer = Gruff::Renderer::Rectangle.new(color: data_row.color)
109
- rect_renderer.render(left_x, left_y, right_x, right_y)
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)
110
96
 
111
97
  # Calculate center based on bar_width and current row
98
+ label_center = left_y + bars_width / 2
112
99
 
113
- if @use_data_label
114
- label_center = left_y + bar_width / 2
115
- draw_label(label_center, row_index, store.norm_data[row_index].label)
116
- else
117
- label_center = left_y + bars_width / 2
118
- draw_label(label_center, point_index)
119
- end
100
+ # Subtract half a bar width to center left if requested
101
+ draw_label(label_center, point_index)
120
102
  if @show_labels_for_bar_values
121
- val = (@label_formatting || '%.2f') % store.data[row_index].points[point_index]
122
- draw_value_label(right_x + 40, right_y - bar_width / 2, val.commify, true)
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)
106
+ end
123
107
  end
124
108
  end
125
109
  end
@@ -139,14 +123,14 @@ private
139
123
  line_diff = (@graph_right - @graph_left) / number_of_lines
140
124
  x = @graph_right - (line_diff * index) - 1
141
125
 
142
- line_renderer = Gruff::Renderer::Line.new(color: @marker_color, shadow_color: @marker_shadow_color)
126
+ line_renderer = Gruff::Renderer::Line.new(renderer, color: @marker_color, shadow_color: @marker_shadow_color)
143
127
  line_renderer.render(x, @graph_bottom, x, @graph_top)
144
128
 
145
- diff = index - number_of_lines
146
- marker_label = diff.abs * increment + minimum_value
147
-
148
129
  unless @hide_line_numbers
149
- text_renderer = Gruff::Renderer::Text.new(marker_label, font: @font, size: @marker_font_size, color: @font_color)
130
+ diff = index - number_of_lines
131
+ marker_label = BigDecimal(diff.abs.to_s) * BigDecimal(increment.to_s) + BigDecimal(minimum_value.to_s)
132
+ label = x_axis_label(marker_label, @increment)
133
+ text_renderer = Gruff::Renderer::Text.new(renderer, label, font: @marker_font)
150
134
  text_renderer.add_to_render_queue(0, 0, x, @graph_bottom + LABEL_MARGIN, Magick::CenterGravity)
151
135
  end
152
136
  end
@@ -155,12 +139,9 @@ private
155
139
  ##
156
140
  # Draw on the Y axis instead of the X
157
141
 
158
- def draw_label(y_offset, index, label = nil)
142
+ def draw_label(y_offset, index)
159
143
  draw_unique_label(index) do
160
- lbl = @use_data_label ? label : @labels[index]
161
-
162
- text_renderer = Gruff::Renderer::Text.new(lbl, font: @font, size: @marker_font_size, color: @font_color)
163
- text_renderer.add_to_render_queue(@graph_left - LABEL_MARGIN, 1.0, 0.0, y_offset, Magick::EastGravity)
144
+ draw_label_at(@graph_left - LABEL_MARGIN, 1.0, 0.0, y_offset, @labels[index], Magick::EastGravity)
164
145
  end
165
146
  end
166
147
 
@@ -21,7 +21,6 @@
21
21
  #
22
22
  class Gruff::SideStackedBar < Gruff::SideBar
23
23
  include StackedMixin
24
- include BarValueLabelMixin
25
24
 
26
25
  # Spacing factor applied between bars.
27
26
  attr_writer :bar_spacing
@@ -29,7 +28,7 @@ class Gruff::SideStackedBar < Gruff::SideBar
29
28
  # Number of pixels between bar segments.
30
29
  attr_writer :segment_spacing
31
30
 
32
- # Set the number output format for labels using sprintf.
31
+ # Set the number output format string or lambda.
33
32
  # Default is +"%.2f"+.
34
33
  attr_writer :label_formatting
35
34
 
@@ -40,24 +39,23 @@ class Gruff::SideStackedBar < Gruff::SideBar
40
39
  # Prevent drawing of column labels left of a side stacked bar graph. Default is +false+.
41
40
  attr_writer :hide_labels
42
41
 
43
- def initialize_ivars
42
+ private
43
+
44
+ def initialize_attributes
44
45
  super
45
46
  @bar_spacing = 0.9
46
47
  @segment_spacing = 2.0
47
48
  @label_formatting = nil
48
49
  @show_labels_for_bar_values = false
49
50
  @hide_labels = false
51
+ @has_left_labels = true
50
52
  end
51
- private :initialize_ivars
52
53
 
53
- def draw
54
- @has_left_labels = true
54
+ def setup_data
55
55
  calculate_maximum_by_stack
56
56
  super
57
57
  end
58
58
 
59
- protected
60
-
61
59
  def hide_labels?
62
60
  @hide_labels
63
61
  end
@@ -70,9 +68,7 @@ protected
70
68
  @hide_line_markers
71
69
  end
72
70
 
73
- private
74
-
75
- def draw_bars
71
+ def draw_graph
76
72
  # Setup spacing.
77
73
  #
78
74
  # Columns sit stacked.
@@ -80,7 +76,7 @@ private
80
76
  height = Array.new(column_count, 0)
81
77
  length = Array.new(column_count, @graph_left)
82
78
  padding = (bar_width * (1 - @bar_spacing)) / 2
83
- bar_value_label = BarValueLabel.new(column_count, bar_width)
79
+ stack_bar_value_label = Gruff::BarValueLabel::StackedBar.new
84
80
 
85
81
  store.norm_data.each_with_index do |data_row, row_index|
86
82
  data_row.points.each_with_index do |data_point, point_index|
@@ -91,6 +87,7 @@ private
91
87
  height[point_index]) + 1
92
88
  temp2 = @graph_left + @graph_width - height[point_index] - 1
93
89
  difference = temp2 - temp1
90
+ difference = 0 if row_index == 0 && difference < 0
94
91
 
95
92
  left_x = length[point_index]
96
93
  left_y = @graph_top + (bar_width * point_index) + padding
@@ -99,14 +96,14 @@ private
99
96
  length[point_index] += difference
100
97
  height[point_index] += (data_point * @graph_width - 2)
101
98
 
102
- bar_value_label.coordinates[point_index] = [left_x, left_y, right_x, right_y]
103
- bar_value_label.values[point_index] += store.data[row_index].points[point_index]
99
+ bar_value_label = Gruff::BarValueLabel::SideBar.new([left_x, left_y, right_x, right_y], store.data[row_index].points[point_index])
100
+ stack_bar_value_label.add(bar_value_label, point_index)
104
101
 
105
102
  # if a data point is 0 it can result in weird really thing lines
106
103
  # that shouldn't even be there being drawn on top of the existing
107
104
  # bar - this is bad
108
105
  if data_point != 0
109
- rect_renderer = Gruff::Renderer::Rectangle.new(color: data_row.color)
106
+ rect_renderer = Gruff::Renderer::Rectangle.new(renderer, color: data_row.color)
110
107
  rect_renderer.render(left_x, left_y, right_x, right_y)
111
108
  # Calculate center based on bar_width and current row
112
109
  end
@@ -118,8 +115,8 @@ private
118
115
  end
119
116
 
120
117
  if @show_labels_for_bar_values
121
- bar_value_label.prepare_sidebar_rendering(@label_formatting) do |x, y, text|
122
- draw_value_label(x, y, text, true)
118
+ stack_bar_value_label.prepare_rendering(@label_formatting, bar_width) do |x, y, text|
119
+ draw_value_label(x, y, text)
123
120
  end
124
121
  end
125
122
  end